feat(queue): dead-letter store for group shared queues (§11.6 D3 / Track A M2)
An exhausted SHARED durable-queue message was `drop_exhausted()`-ed with a
warning — silent data loss. Per-app queues persist to `dead_letters` (0010);
add the symmetric group store so an exhausted shared-queue message is preserved
and operator-visible.
- Migration 0068: `group_dead_letters`, keyed by (group_id, collection),
CASCADE on the group (an app delete leaves the data — it belongs to the
group, not the consuming app).
- `GroupQueueRepo::dead_letter` (replaces `drop_exhausted`): one tx that INSERTs
the dead-letter + DELETEs the live message, filtered by claim_token so a lost
lease can't dead-letter a re-claimed message (mirrors queue_repo::dead_letter).
- Dispatcher `q_terminal` shared arm now dead-letters instead of dropping. It
returns None (not the dl id) so the per-app `fan_out_dead_letter` is SKIPPED —
firing the consuming app's per-app handlers on a shared message (competing
consumers → nondeterministic app) would be wrong. Fan-out to a *shared*
dead_letter trigger is deferred (needs a new trigger kind).
- Read side: `GroupDeadLetterRepo::list_for_group` backs a new read-only
operator endpoint GET /api/v1/admin/groups/{id}/dead-letters (GroupKvRead,
mirrors the M4 group-blobs surface). `pic dead-letters ls --group` deferred
(optional; the HTTP endpoint is the operator surface).
Pinned by group_queue::dead_letter_moves_an_exhausted_message_to_the_group_store
(store move + claim-token-mismatch guard + operator read). Schema golden
reblessed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -273,6 +273,11 @@ pub async fn build_app(
|
||||
));
|
||||
// §11.6 D3: the group shared-queue store + enqueue service.
|
||||
let group_queue_repo = Arc::new(PostgresGroupQueueRepo::new(pool.clone()));
|
||||
let group_dead_letter_repo = Arc::new(
|
||||
picloud_manager_core::group_dead_letter_repo::PostgresGroupDeadLetterRepo::new(
|
||||
pool.clone(),
|
||||
),
|
||||
);
|
||||
let group_queue: Arc<dyn picloud_shared::GroupQueueService> =
|
||||
Arc::new(GroupQueueServiceImpl::new(
|
||||
group_queue_repo.clone(),
|
||||
@@ -722,6 +727,7 @@ pub async fn build_app(
|
||||
kv: group_kv_repo.clone(),
|
||||
docs: group_docs_repo.clone(),
|
||||
files: group_files_repo.clone(),
|
||||
dead_letters: group_dead_letter_repo.clone(),
|
||||
groups: groups_repo.clone(),
|
||||
authz: authz.clone(),
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user