dotnet / dotnet/efcore

SQLite: Implement SqliteCommand.Cancel() and register CancellationToken handler

Open
#38,757 1 comment 0 reactions 0 assignees View on GitHub
area-adonet-sqlite customer-reported
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

### Question

In the Aspire dashboard, SQLite queries are synchronous, so potentially slow reads run inside `Task.Run` to avoid blocking the Blazor UI thread. We would also like cancellation to stop an in-progress SQLite scan instead of only discarding its result while the query continues using a thread-pool thread and connection.

`Microsoft.Data.Sqlite.SqliteCommand.Cancel()` is documented as doing nothing. We currently register the cancellation token directly against the native connection handle and call `sqlite3_interrupt`:

```csharp
internal CancellationTokenRegistration RegisterInterrupt(CancellationToken cancellationToken)
{
if (!cancellationToken.CanBeCanceled)
{
return default;
}

return cancellationToken.Register(static state =>
{
var connection = (SqliteConnection)state!;
if (connection.Handle is { } handle)
{
raw.sqlite3_interrupt(handle);
}
}, connection);
}

private static Task RunReadAsync(Func read, CancellationToken cancellationToken) =>
Task.Run(() =>
{
try
{
return read(cancellationToken);
}
catch (SqliteException ex) when (ex.SqliteErrorCode == raw.SQLITE_INTERRUPT)
{
cancellationToken.ThrowIfCancellationRequested();
throw;
}
}, cancellationToken);
```

Each read opens a dedicated `SqliteConnection`, registers the callback before executing synchronous Dapper queries, and disposes the `CancellationTokenRegistration` before disposing/closing the connection. Disposing the registration waits for an active callback, so the native handle should not be used after the connection releases it.

Questions:

1. Is calling `SQLitePCL.raw.sqlite3_interrupt(connection.Handle)` this way a supported and safe cancellation pattern with Microsoft.Data.Sqlite, including when connection pooling is enabled?
2. Is there a preferred public Microsoft.Data.Sqlite API for interrupting an already-running synchronous query?
3. Why does `SqliteCommand.Cancel()` not call `sqlite3_interrupt`? Is this because interruption is connection-wide rather than command-specific, because of races with connection/handle reuse, or another semantic issue?
4. Some reads execute multiple statements while one token registration is active. If cancellation occurs between statements, `sqlite3_interrupt` runs while no statement is active and a subsequent statement can still start. Should callers explicitly check the token between statements, register per statement, or use another approach?
5. Are there other races or lifetime concerns we should account for when translating `SQLITE_INTERRUPT` to `OperationCanceledException`?

SQLite documents `sqlite3_interrupt` as safe to call from another thread while a query is running, but we want to confirm the intended Microsoft.Data.Sqlite usage rather than depend on native implementation details incorrectly.

Relevant Aspire implementation:
- https://github.com/dotnet/aspire/blob/1dceef8544f40bf7146828f62440c0cb80fae658/src/Aspire.Dashboard/ServiceClient/TracingSqliteConnection.cs
- https://github.com/dotnet/aspire/blob/1dceef8544f40bf7146828f62440c0cb80fae658/src/Aspire.Dashboard/Otlp/Storage/SqliteTelemetryRepository.cs

### Versions

- Microsoft.Data.Sqlite.Core: 10.0.10
- Target framework: .NET 8.0
- Operating system: Cross-platform (Windows, Linux, macOS)

Contributor guide

Open the contributing guide

Research direction

Start with SqliteCommand.Cancel and the connection interrupt handling in Microsoft.Data.Sqlite, then review TracingSqliteConnection.cs and SqliteTelemetryRepository.cs in the linked Aspire implementation. Determine the supported cancellation and lifetime behavior for synchronous queries, pooled connections, and multiple statements, and define tests that establish the intended API and SQLITE_INTERRUPT semantics.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp, sqlite
Domain
backend-api-design, databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.