Implement incremental JSONL export with dirty issue tracking
Optimize auto-flush by tracking which issues have changed instead of exporting the entire database on every flush. For large projects with 1000+ issues, this provides significant performance improvements. Changes: - Add dirty_issues table to schema with issue_id and marked_at columns - Implement dirty tracking functions in new dirty.go file: * MarkIssueDirty() - Mark single issue as needing export * MarkIssuesDirty() - Batch mark multiple issues efficiently * GetDirtyIssues() - Query which issues need export * ClearDirtyIssues() - Clear tracking after successful export * GetDirtyIssueCount() - Monitor dirty issue count - Update all CRUD operations to mark affected issues as dirty: * CreateIssue, UpdateIssue, DeleteIssue * AddDependency, RemoveDependency (marks both issues) * AddLabel, RemoveLabel, AddEvent - Modify export to support incremental mode: * Add --incremental flag to export only dirty issues * Used by auto-flush for performance * Full export still available without flag - Add Storage interface methods for dirty tracking Performance impact: With incremental export, large databases only write changed issues instead of regenerating entire JSONL file on every auto-flush. Closes bd-39 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -110,6 +110,26 @@ func (s *SQLiteStorage) AddDependency(ctx context.Context, dep *types.Dependency
|
||||
return fmt.Errorf("failed to record event: %w", err)
|
||||
}
|
||||
|
||||
// Mark both issues as dirty for incremental export
|
||||
// (dependencies are exported with each issue, so both need updating)
|
||||
now := time.Now()
|
||||
stmt, err := tx.PrepareContext(ctx, `
|
||||
INSERT INTO dirty_issues (issue_id, marked_at)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT (issue_id) DO UPDATE SET marked_at = excluded.marked_at
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare dirty statement: %w", err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, dep.IssueID, now); err != nil {
|
||||
return fmt.Errorf("failed to mark issue dirty: %w", err)
|
||||
}
|
||||
if _, err := stmt.ExecContext(ctx, dep.DependsOnID, now); err != nil {
|
||||
return fmt.Errorf("failed to mark dependency target dirty: %w", err)
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
@@ -137,6 +157,25 @@ func (s *SQLiteStorage) RemoveDependency(ctx context.Context, issueID, dependsOn
|
||||
return fmt.Errorf("failed to record event: %w", err)
|
||||
}
|
||||
|
||||
// Mark both issues as dirty for incremental export
|
||||
now := time.Now()
|
||||
stmt, err := tx.PrepareContext(ctx, `
|
||||
INSERT INTO dirty_issues (issue_id, marked_at)
|
||||
VALUES (?, ?)
|
||||
ON CONFLICT (issue_id) DO UPDATE SET marked_at = excluded.marked_at
|
||||
`)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare dirty statement: %w", err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
if _, err := stmt.ExecContext(ctx, issueID, now); err != nil {
|
||||
return fmt.Errorf("failed to mark issue dirty: %w", err)
|
||||
}
|
||||
if _, err := stmt.ExecContext(ctx, dependsOnID, now); err != nil {
|
||||
return fmt.Errorf("failed to mark dependency target dirty: %w", err)
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user