Open
Conversation
ba337ba to
1047aa2
Compare
There was a problem hiding this comment.
Pull request overview
Adds support for configuring query execution timeouts so long-running SQLite queries can be interrupted (via sqlite3_interrupt) using a background Tokio task that tracks per-query deadlines.
Changes:
- Introduces
QueryTimeoutManager/TimeoutGuardto track deadlines and interrupt connections when timeouts expire. - Adds
defaultQueryTimeout(connection-wide default) andQueryOptions.queryTimeout(per-query override) and threads these throughDatabase.execand statement execution/iteration paths. - Updates JS wrappers, TypeScript definitions, docs, and integration tests to expose/verify the new options.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
src/query_timeout.rs |
New timeout manager + guard implementation and unit tests. |
src/lib.rs |
Wires default/per-query timeouts into NAPI-exposed DB/statement APIs and sync wrappers. |
promise.js |
Adds queryOptions plumbing for async wrapper API. |
compat.js |
Adds queryOptions plumbing for sync wrapper API. |
index.d.ts |
Updates TS types for new options / signatures. |
docs/api.md |
Documents defaultQueryTimeout and per-query options. |
integration-tests/tests/sync.test.js |
Adds sync integration coverage for query timeouts. |
integration-tests/tests/async.test.js |
Adds async integration coverage for query timeouts. |
Cargo.toml |
Adds Rust dev-deps needed for the new unit tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
Comment on lines
+58
to
+71
| impl QueryTimeoutManager { | ||
| pub fn new() -> Self { | ||
| let inner = Arc::new(Inner { | ||
| entries: Mutex::new(Entries { | ||
| heap: BinaryHeap::new(), | ||
| next_id: 0, | ||
| }), | ||
| notify: Arc::new(Notify::new()), | ||
| }); | ||
| let bg = inner.clone(); | ||
| tokio::spawn(async move { | ||
| Self::background_task(bg).await; | ||
| }); | ||
| Self { inner } |
Comment on lines
+30
to
+37
| #[derive(Clone)] | ||
| struct Entry { | ||
| id: u64, | ||
| deadline: Instant, | ||
| conn: Arc<libsql::Connection>, | ||
| /// Cleared when the guard is dropped (query finished in time). | ||
| active: Arc<AtomicBool>, | ||
| } |
src/query_timeout.rs
Outdated
Comment on lines
+78
to
+83
| let mut entries = self.inner.entries.lock().unwrap(); | ||
| let id = entries.next_id; | ||
| entries.next_id += 1; | ||
| let entry = Entry { | ||
| id, | ||
| deadline: Instant::now() + timeout, |
src/lib.rs
Outdated
Comment on lines
+666
to
+670
| if timeout_ms > 0.0 { | ||
| Some(Duration::from_millis(timeout_ms as u64)) | ||
| } else { | ||
| None | ||
| } |
Comment on lines
+339
to
351
| let query_timeout = opts | ||
| .as_ref() | ||
| .and_then(|o| o.defaultQueryTimeout) | ||
| .and_then(query_timeout_duration); | ||
| let timeout_manager = Arc::new(QueryTimeoutManager::new()); | ||
| Ok(Database { | ||
| db: Some(db), | ||
| conn: Some(Arc::new(conn)), | ||
| default_safe_integers, | ||
| memory, | ||
| query_timeout, | ||
| timeout_manager, | ||
| }) |
Comment on lines
+55
to
+61
| if (bindParameters.length > 1 && isQueryOptions(bindParameters[bindParameters.length - 1])) { | ||
| return { | ||
| params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1), | ||
| queryOptions: bindParameters[bindParameters.length - 1], | ||
| }; | ||
| } | ||
| return { params: bindParameters.length === 1 ? bindParameters[0] : bindParameters, queryOptions: undefined }; |
Comment on lines
+42
to
+48
| if (bindParameters.length > 1 && isQueryOptions(bindParameters[bindParameters.length - 1])) { | ||
| return { | ||
| params: bindParameters.length === 2 ? bindParameters[0] : bindParameters.slice(0, -1), | ||
| queryOptions: bindParameters[bindParameters.length - 1], | ||
| }; | ||
| } | ||
| return { params: bindParameters.length === 1 ? bindParameters[0] : bindParameters, queryOptions: undefined }; |
| let pluck = self.mode.pluck.load(Ordering::SeqCst); | ||
| let stmt = self.stmt.clone(); | ||
| stmt.reset(); | ||
| let params = map_params(&stmt, params).unwrap(); |
ee99f13 to
90639cc
Compare
6d3e93b to
38ccae1
Compare
Adds `defaultQueryTimeout` connection option and per-query
`{ queryTimeout: ms }` parameter to interrupt long-running queries
via sqlite3_interrupt(). Available on exec(), run(), get(), iterate(),
and all() in both sync and async APIs.
A shared background tokio task manages deadlines with a min-heap.
Registering a query returns a TimeoutGuard — if the deadline expires
before the guard is dropped, the connection is interrupted. Dropping
the guard early (query completed) cancels the timeout and wakes the
background task to release the connection reference immediately.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
defaultQueryTimeoutconnection option and per-query{ queryTimeout: ms }parameter to interrupt long-running queries via sqlite3_interrupt(). Available on exec(), run(), get(), iterate(), and all() in both sync and async APIs.A shared background tokio task manages deadlines with a min-heap. Registering a query returns a TimeoutGuard — if the deadline expires before the guard is dropped, the connection is interrupted. Dropping the guard early (query completed) cancels the timeout and wakes the background task to release the connection reference immediately.