Commit Graph

11 Commits

Author SHA1 Message Date
ruby
a45b441bc5 fix(sqlite): use BEGIN IMMEDIATE without retry loop (GH#1272)
Some checks failed
CI / Check version consistency (push) Successful in 3s
CI / Check for .beads changes (push) Has been skipped
CI / Test (ubuntu-latest) (push) Failing after 8m13s
CI / Lint (push) Failing after 3m18s
CI / Test Nix Flake (push) Failing after 1m7s
Nightly Full Tests / Full Test Suite (push) Failing after 36m59s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (Windows - smoke) (push) Has been cancelled
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>
2026-01-23 13:43:31 -08:00
ruby
442ad0f0e5 fix(sqlite): add retry logic to transaction entry points (GH#1272)
Update withTx to use BEGIN IMMEDIATE with exponential backoff retry
on SQLITE_BUSY errors. This prevents "database is locked" failures
during concurrent operations (daemon + CLI, multi-agent workflows).

Changes:
- withTx now uses beginImmediateWithRetry (same pattern as RunInTransaction)
- Add dbExecutor interface for helper functions that work with both
  *sql.Tx and *sql.Conn
- Update all withTx callers to use *sql.Conn
- Refactor DeleteIssue to use withTx (fixes the specific error in auto-import)
- Update markIssuesDirtyTx to accept dbExecutor interface

Affected paths:
- MarkIssuesDirty, ClearDirtyIssuesByID (dirty.go)
- AddDependency, RemoveDependency (dependencies.go)
- executeLabelOperation (labels.go)
- AddComment (events.go)
- ApplyCompaction (compact.go)
- DeleteIssue (queries.go)

Note: Some direct BeginTx calls in queries.go (CloseIssue, UpdateIssue,
ReopenIssue, DeleteIssues) still use the old pattern and could be
refactored in a follow-up.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 13:43:30 -08:00
Steve Yegge
071fc96206 fix: resolve golangci-lint errors
- Add error check for fmt.Fprintf in tips.go (errcheck)
- Add nolint for safe SQL formatting in transaction.go (gosec G201)
- Fix 'cancelled' -> 'canceled' spelling (misspell)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 22:21:55 -08:00
Steve Yegge
125d02860c Implement transaction retry logic for SQLITE_BUSY (bd-ola6)
Add exponential backoff retry for BEGIN IMMEDIATE transactions to handle
concurrent write load without spurious failures.

Changes:
- Add IsBusyError() helper to detect database locked errors
- Add beginImmediateWithRetry() with exponential backoff (10ms, 20ms, 40ms, 80ms, 160ms)
- Update CreateIssue and CreateIssuesInBatch to use retry logic
- Add comprehensive tests for error detection and retry behavior
- Handles context cancellation between retry attempts
- Fails fast on non-busy errors

This eliminates spurious SQLITE_BUSY failures under normal concurrent usage
while maintaining proper error handling for other failure modes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 01:10:16 -08:00
Steve Yegge
d4f9a05bb2 Fix bd-pq5k: merge conflicts now prefer closed>open and deletion>modification
CHANGES:
1. Merge logic (internal/merge/merge.go):
   - Added mergeStatus() enforcing closed ALWAYS wins over open
   - Fixed closed_at handling: only set when status='closed'
   - Changed deletion handling: deletion ALWAYS wins over modification

2. Deletion tracking (cmd/bd/snapshot_manager.go):
   - Updated ComputeAcceptedDeletions to accept all merge deletions
   - Removed "unchanged locally" check (deletion wins regardless)

3. FK constraint helper (internal/storage/sqlite/util.go):
   - Added IsForeignKeyConstraintError() for bd-koab
   - Detects FK violations for graceful import handling

TESTS UPDATED:
- TestMergeStatus: comprehensive status merge tests
- TestIsForeignKeyConstraintError: FK constraint detection
- bd-pq5k test: validates no invalid state (status=open with closed_at)
- Deletion tests: reflect new deletion-wins behavior
- All tests pass ✓

This ensures issues never get stuck in invalid states and prevents
the insane situation where issues never die!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-23 21:42:43 -08:00
Steve Yegge
3b2cac4d8f Centralize error handling patterns in storage layer (bd-bwk2)
Created internal/storage/sqlite/errors.go with:
- Sentinel errors: ErrNotFound, ErrInvalidID, ErrConflict, ErrCycle
- wrapDBError helpers that auto-convert sql.ErrNoRows to ErrNotFound
- Type-safe error checking with errors.Is() compatibility

Updated error handling across storage layer:
- dirty.go: Added context to error returns, converted sql.ErrNoRows checks
- util.go: Updated withTx to use wrapDBError
- batch_ops.go: Added context wrapping to batch operations
- dependencies.go: Wrapped errors from markIssuesDirtyTx calls
- ids.go: Added error wrapping for ID validation

Also restored sqlite.go that was accidentally deleted in previous commit.

All tests pass. Provides consistent error wrapping with operation context
for better debugging.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 19:17:57 -05:00
Steve Yegge
7d6d64d2c1 Refactor: Replace manual transaction handling with withTx() helper
Fixes bd-1b0a

Changes:
- Added withTx() helper in util.go for cleaner transaction handling
- Kept ExecInTransaction() as deprecated wrapper for backward compatibility
- Refactored all manual BEGIN/COMMIT/ROLLBACK blocks to use withTx():
  - events.go: AddComment
  - dirty.go: MarkIssuesDirty, ClearDirtyIssuesByID
  - labels.go: executeLabelOperation
  - dependencies.go: AddDependency, RemoveDependency
  - compact.go: ApplyCompaction

All tests pass.

Amp-Thread-ID: https://ampcode.com/threads/T-dfacc972-f6c8-4bb3-8997-faa079b5d070
Co-authored-by: Amp <amp@ampcode.com>
2025-11-02 12:55:47 -08:00
Steve Yegge
0b852f52d9 Remove obsolete renumber and stale commands, fix test
- Remove renumber.go: Hash IDs eliminated need for ID compaction
- Remove stale.go: Executor/heartbeat tables were never implemented
- Fix TestListCommand: duplicate dependency constraint violation
- Update comments removing references to removed commands

Amp-Thread-ID: https://ampcode.com/threads/T-3dcd8681-c7d3-4fe1-9750-b38279b56cdb
Co-authored-by: Amp <amp@ampcode.com>
2025-10-31 21:27:33 -07:00
Steve Yegge
ddab26315f Remove dead code found by deadcode analyzer
- Removed 5 unreachable functions (~200 LOC)
- computeIssueContentHash, shouldSkipExport from autoflush.go
- addDependencyUnchecked, removeDependencyIfExists from dependencies.go
- isUniqueConstraintError alias from util.go
- All tests still pass
- Closes bd-7c5915ae
2025-10-31 15:12:01 -07:00
Steve Yegge
ebb425388c bd-109: Add retry logic and race condition handling for N-way collisions
- Added ExecInTransaction helper for atomic database operations
- Added IsUniqueConstraintError to detect UNIQUE constraint violations
- Wrapped RemapCollisions with retry logic (3 attempts with counter sync)
- Enhanced handleRename to detect race conditions where target ID exists
- Added defensive checks for when old ID has been deleted by another clone

Progress: Improves N-way collision handling, though full solution requires
more work (tracked in bd-108). Tests now reach later convergence rounds
before hitting complex collision scenarios.

Amp-Thread-ID: https://ampcode.com/threads/T-2b850a80-f8bd-4e38-b661-e33d1cfa7281
Co-authored-by: Amp <amp@ampcode.com>
2025-10-29 10:45:25 -07:00
Steve Yegge
790233f748 feat: Add 'bd stale' command to show and release orphaned executor claims
- Implements bd stale command to show issues with execution_state where executor is dead/stopped
- Adds --release flag to automatically release orphaned issues
- Adds --threshold flag to customize heartbeat staleness threshold (default: 300s/5min)
- Handles missing executor instances (LEFT JOIN) for cases where executor was deleted
- Adds QueryContext and BeginTx helper methods to SQLiteStorage for advanced queries
- Fixes ExternalRef comparison bug in import_shared.go (pointer vs string)
- Removes unused imports in import.go

Resolves vc-124
2025-10-18 17:14:21 -07:00