Repository navigation
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
🔐 Codex Security Review
Review SummaryOverall Risk: NONE
FindingsNo concrete security, correctness, or reliability findings were identified. Notes
Generated by Codex Security Review | |
The delivery worker found new outbox rows only on its idle poll (250ms backing off to 2s), and a claimed batch had to finish before the next claim, so one slow endpoint held up every mention behind it. An event transaction now records the outbox ids it inserts and, once it commits, hands them to this process's delivery worker over a bounded channel. The worker claims exactly those rows with the poll's claim query plus an id filter (same due/lease conditions and SKIP LOCKED), so the fast path and any poll never both claim a row. The worker reads hand-offs only while one of its 10 in-flight slots is free; anything dropped, failed or retried is left to the unchanged idle poll, which now runs on a fixed deadline that hand-offs cannot push back. This avoids pg_notify, whose global commit lock would make every notifying event transaction wait in line while holding its connection and row locks. Local relay, accepted -> listener POST: median 684ms -> 3.0ms. Co-authored-by: mbot <3754f8729004d95654c46dbab3129e4ab9ef05cc2534e2a3fbfc155983bd637b@buzz.block.builderlab.xyz> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Bradley Axen <baxen@squareup.com>
9716743 to
ea9a023
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ea9a023712
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| Claim::Committed(ids) => { | ||
| state | ||
| .db | ||
| .claim_operator_listener_deliveries_by_id(&ids, lease_until) | ||
| .await |
There was a problem hiding this comment.
Filter fast-path claims to listeners routed by this pod
When pods temporarily have different listener route sets, such as during a rolling configuration update, the saving pod claims every committed ID even if another pod is the only one able to route its listener. deliver_one then releases the unroutable claim with next_attempt_at 30 seconds in the future, preventing the correctly configured pod from claiming it and turning the new fast path into a deterministic delay. Restrict committed claims to listener pubkeys routed by this pod, or only hand off locally routable rows.
Useful? React with 👍 / 👎.
| let ids: Vec<Uuid> = query.build_query_scalar().fetch_all(tx.conn()).await?; | ||
| let enqueued = ids.len() as u64; | ||
| tx.record_operator_listener_deliveries(ids); |
There was a problem hiding this comment.
Bound the returned handoff IDs
A mention can match many registered listeners and targets, but fetch_all materializes every inserted UUID and retains all of them in AdmittedTx until commit even though the handoff channel accepts at most 10 before discarding the remainder. Large fan-outs therefore add unbounded request memory and commit latency, potentially exhausting the relay before the intended bounded queue provides protection. Stream/count the returned rows while retaining only a fixed number of IDs for the fast path.
AGENTS.md reference: AGENTS.md:L240-L248
Useful? React with 👍 / 👎.
wesbillman
left a comment
There was a problem hiding this comment.
🤖 Star Lord’s automated source review via Wes’s account, integrating independent Rocket, Mantis, and Groot review lanes.
One P2 latency issue confirmed; no additional actionable defects found. This is a non-blocking COMMENT review.
[P2] Restrict committed claims to listeners this pod can route. I independently confirmed the existing inline finding. During a rolling listener configuration update, one pod can register listener B globally while another saving pod still routes only listener A. The new committed-ID path at crates/buzz-relay/src/operator_listener.rs:89–93 claims B’s row on the saving pod without a route filter. The unchanged no-route branch at lines 325–337 releases it with next_attempt_at = now() + 30s, preventing the correctly configured pod from delivering it until then. The old poll could also win this race; the immediate local handoff makes that delay much more likely for mentions saved on the mismatched pod. Filter the by-ID claim by locally configured listener pubkeys, leaving other rows due for a routable pod. A regression test should show that an unroutable local handoff leaves the row immediately claimable by the routable worker. This is a rollout latency regression; refunded attempts do not remove the existing 15-minute outbox retention limit.
Optional: the returned-ID allocation concern points to a useful fixed-size collector before the channel: fetch_all and the transaction vector retain all inserted IDs even though only ten can be queued. The extra allocation is visible in source; I have not established a realistic exhaustion threshold, so I am not adding a second defect finding on that basis.
The successful-commit boundary, durable poll fallback, shared SQL lease/claim fencing, ten-task delivery limit, and fixed poll deadline otherwise held up under source inspection. The public PR description has no attached screenshots and no actionable disclosure found. Minimalism 9/10, elegance 9/10, correctness 8/10 pending the route filter above.
Reviewed head ea9a023712fa08e610805f6f4a88891aa8c2235e against base tip bbb5183f33df3c6a5f7c587cc41233bc555cd15c; three-dot merge-base 7c1a8967e0d2e12fb8b2ec3ae9783869b4a8be9f (six Rust files). Source only: no tests, builds, application execution, or deployment probes run. One CI snapshot reported 60 successful and 31 skipped check runs; it does not verify runtime behavior. Author latency and mutation-test claims, multi-pod rollout timing, and production performance were not independently reproduced.
Why
Operator-listener deliveries (the push path kgoose uses to wake remote agents) reached the listener 0.25–2s after the mention committed. The delivery worker only found new outbox rows on its idle poll, which backs off from 250ms to 2s. A claimed batch also had to finish completely before the worker claimed again, so one slow endpoint (up to the 5s timeout) held up every mention queued behind it.
What changed
enqueue_mentions_in_transactionnow returns the ids of the outbox rows it inserted, and the event'sAdmittedTxholds them. Aftercommit()succeeds, it hands them to this process's delivery worker over a bounded channel. Rollback hands off nothing. The worker claims exactly those rows withclaim_deliveries_by_id, then uses the existing send/complete/retry path.claim_deliveries_by_idis the poll's query plusAND ($6::uuid[] IS NULL OR o.id = ANY($6)). It has the same due/lease conditions and the sameFOR UPDATE SKIP LOCKED. So a row is claimed by the fast path, by this pod's poll, or by another pod's poll, never by two of them.JoinSet.JoinSetand claims again whenever a slot frees, rather thanjoin_allon a batch of 10.Why not
pg_notifyAn earlier version of this PR woke workers with
pg_notifyfrom inside the event transaction. Jarrod pointed out the cost. Postgres takes one global lock during the commit of every transaction that ranNOTIFY, and holds it through the WAL flush. So every commit that sends a notification goes through a single queue. While a commit waits in that queue, it still holds its pool connection and its row locks, including thethread_metadatacounter rows. A burst of agent mentions would back up behind that lock and slow ordinary ingestion too. The hand-off here never touches Postgres beyond the claim the poll already did. It also doesn't need a dedicatedLISTENconnection, and the transaction-pooler and half-open-socket caveats are gone.The tradeoff: a mention only takes the fast path on the pod that saved it. Every relay pod with
BUZZ_OPERATOR_LISTENERSruns a worker, so in practice that's every pod that saves one. Saves on paths without aDbhandle (raw-pool helpers) fall back to the poll.Measured
Local relay (debug build) against the same database. Each latency runs from
POST /eventsaccepted to the listener receiving the delivery, with 20 mentions spaced 2.5–4s apart so the worker sits at its idle ceiling:The earlier
pg_notifyversion measured 1.9 ms median on the same harness.Tests
buzz-dbPostgres tests:attempts = 1, and leased rows can't be claimed by id again. RemovingSKIP LOCKEDmakes this fail.buzz-relayunit tests drive the productionrun_delivery_loop:🤖 Generated with Claude Code