Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/dns/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const (
// InstanceResolver provides instance IP resolution.
// This interface is implemented by the instances package.
type InstanceResolver interface {
// ResolveInstanceIP resolves an instance name or ID to its IP address.
// ResolveInstanceIP resolves an instance name or ID to its IP address for DNS.
ResolveInstanceIP(ctx context.Context, nameOrID string) (string, error)
}

Expand Down
20 changes: 0 additions & 20 deletions lib/instances/fork.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"hash/crc32"
"os"
"path/filepath"
"strings"
"time"

Expand Down Expand Up @@ -368,25 +367,6 @@ func validateForkVolumeSafety(volumes []VolumeAttachment) error {
return nil
}

func (m *manager) instanceNameExists(name string) (bool, error) {
metaFiles, err := m.listMetadataFiles()
if err != nil {
return false, err
}

for _, metaFile := range metaFiles {
id := filepath.Base(filepath.Dir(metaFile))
meta, err := m.loadMetadata(id)
if err != nil {
continue
}
if meta.Name == name {
return true, nil
}
}
return false, nil
}

func resolveForkTargetState(requested State, sourceState State) (State, error) {
if requested == "" {
switch sourceState {
Expand Down
13 changes: 12 additions & 1 deletion lib/instances/ingress_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,32 @@ import (
"fmt"
)

const dnsMinIDPrefixLength = 8

// IngressResolver provides instance resolution for the ingress package.
// It implements ingress.InstanceResolver interface without importing the ingress package
// to avoid import cycles.
type IngressResolver struct {
manager Manager
}

type minPrefixInstanceManager interface {
getInstanceWithMinIDPrefix(ctx context.Context, idOrName string, minPrefixLength int) (*Instance, error)
}

// NewIngressResolver creates a new IngressResolver that wraps an instance manager.
func NewIngressResolver(manager Manager) *IngressResolver {
return &IngressResolver{manager: manager}
}

// ResolveInstanceIP resolves an instance name, ID, or ID prefix to its IP address.
func (r *IngressResolver) ResolveInstanceIP(ctx context.Context, nameOrID string) (string, error) {
inst, err := r.manager.GetInstance(ctx, nameOrID)
manager, ok := r.manager.(minPrefixInstanceManager)
if !ok {
return "", fmt.Errorf("instance resolver does not support DNS-safe lookup")
}

inst, err := manager.getInstanceWithMinIDPrefix(ctx, nameOrID, dnsMinIDPrefixLength)
if err != nil {
return "", fmt.Errorf("instance not found: %s", nameOrID)
}
Expand Down
71 changes: 25 additions & 46 deletions lib/instances/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ func (m *manager) maybePersistBootMarkers(ctx context.Context, id string) {
m.persistBootMarkers(ctx, id)
}

func (m *manager) finalizeResolvedInstance(ctx context.Context, inst *Instance) {
if inst.State == StateStopped && inst.ExitCode != nil {
m.maybePersistExitInfo(ctx, inst.Id)
}
if (inst.State == StateRunning || inst.State == StateInitializing) && inst.BootMarkersHydrated {
m.maybePersistBootMarkers(ctx, inst.Id)
}
}

func (m *manager) recordImageUsage(ctx context.Context, imageInfo *images.Image) {
if m.imageUsageRecorder == nil || imageInfo == nil {
return
Expand Down Expand Up @@ -499,66 +508,36 @@ func (m *manager) ListInstances(ctx context.Context, filter *ListInstancesFilter
// Lookup order: exact ID match -> exact name match -> ID prefix match.
// Returns ErrAmbiguousName if prefix matches multiple instances.
func (m *manager) GetInstance(ctx context.Context, idOrName string) (*Instance, error) {
return m.getInstanceWithMinIDPrefix(ctx, idOrName, 1)
}

func (m *manager) getInstanceWithMinIDPrefix(ctx context.Context, idOrName string, minPrefixLength int) (*Instance, error) {
// 1. Try exact ID match first (most common case)
lock := m.getInstanceLock(idOrName)
lock.RLock()
inst, err := m.getInstance(ctx, idOrName)
lock.RUnlock()
if err == nil {
// If VM is stopped with unpersisted exit info, persist under write lock.
// This handles the "app exited on its own" case where stopInstance wasn't called.
if inst.State == StateStopped && inst.ExitCode != nil {
m.maybePersistExitInfo(ctx, inst.Id)
}
if (inst.State == StateRunning || inst.State == StateInitializing) && inst.BootMarkersHydrated {
m.maybePersistBootMarkers(ctx, inst.Id)
}
m.finalizeResolvedInstance(ctx, inst)
return inst, nil
}

// 2. List all instances for name and prefix matching
instances, err := m.ListInstances(ctx, nil)
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the slow path I'm optimizing:

  • get instance by name
  • lists all instances
  • listing all instances queries all hypervisors to get state

but we don't need all the VM's hypervisor states to get an instance by name. So instead we do a metadata-only lookup, and consolidate the other metadata lookup that was in fork to use the same.

// 2. Resolve exact name or ID prefix from metadata only, then hydrate the
// single matched instance.
meta, err := m.findInstanceMetadataByNameOrIDPrefix(idOrName, minPrefixLength)
if err != nil {
return nil, err
}

// 3. Try exact name match
var nameMatches []Instance
for _, inst := range instances {
if inst.Name == idOrName {
nameMatches = append(nameMatches, inst)
}
}
if len(nameMatches) == 1 {
inst := &nameMatches[0]
if inst.State == StateStopped && inst.ExitCode != nil {
m.maybePersistExitInfo(ctx, inst.Id)
}
return inst, nil
}
if len(nameMatches) > 1 {
return nil, ErrAmbiguousName
}

// 4. Try ID prefix match
var prefixMatches []Instance
for _, inst := range instances {
if len(idOrName) > 0 && len(inst.Id) >= len(idOrName) && inst.Id[:len(idOrName)] == idOrName {
prefixMatches = append(prefixMatches, inst)
}
}
if len(prefixMatches) == 1 {
inst := &prefixMatches[0]
if inst.State == StateStopped && inst.ExitCode != nil {
m.maybePersistExitInfo(ctx, inst.Id)
}
return inst, nil
}
if len(prefixMatches) > 1 {
return nil, ErrAmbiguousName
resolvedLock := m.getInstanceLock(meta.Id)
resolvedLock.RLock()
inst, err = m.getInstance(ctx, meta.Id)
resolvedLock.RUnlock()
if err != nil {
return nil, err
}

return nil, ErrNotFound
m.finalizeResolvedInstance(ctx, inst)
return inst, nil
}

// StreamInstanceLogs streams instance logs from the specified source
Expand Down
81 changes: 81 additions & 0 deletions lib/instances/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,87 @@ func (m *manager) listInstances(ctx context.Context) ([]Instance, error) {
return result, nil
}

func (m *manager) findInstanceMetadataByExactName(name string) (*metadata, error) {
files, err := m.listMetadataFiles()
if err != nil {
return nil, err
}

for _, file := range files {
id := filepath.Base(filepath.Dir(file))
meta, err := m.loadMetadata(id)
if err != nil {
continue
}
if meta.Name == name {
return meta, nil
}
}
return nil, ErrNotFound
}

func (m *manager) findInstanceMetadataByNameOrIDPrefix(idOrName string, minPrefixLength int) (*metadata, error) {
files, err := m.listMetadataFiles()
if err != nil {
return nil, err
}
if minPrefixLength < 1 {
minPrefixLength = 1
}

var nameMatch *metadata
var prefixMatch *metadata
nameMatches := 0
prefixMatches := 0

for _, file := range files {
id := filepath.Base(filepath.Dir(file))
meta, err := m.loadMetadata(id)
if err != nil {
continue
}

if meta.Name == idOrName {
nameMatches++
if nameMatches == 1 {
nameMatch = meta
}
}

if len(idOrName) >= minPrefixLength && strings.HasPrefix(meta.Id, idOrName) {
prefixMatches++
if prefixMatches == 1 {
prefixMatch = meta
}
}
}

if nameMatches == 1 {
return nameMatch, nil
}
if nameMatches > 1 {
return nil, ErrAmbiguousName
}
if prefixMatches == 1 {
return prefixMatch, nil
}
if prefixMatches > 1 {
return nil, ErrAmbiguousName
}
return nil, ErrNotFound
}

func (m *manager) instanceNameExists(name string) (bool, error) {
_, err := m.findInstanceMetadataByExactName(name)
if err == nil {
return true, nil
}
if err == ErrNotFound {
return false, nil
}
return false, err
}

// getInstance returns a single instance by ID
func (m *manager) getInstance(ctx context.Context, id string) (*Instance, error) {
log := logger.FromContext(ctx)
Expand Down
Loading