Fix blocked cache invalidation in transaction operations (bd-1c4h)
Transaction operations weren't invalidating the blocked_issues_cache, causing GetReadyWork to return stale results after transactional changes. Changes: - Refactor invalidateBlockedCache to accept execer interface (supports both *sql.Tx and *sql.Conn) - Add cache invalidation in transaction.go for: - UpdateIssue (when status changes) - CloseIssue (always - closed issues don't block - AddDependency (for DepBlocks/DepParentChild types) - RemoveDependency (queries type before deletion) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> EOF )
This commit is contained in:
@@ -100,11 +100,10 @@ type execer interface {
|
||||
|
||||
// rebuildBlockedCache completely rebuilds the blocked_issues_cache table
|
||||
// This is used during cache invalidation when dependencies change
|
||||
func (s *SQLiteStorage) rebuildBlockedCache(ctx context.Context, tx *sql.Tx) error {
|
||||
// Use the transaction if provided, otherwise use direct db connection
|
||||
var exec execer = s.db
|
||||
if tx != nil {
|
||||
exec = tx
|
||||
func (s *SQLiteStorage) rebuildBlockedCache(ctx context.Context, exec execer) error {
|
||||
// Use direct db connection if no execer provided
|
||||
if exec == nil {
|
||||
exec = s.db
|
||||
}
|
||||
|
||||
// Clear the cache
|
||||
@@ -152,6 +151,6 @@ func (s *SQLiteStorage) rebuildBlockedCache(ctx context.Context, tx *sql.Tx) err
|
||||
|
||||
// invalidateBlockedCache rebuilds the blocked issues cache
|
||||
// Called when dependencies change or issue status changes
|
||||
func (s *SQLiteStorage) invalidateBlockedCache(ctx context.Context, tx *sql.Tx) error {
|
||||
return s.rebuildBlockedCache(ctx, tx)
|
||||
func (s *SQLiteStorage) invalidateBlockedCache(ctx context.Context, exec execer) error {
|
||||
return s.rebuildBlockedCache(ctx, exec)
|
||||
}
|
||||
|
||||
@@ -414,6 +414,14 @@ func (t *sqliteTxStorage) UpdateIssue(ctx context.Context, id string, updates ma
|
||||
return fmt.Errorf("failed to mark issue dirty: %w", err)
|
||||
}
|
||||
|
||||
// Invalidate blocked issues cache if status changed (bd-1c4h)
|
||||
// Status changes affect which issues are blocked (blockers must be open/in_progress/blocked)
|
||||
if _, statusChanged := updates["status"]; statusChanged {
|
||||
if err := t.parent.invalidateBlockedCache(ctx, t.conn); err != nil {
|
||||
return fmt.Errorf("failed to invalidate blocked cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -499,6 +507,12 @@ func (t *sqliteTxStorage) CloseIssue(ctx context.Context, id string, reason stri
|
||||
return fmt.Errorf("failed to mark issue dirty: %w", err)
|
||||
}
|
||||
|
||||
// Invalidate blocked issues cache since status changed to closed (bd-1c4h)
|
||||
// Closed issues don't block others, so this affects blocking calculations
|
||||
if err := t.parent.invalidateBlockedCache(ctx, t.conn); err != nil {
|
||||
return fmt.Errorf("failed to invalidate blocked cache: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -646,11 +660,30 @@ func (t *sqliteTxStorage) AddDependency(ctx context.Context, dep *types.Dependen
|
||||
return fmt.Errorf("failed to mark depends-on issue dirty: %w", err)
|
||||
}
|
||||
|
||||
// Invalidate blocked cache for blocking dependencies (bd-1c4h)
|
||||
if dep.Type == types.DepBlocks || dep.Type == types.DepParentChild {
|
||||
if err := t.parent.invalidateBlockedCache(ctx, t.conn); err != nil {
|
||||
return fmt.Errorf("failed to invalidate blocked cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveDependency removes a dependency within the transaction.
|
||||
func (t *sqliteTxStorage) RemoveDependency(ctx context.Context, issueID, dependsOnID string, actor string) error {
|
||||
// First, check what type of dependency is being removed (bd-1c4h)
|
||||
var depType types.DependencyType
|
||||
err := t.conn.QueryRowContext(ctx, `
|
||||
SELECT type FROM dependencies WHERE issue_id = ? AND depends_on_id = ?
|
||||
`, issueID, dependsOnID).Scan(&depType)
|
||||
|
||||
// Store whether cache needs invalidation before deletion
|
||||
needsCacheInvalidation := false
|
||||
if err == nil {
|
||||
needsCacheInvalidation = (depType == types.DepBlocks || depType == types.DepParentChild)
|
||||
}
|
||||
|
||||
result, err := t.conn.ExecContext(ctx, `
|
||||
DELETE FROM dependencies WHERE issue_id = ? AND depends_on_id = ?
|
||||
`, issueID, dependsOnID)
|
||||
@@ -684,6 +717,13 @@ func (t *sqliteTxStorage) RemoveDependency(ctx context.Context, issueID, depends
|
||||
return fmt.Errorf("failed to mark depends-on issue dirty: %w", err)
|
||||
}
|
||||
|
||||
// Invalidate blocked cache if this was a blocking dependency (bd-1c4h)
|
||||
if needsCacheInvalidation {
|
||||
if err := t.parent.invalidateBlockedCache(ctx, t.conn); err != nil {
|
||||
return fmt.Errorf("failed to invalidate blocked cache: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user