Skip to content

Conversation

@bcho
Copy link
Member

@bcho bcho commented Jan 3, 2026

This pull request introduces significant improvements to task cleanup performance and reliability in the absurd-sqlite-extension, along with enhancements to developer experience and error handling in the standalone app. The main focus is on optimizing the cleanup of completed tasks and their dependencies, making the process more robust for large datasets and concurrent operations. Additionally, the UI now provides clearer feedback for migration errors, and developer workflows for extension loading are improved.

Task cleanup performance and reliability:

  • Refactored the absurd_cleanup_tasks function to use a uniquely named temporary table for selecting terminal tasks, avoiding collisions during concurrent cleanups and improving referential integrity by deleting dependent rows (waits, checkpoints, runs) before tasks, with foreign key checks temporarily disabled for speed. Foreign key enforcement is reliably restored after the transaction. [1] [2] [3]
  • Added an index on absurd_waits (queue_name, task_id) to speed up cascades during task cleanup.
  • Added a large-scale test (test_cleanup_tasks_large_with_children) to verify correctness and performance of task cleanup with many tasks and dependent rows.
  • Increased the default cleanup batch size in the Tauri data provider from 500 to 5000 for more efficient deletions.

Developer and user experience improvements:

  • Improved extension loading in development: the app now checks both debug and release build outputs for the SQLite extension, with clearer logging and warnings if not found. [1] [2]
  • Fixed a parameter naming issue in the TypeScript provider to match backend expectations for migration application.
  • Enhanced migration error handling in the settings page: migration errors are now displayed to the user, with specific guidance if migration files are newer than the loaded extension. [1] [2] [3] [4]

@bcho bcho marked this pull request as ready for review January 3, 2026 06:27
@bcho bcho requested a review from Copilot January 3, 2026 06:27
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR optimizes task cleanup performance in the absurd-sqlite-extension by refactoring the cleanup logic to use temporary tables, disable foreign key checks during deletion, and manually delete dependent rows in the correct order. The changes also improve developer experience with better extension loading fallbacks and provide clearer user feedback for migration errors.

Key changes:

  • Refactored absurd_cleanup_tasks to use uniquely-named temporary tables and manually handle cascading deletes with foreign keys temporarily disabled for performance
  • Added index on absurd_waits (queue_name, task_id) to speed up cleanup operations
  • Increased default cleanup batch size from 500 to 5,000 tasks

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
absurd-sqlite-extension/src/run.rs Refactored cleanup function to use temp tables and manual cascade deletion with FK checks disabled
absurd-sqlite-extension/src/lib.rs Added large-scale test (100k tasks) to verify cleanup correctness and performance
absurd-sqlite-extension/migrations/2.sql Added index on absurd_waits to speed up task cleanup operations
standalone/src-tauri/src/db_commands.rs Increased cleanup batch size from 500 to 5000
standalone/src-tauri/src/db.rs Improved extension path resolution to check debug/release builds in development
standalone/src/lib/providers/absurdData.ts Fixed parameter name to match backend expectations (migration_id → migrationId)
standalone/src/routes/settings/+page.svelte Added error handling and display for migration failures with helpful messages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 695 to 697
sql::exec_with_bind_text(db, "commit", &[])?;
sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[])?;
count
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The foreign key pragma is set to "off" outside the transaction on line 611, but if the transaction commit on line 695 fails (between the commit and re-enabling foreign keys on line 696), foreign keys will remain disabled for the connection. The error handling should ensure foreign keys are re-enabled even if the commit fails.

Consider wrapping the commit in error handling that guarantees foreign key restoration, similar to how the rollback path handles it.

Suggested change
sql::exec_with_bind_text(db, "commit", &[])?;
sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[])?;
count
match sql::exec_with_bind_text(db, "commit", &[]) {
Ok(_) => {
let _ = sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[]);
count
}
Err(err) => {
let _ = sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[]);
return Err(err);
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +619 to +626
let temp_table = format!("temp_cleanup_task_ids_{}", uuid::Uuid::now_v7().simple());
let drop_sql = format!("drop table if exists {}", temp_table);
let create_sql = format!(
"create temp table {} (task_id text primary key)",
temp_table
);
sql::exec_batch(db, &drop_sql)?;
sql::exec_batch(db, &create_sql)?;
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temp_table name is constructed using format! with a UUID, making it vulnerable to SQL injection if the UUID generation ever produces unexpected characters. While uuid::Uuid::now_v7().simple() should only produce alphanumeric characters, it's safer to explicitly validate or quote the table name, or use a prepared statement approach.

Consider using a fixed prefix with validation, or documenting the security assumption about UUID format explicitly.

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +74
if (message.includes("target version is newer than available migrations")) {
return "Migration files are newer than the loaded SQLite extension. Rebuild the extension and restart the app.";
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message check uses a string contains check to detect a specific migration error. This approach is fragile because it depends on the exact error message text, which may change. If the error message wording changes in the future, this special case handling will silently fail to provide the helpful message.

Consider using a typed error or error code instead of string matching for more reliable error detection.

Copilot uses AI. Check for mistakes.
Comment on lines +611 to 617
sql::exec_with_bind_text(db, "pragma foreign_keys = off", &[])?;
if let Err(err) = sql::exec_with_bind_text(db, "begin immediate", &[]) {
let _ = sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[]);
return Err(err);
}

let deleted = (|| -> Result<i64> {
Copy link

Copilot AI Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temporary table name uses a UUID to avoid collisions, but the foreign key pragma is set at the connection level (line 611) before the transaction begins. In concurrent scenarios where multiple cleanup operations run on the same connection, this could lead to race conditions where one operation disables foreign keys while another is in progress.

SQLite's PRAGMA foreign_keys is a per-connection setting, not transaction-scoped. If the same database connection is reused across multiple cleanup calls, there's a window between line 611 and line 615 where another operation could begin and be affected by the disabled foreign keys.

Ensure that each cleanup operation uses a separate connection, or consider alternative approaches like using savepoints or ensuring serialized access to cleanup operations.

Suggested change
sql::exec_with_bind_text(db, "pragma foreign_keys = off", &[])?;
if let Err(err) = sql::exec_with_bind_text(db, "begin immediate", &[]) {
let _ = sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[]);
return Err(err);
}
let deleted = (|| -> Result<i64> {
if let Err(err) = sql::exec_with_bind_text(db, "begin immediate", &[]) {
let _ = sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[]);
return Err(err);
}
let deleted = (|| -> Result<i64> {
sql::exec_with_bind_text(db, "pragma foreign_keys = off", &[])?;

Copilot uses AI. Check for mistakes.
@bcho bcho merged commit 0ecb6d4 into main Jan 3, 2026
8 checks passed
@bcho bcho deleted the delete-task-perf branch January 3, 2026 06:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants