fix(sqlite): use BEGIN IMMEDIATE without retry loop (GH#1272)

The original PR added retry logic on top of BEGIN IMMEDIATE, but this caused
multi-minute hangs because:

1. Connection has busy_timeout=30s set via pragma
2. Each BEGIN IMMEDIATE waits up to 30s before returning SQLITE_BUSY
3. With 5 retries, worst case was 5 × 30s = 150+ seconds

The fix removes the retry loop since SQLite's busy_timeout already handles
retries internally. BEGIN IMMEDIATE still acquires the write lock early,
preventing deadlocks - we just let busy_timeout handle contention.

Root cause analysis in bd-9ldm.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ruby
2026-01-22 18:55:30 -08:00
committed by Aaron Leiby
parent 718fc49776
commit 35bd93b443
5 changed files with 32 additions and 99 deletions

View File

@@ -273,8 +273,9 @@ func (s *SQLiteStorage) CreateIssuesWithFullOptions(ctx context.Context, issues
}
defer func() { _ = conn.Close() }()
// Use retry logic with exponential backoff to handle SQLITE_BUSY under concurrent load
if err := beginImmediateWithRetry(ctx, conn, 5, 10*time.Millisecond); err != nil {
// Start IMMEDIATE transaction to acquire write lock early.
// The connection's busy_timeout pragma (30s) handles retries if locked.
if _, err := conn.ExecContext(ctx, "BEGIN IMMEDIATE"); err != nil {
return fmt.Errorf("failed to begin immediate transaction: %w", err)
}