v5.0.63: instance-level mutex to prevent parallel operations on same resource

This commit is contained in:
“Naeel”
2026-07-07 08:27:42 +04:00
parent d9f2c60e90
commit 4ac21d9367
9 changed files with 184 additions and 9 deletions
+17
View File
@@ -51,6 +51,9 @@ func UpdateResourceWithTimeout(ctx context.Context, client *core.UniversalClient
if strings.TrimSpace(instanceID) == "" {
return fmt.Errorf("missing instance id for modify")
}
unlock := client.LockInstance(instanceID)
defer unlock()
ctxWithTimeout, err := core.WithOperationTimeout(ctx, operationTimeout)
if err != nil {
return err
@@ -70,6 +73,11 @@ func DeleteResourceWithTimeout(ctx context.Context, client *core.UniversalClient
mode = "state_only"
}
if mode != "state_only" && mode != "detach" {
unlock := client.LockInstance(instanceID)
defer unlock()
}
ctxWithTimeout, err := core.WithOperationTimeout(ctx, operationTimeout)
if err != nil {
return err
@@ -121,6 +129,9 @@ func RunOperationByCodeWithTimeout(ctx context.Context, client *core.UniversalCl
if client == nil {
return fmt.Errorf("missing client for operation")
}
unlock := client.LockInstance(instanceID)
defer unlock()
ctxWithTimeout, err := core.WithOperationTimeout(ctx, operationTimeout)
if err != nil {
return err
@@ -143,10 +154,13 @@ func adoptExistingInstanceOnCreate(ctx context.Context, client *core.UniversalCl
switch state {
case StateNotCreated:
// P0.2: авто-cleanup orphan-инстанса при adopt_existing_on_create=true.
unlock := client.LockInstance(existing.InstanceUid)
if err := client.RunInstanceOperationUniversal(ctx, existing.InstanceUid, "delete", nil); err != nil {
unlock()
return "", fmt.Errorf("не удалось авто-удалить orphan-инстанс %s (статус: %s): %w. %s",
existing.InstanceUid, statusText, err, formatInstanceDetails(existing, serviceID, displayName))
}
unlock()
return client.CreateGenericInstanceUniversalV6(ctx, serviceID, displayName, params)
case StateRunning, StateRunningPending:
@@ -167,9 +181,12 @@ func adoptExistingInstanceOnCreate(ctx context.Context, client *core.UniversalCl
if len(mismatches) > 0 {
return "", fmt.Errorf("required params mismatch for resource_name %s: %s. %s", displayName, FormatRequiredParamMismatchMessage(mismatches), formatInstanceDetails(existing, serviceID, displayName))
}
unlock := client.LockInstance(existing.InstanceUid)
if err := client.RunInstanceOperationUniversal(ctx, existing.InstanceUid, "resume", nil); err != nil {
unlock()
return "", err
}
unlock()
resumed, err := client.GetInstanceState(ctx, existing.InstanceUid)
if err != nil {
return "", err
@@ -83,6 +83,9 @@ func (r *ServiceOperationResource) Create(ctx context.Context, req resource.Crea
return
}
unlock := r.client.LockInstance(instanceUID)
defer unlock()
if err := r.client.RunInstanceOperationUniversalByCode(ctx, instanceUID, operation, params); err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return
@@ -127,6 +130,9 @@ func (r *ServiceOperationResource) Update(ctx context.Context, req resource.Upda
return
}
unlock := r.client.LockInstance(instanceUID)
defer unlock()
if err := r.client.RunInstanceOperationUniversalByCode(ctx, instanceUID, operation, params); err != nil {
resp.Diagnostics.AddError("Ошибка клиента", err.Error())
return