Dispatch queued messages written by other processes (#39034)

## Why

Durable thread queues can be updated through another SQLite connection, but loaded idle threads need to notice those updates before they can dispatch the new messages.

## What changed

- Track a durable revision for each thread queue, including backfilling existing queues and updating revisions on inserts, updates, and deletes.
- Poll SQLite's data version and query revisions to find changed queues for loaded threads.
- Wake idle threads with pending external messages, discover queued work when threads are loaded or resumed, and retry each thread independently so one blocked queue does not stall others.

## Testing

- Cover cross-runtime queue writes, edits, independent dispatch, wake retries, and resumed threads.
- Cover migration backfills and revision tracking for queue updates and deletes.

GitOrigin-RevId: 906d902bbf6a760be206a1b2c08fd71427b63c46
This commit is contained in:
Eric Traut authored and copyberry committed 2026-08-17 17:19:46 +00:00
1 parent a8525b139c
commit eeb82a156d
7 files changed
+552 -4

No files matched your search

+22
View File
@@ -12,6 +12,16 @@ use crate::ThreadStoreFuture;
/// Storage-neutral persistence for ordered, thread-scoped user messages.
pub trait QueueStore: Send + Sync {
/// Return a stable revision that changes when another connection updates the queue.
fn change_version(&self) -> ThreadStoreFuture<'_, i64>;
/// Return changed, loaded thread IDs and their durable revisions after `revision`.
fn changes_since<'a>(
&'a self,
revision: i64,
thread_ids: &'a [ThreadId],
) -> ThreadStoreFuture<'a, Vec<(ThreadId, i64)>>;
fn enqueue(
&self,
thread_id: ThreadId,
@@ -72,6 +82,18 @@ where
}
impl QueueStore for LocalQueueStore {
fn change_version(&self) -> ThreadStoreFuture<'_, i64> {
queue_future(self.queue().change_version())
}
fn changes_since<'a>(
&'a self,
revision: i64,
thread_ids: &'a [ThreadId],
) -> ThreadStoreFuture<'a, Vec<(ThreadId, i64)>> {
queue_future(self.queue().changes_since(revision, thread_ids))
}
fn enqueue(
&self,
thread_id: ThreadId,