{"record":{"id":"cedff55108bbfaed","repo":"plandex-ai/plandex","slug":"failed-to-get-db-lock-w","errorCode":null,"errorMessage":"failed to get DB lock: %w","messagePattern":"failed to get DB lock: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"app/server/db/queue.go","lineNumber":210,"sourceCode":"\n\t\t\t\tdefer func() {\n\t\t\t\t\tlog.Printf(\"[Queue] Releasing DB lock %s for plan %s\", lockId, firstOp.planId)\n\t\t\t\t\treleaseErr := deleteRepoLockDB(lockId, firstOp.planId, firstOp.reason, 0)\n\t\t\t\t\tif releaseErr != nil {\n\t\t\t\t\t\tlog.Printf(\"[Queue] Failed to release DB lock: %v\", releaseErr)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlog.Printf(\"[Queue] DB lock %s released successfully\", lockId)\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t}\n\n\t\t\tif err != nil {\n\t\t\t\tlog.Printf(\"[Queue] Failed to get DB lock: %v\", err)\n\t\t\t\tfor _, op := range ops {\n\t\t\t\t\tif locksVerboseLogging {\n\t\t\t\t\t\tlog.Printf(\"[Queue] Notifying operation %s (%s) of lock failure\", op.id, op.reason)\n\t\t\t\t\t}\n\t\t\t\t\top.done <- fmt.Errorf(\"failed to get DB lock: %w\", err)\n\t\t\t\t}\n\t\t\t\t// we still need to process the rest of the queue\n\t\t\t\t// if the error is critical, caller will handle it\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif locksVerboseLogging {\n\t\t\t\tlog.Printf(\"[Queue] Acquired DB lock %s, processing batch of %d operations\", lockId, len(ops))\n\t\t\t}\n\n\t\t\trepo := getGitRepo(firstOp.orgId, firstOp.planId)\n\t\t\tvar needsRollback bool\n\n\t\t\t// Process the batch\n\t\t\t// If it's a writer => single op\n\t\t\t// If multiple same‐branch readers => do them in parallel\n\t\t\tvar wg sync.WaitGroup\n\t\t\tfor _, op := range ops {","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/db/queue.go#L192-L228","documentation":"The queue acquires a DB-backed lock before processing batched operations. When lock acquisition fails, each queued operation's done channel receives `failed to get DB lock: %w` so every waiting caller learns the batch could not proceed; the queue then returns to process the rest of the queue rather than blocking.","triggerScenarios":"The DB lock query/transaction used by the queue fails at queue.go:210 — DB unreachable, lock contention timeout, or an aborted lock transaction — while a batch of queued ops is pending.","commonSituations":"Long-running write transactions holding the lock past the timeout, database failover/restart, connection pool exhaustion preventing the lock acquisition query from running.","solutions":["Inspect the wrapped err logged as '[Queue] Failed to get DB lock' for the root cause","Check which transaction holds the DB lock (pg_locks / blocking queries) and shorten it","Increase the lock acquisition timeout or add bounded retry with backoff","Verify DB connectivity and pool sizing under load"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"if err := db.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"database unavailable before queuing ops: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"done := make(chan error, 1)\nqueueOp(op, done)\nselect {\ncase err := <-done:\n    if err != nil && strings.Contains(err.Error(), \"failed to get DB lock\") {\n        // bounded retry with backoff\n        time.Sleep(backoff)\n        queueOp(op, done)\n    }\ncase <-ctx.Done():\n    return ctx.Err()\n}","preventionTips":["Keep transactions that hold the DB lock short","Enable locksVerboseLogging to diagnose contention","Size the connection pool for lock acquisition queries","Use context timeouts on ops waiting on the queue"],"tags":["go","database","locking","queue","concurrency"],"backgroundTag":"db-lock-acquisition-failed","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}