QoreDB LogoQoreDB
Back to blog
ArchitectureTechnical journal

How QoreDB handles query cancellation depending on the engine

Cancelling a long-running query is a routine operation for a database client, but how it is carried out depends entirely on the target engine. PostgreSQL exposes a dedicated mechanism, MySQL another, and MongoDB exposes nothing equivalent on the server side. QoreDB doesn't hide…

Raphaël – Creator of QoreDBRaphaël – Creator of QoreDB
5 min readUpdated Jul 18, 2026
How QoreDB handles query cancellation depending on the engine

Cancelling a long-running query is a routine operation for a database client, but how it is carried out depends entirely on the target engine. PostgreSQL exposes a dedicated mechanism, MySQL another, and MongoDB exposes nothing equivalent on the server side. QoreDB doesn't hide these differences behind an artificial abstraction.

Depending on the driver, the Cancel button triggers either an explicit server call, or the abort of the client-side task, or an honest error message when the engine doesn't know how to do it. This article describes what happens on each click.

One Rust command, six implementations

On the frontend, the user triggers the Tauri command cancel_query with the session identifier and, optionally, that of the query in progress. The backend retrieves the driver associated with the session and calls driver.cancel(session, query_id). The DataEngine trait exposes this entry point, and each driver implements it in its own way. No shared logic decides what happens next: the code immediately delegates to the driver, which knows what its engine supports.

QoreDB maintains a map of active queries per session, indexed by identifier. This table serves two purposes. First, finding the server-side identifier (PostgreSQL PID, MySQL connection_id, SQL Server SPID) to pass it to the cancellation command. Second, keeping a Tokio JoinHandle for the drivers that have no server API, in order to abort the task locally.

PostgreSQL and CockroachDB: pg_cancel_backend

On PostgreSQL, each connection has a server-side PID. The recommended API for cancelling a query in progress is the pg_cancel_backend(pid) function, which sends a signal to the targeted backend. QoreDB retrieves the PIDs registered for the session, then runs the function for each active PID. It is a cooperative call: PostgreSQL interrupts the query between two steps of the execution plan, without corrupting the state.

CockroachDB shares the bulk of the PostgreSQL protocol and uses the same implementation, in the pg_compat.rs module. No adaptation is needed for this engine; wire compatibility is here a concrete asset that avoids forking the cancel code.

MySQL and SQL Server: KILL with the connection identifier

MySQL doesn't expose pg_cancel_backend. The equivalent method is KILL QUERY <connection_id>, which interrupts the running execution without closing the underlying session. QoreDB stores the connection_id returned by the connection when it is opened, then issues the corresponding KILL QUERY at cancellation time. The statement goes through another connection from the pool, which avoids getting blocked on the very connection you want to interrupt.

SQL Server follows a similar logic with its SPID (Server Process Identifier). The Transact-SQL statement KILL <spid> interrupts the query in progress. QoreDB uses the Tiberius client, captures the SPID at connection time, and sends the command via simple_query. The main behavioral difference from MySQL is that KILL on SQL Server marks the session as killed and sometimes requires an extended rollback before the connection becomes usable again. QoreDB doesn't hide this nuance.

MongoDB and Redis: cancelling the task on the client side

MongoDB and Redis do not, in the official Rust drivers, have a standard API for interrupting an operation in progress on the server side from another connection. The approach taken is different. QoreDB runs each query in a Tokio task via tokio::spawn and keeps the corresponding JoinHandle in the active-queries table. On cancellation, it calls handle.abort(), which interrupts the task on the client side.

The practical result: the local connection is freed immediately and the application becomes responsive again. For MongoDB in particular, a find or an aggregation already underway can keep running on the server side until it finishes. This quirk stems from the protocol, not from a shortcoming in QoreDB. The behavior is explicit and the user receives a success response for the local cancellation.

SQLite: an explicit refusal

SQLite doesn't support cancellation from a third-party connection. The native sqlite3_interrupt API must be called on the connection running the query, which is incompatible with a pool where the query already blocks that connection. Rather than faking a success, the SQLite driver returns EngineError::not_supported. The user sees a clear message, and no cancel metric is recorded.

This is a case where transparency takes precedence over apparent consistency. Pretending to cancel a SQLite query (by closing the connection, for example) would introduce side effects more dangerous than a plain admission of incapacity.

The complete flow in use

On the UI side, the Cancel button triggers the cancel_query command with the session ID and, by default, the last known query for that session. The backend routes the call to the driver, which runs its mechanism. On success, the record_cancel metric is incremented and the result is sent back to the frontend. On error (query already finished, or not supported), the message is sanitized and passed through as is.

No automatic retry, no attempt to cancel by any means necessary. The logic is deliberately linear: if the engine's API succeeds, it is cancelled; otherwise the response is honest.

An approach consistent with the rest of QoreDB

Query cancellation is a good example of what it means not to artificially hide the differences between engines. Each driver implements what its protocol allows, no more and no less. PostgreSQL, CockroachDB, MySQL, and SQL Server benefit from real server-side cancellation. MongoDB and Redis cancel on the client side, which is enough in the desktop use case where the goal is to free up the interface quickly. SQLite refuses explicitly. The developer always knows what happens when they click Cancel, because QoreDB doesn't lie to them to preserve a false uniformity.

Newsletter

Stay updated on new releases

Subscribe to get product releases, new drivers notifications, and technical tutorials.

🎁 Bonus: Get our free SQL Performance Cheat Sheet — 9 pages, PostgreSQL / MySQL / SQLite (PDF)!
Share
How QoreDB handles query cancellation depending on the engine - Blog - QoreDB