perf(worker): 隔离异步等待登记与执行锁
为异步等待队列登记增加独立的进程内和 PostgreSQL advisory lock 域,使严格队列上限校验不再等待 Worker 执行槽事务的跨城同步提交。任务级锁保持不变,调度期间 active 与 waiting 总量守恒。\n\n新增 PostgreSQL 集成测试,验证执行 scope 锁被占用时等待登记仍可独立完成。\n\n验证:go vet ./...;go test ./... -count=1;隔离 PostgreSQL 集成测试;迁移安全检查;gofmt;git diff --check。
This commit is contained in:
@@ -136,7 +136,7 @@ func (s *Store) QueueTaskAdmissionWithHook(
|
||||
if input.Mode != "async" {
|
||||
return TaskAdmission{}, errors.New("queued admission registration requires asynchronous mode")
|
||||
}
|
||||
return retryAdmissionOperation(ctx, admissionOperationLockKeys(input), func() (TaskAdmission, error) {
|
||||
return retryAdmissionOperation(ctx, queuedAdmissionOperationLockKeys(input), func() (TaskAdmission, error) {
|
||||
return s.queueTaskAdmissionOnce(ctx, input, onQueued)
|
||||
})
|
||||
}
|
||||
@@ -188,8 +188,14 @@ WHERE id = $1::uuid`, input.TaskID).Scan(&taskActive); err != nil {
|
||||
AdmissionScope{ScopeType: "user_group", ScopeKey: admission.UserGroupID},
|
||||
)
|
||||
}
|
||||
// Waiting registration has its own cross-process lock domain. Registrations
|
||||
// still serialize with each other for exact queue-limit checks, but they do
|
||||
// not wait behind a Worker admission transaction holding execution scope
|
||||
// locks through synchronous replication. Moving one task from waiting to
|
||||
// active preserves active+waiting, so concurrent dispatch cannot make this
|
||||
// capacity check over-admit.
|
||||
for _, scope := range normalizedAdmissionScopes(lockScopes) {
|
||||
if err := tryAdmissionTransactionLock(ctx, tx, admissionLockKey(scope)); err != nil {
|
||||
if err := tryAdmissionTransactionLock(ctx, tx, admissionQueueRegistrationLockKey(scope)); err != nil {
|
||||
return TaskAdmission{}, err
|
||||
}
|
||||
}
|
||||
@@ -567,7 +573,7 @@ func (s *Store) RebindWaitingTaskAdmission(ctx context.Context, input TaskAdmiss
|
||||
if err := validateTaskAdmissionInput(input); err != nil {
|
||||
return TaskAdmission{}, err
|
||||
}
|
||||
return retryAdmissionOperation(ctx, admissionOperationLockKeys(input), func() (TaskAdmission, error) {
|
||||
return retryAdmissionOperation(ctx, queuedAdmissionOperationLockKeys(input), func() (TaskAdmission, error) {
|
||||
return s.rebindWaitingTaskAdmissionOnce(ctx, input)
|
||||
})
|
||||
}
|
||||
@@ -598,7 +604,7 @@ func (s *Store) rebindWaitingTaskAdmissionOnce(ctx context.Context, input TaskAd
|
||||
AdmissionScope{ScopeType: "user_group", ScopeKey: current.UserGroupID},
|
||||
)
|
||||
for _, scope := range normalizedAdmissionScopes(lockScopes) {
|
||||
if err := tryAdmissionTransactionLock(ctx, tx, admissionLockKey(scope)); err != nil {
|
||||
if err := tryAdmissionTransactionLock(ctx, tx, admissionQueueRegistrationLockKey(scope)); err != nil {
|
||||
return TaskAdmission{}, err
|
||||
}
|
||||
}
|
||||
@@ -1099,6 +1105,10 @@ func admissionLockKey(scope AdmissionScope) string {
|
||||
return fmt.Sprintf("task-admission:%d:%s:%d:%s", len(scope.ScopeType), scope.ScopeType, len(scope.ScopeKey), scope.ScopeKey)
|
||||
}
|
||||
|
||||
func admissionQueueRegistrationLockKey(scope AdmissionScope) string {
|
||||
return fmt.Sprintf("task-admission-queue:%d:%s:%d:%s", len(scope.ScopeType), scope.ScopeType, len(scope.ScopeKey), scope.ScopeKey)
|
||||
}
|
||||
|
||||
type admissionScopeState struct {
|
||||
Scope AdmissionScope
|
||||
Active float64
|
||||
|
||||
Reference in New Issue
Block a user