From a2d4622486ef9f6834c57774f3c1caaf8b132820 Mon Sep 17 00:00:00 2001 From: LordMathis Date: Mon, 20 Oct 2025 22:59:31 +0200 Subject: [PATCH] Refactor instance locking mechanism to use per-instance locks for concurrency --- pkg/manager/manager.go | 22 ++++++++++++++++++++-- pkg/manager/operations.go | 35 ++++++++++++++++++++--------------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 171d6ca..551b1dd 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -40,8 +40,9 @@ type instanceManager struct { localNodeName string // Name of the local node // Synchronization - operationMu sync.Mutex // Protects start/stop/update/delete/restart operations - shutdownOnce sync.Once + operationMu sync.Mutex // DEPRECATED: Use instanceLocks for per-instance operations + instanceLocks sync.Map // map[string]*sync.Mutex - per-instance locks for concurrent operations + shutdownOnce sync.Once } // New creates a new instance of InstanceManager. @@ -287,3 +288,20 @@ func (im *instanceManager) getNodeForInstance(inst *instance.Instance) *config.N return nil } + +// lockInstance returns the lock for a specific instance, creating one if needed. +// This allows concurrent operations on different instances while preventing +// concurrent operations on the same instance. +func (im *instanceManager) lockInstance(name string) *sync.Mutex { + lock, _ := im.instanceLocks.LoadOrStore(name, &sync.Mutex{}) + return lock.(*sync.Mutex) +} + +// unlockAndCleanup unlocks the instance lock and removes it from the map. +// This should only be called when deleting an instance to prevent memory leaks. +func (im *instanceManager) unlockAndCleanup(name string) { + if lock, ok := im.instanceLocks.Load(name); ok { + lock.(*sync.Mutex).Unlock() + im.instanceLocks.Delete(name) + } +} diff --git a/pkg/manager/operations.go b/pkg/manager/operations.go index a88ec77..4b2e719 100644 --- a/pkg/manager/operations.go +++ b/pkg/manager/operations.go @@ -240,9 +240,10 @@ func (im *instanceManager) UpdateInstance(name string, options *instance.Options return nil, err } - // Lock for local instance operations to prevent races - im.operationMu.Lock() - defer im.operationMu.Unlock() + // Lock this specific instance only + lock := im.lockInstance(name) + lock.Lock() + defer lock.Unlock() // Handle port changes oldPort := inst.GetPort() @@ -330,9 +331,10 @@ func (im *instanceManager) DeleteInstance(name string) error { return nil } - // Lock for local instance operations to prevent races - im.operationMu.Lock() - defer im.operationMu.Unlock() + // Lock this specific instance and clean up the lock on completion + lock := im.lockInstance(name) + lock.Lock() + defer im.unlockAndCleanup(name) if inst.IsRunning() { return fmt.Errorf("instance with name %s is still running, stop it before deleting", name) @@ -376,9 +378,10 @@ func (im *instanceManager) StartInstance(name string) (*instance.Instance, error return inst, nil } - // Lock for local instance operations to prevent races - im.operationMu.Lock() - defer im.operationMu.Unlock() + // Lock this specific instance only + lock := im.lockInstance(name) + lock.Lock() + defer lock.Unlock() if inst.IsRunning() { return inst, fmt.Errorf("instance with name %s is already running", name) @@ -438,9 +441,10 @@ func (im *instanceManager) StopInstance(name string) (*instance.Instance, error) return inst, nil } - // Lock for local instance operations to prevent races - im.operationMu.Lock() - defer im.operationMu.Unlock() + // Lock this specific instance only + lock := im.lockInstance(name) + lock.Lock() + defer lock.Unlock() if !inst.IsRunning() { return inst, fmt.Errorf("instance with name %s is already stopped", name) @@ -479,9 +483,10 @@ func (im *instanceManager) RestartInstance(name string) (*instance.Instance, err return inst, nil } - // Lock for the entire restart operation to ensure atomicity - im.operationMu.Lock() - defer im.operationMu.Unlock() + // Lock this specific instance for the entire restart operation to ensure atomicity + lock := im.lockInstance(name) + lock.Lock() + defer lock.Unlock() // Stop the instance if inst.IsRunning() {