Skip to content

Add per-query and default query timeout support#209

Open
penberg wants to merge 1 commit intomainfrom
query-timeout
Open

Add per-query and default query timeout support#209
penberg wants to merge 1 commit intomainfrom
query-timeout

Conversation

@penberg
Copy link
Contributor

@penberg penberg commented Mar 7, 2026

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.

@penberg penberg marked this pull request as draft March 7, 2026 09:03
@penberg penberg marked this pull request as ready for review March 17, 2026 07:35
@penberg penberg force-pushed the query-timeout branch 8 times, most recently from ba337ba to 1047aa2 Compare March 19, 2026 11:32
@penberg penberg requested a review from Copilot March 19, 2026 11:44
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

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 / TimeoutGuard to track deadlines and interrupt connections when timeouts expire.
  • Adds defaultQueryTimeout (connection-wide default) and QueryOptions.queryTimeout (per-query override) and threads these through Database.exec and 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>,
}
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();
@penberg penberg force-pushed the query-timeout branch 3 times, most recently from ee99f13 to 90639cc Compare March 19, 2026 12:43
@penberg penberg changed the title Add query timeout option to interrupt long-running queries Add per-query and default query timeout support Mar 19, 2026
@penberg penberg force-pushed the query-timeout branch 3 times, most recently from 6d3e93b to 38ccae1 Compare March 20, 2026 16:37
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.
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