-
Notifications
You must be signed in to change notification settings - Fork 0
perf: improve task delete performance #12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this 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_tasksto 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.
| sql::exec_with_bind_text(db, "commit", &[])?; | ||
| sql::exec_with_bind_text(db, "pragma foreign_keys = on", &[])?; | ||
| count |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
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.
| 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); | |
| } | |
| } |
| 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)?; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
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.
| 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."; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
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.
| 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> { |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
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.
| 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", &[])?; |
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:
absurd_cleanup_tasksfunction 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]absurd_waits (queue_name, task_id)to speed up cascades during task cleanup.test_cleanup_tasks_large_with_children) to verify correctness and performance of task cleanup with many tasks and dependent rows.Developer and user experience improvements: