Skip to content

S3C-11127: add the notification delivery replay tool - #2801

Draft
anurag4DSB wants to merge 3 commits into
development/9.3from
poc/S3C-11127-w3-replay
Draft

S3C-11127: add the notification delivery replay tool#2801
anurag4DSB wants to merge 3 commits into
development/9.3from
poc/S3C-11127-w3-replay

Conversation

@anurag4DSB

@anurag4DSB anurag4DSB commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Intent: why does this change exist?

An existing deployment has records sitting on the old per-destination internal topics. Turning the delivery pool on needs a way to move that backlog across without losing any of it.

System impact: what's affected, including downstream?

A standalone entrypoint plus a drainer, run on demand during cutover with the populator and the queue processors stopped, never as part of the service. The drain consumes under a throwaway group, while a short-lived client configured with each old group id reads that group's committed offsets to find the start point: it only ever calls committed(), so it never joins the group or triggers a rebalance, and both clients run with auto-commit and auto-offset-store disabled. No offset is written anywhere, which is what keeps a rollback to the old pipeline possible.

Note for review: extensions/notification/utils/deliveryKey.js is a deliberately identical copy of the file added by the populator PR, so that both halves address records the same way. The blobs are byte-identical, which is what lets the two branches merge without conflict.

Note for review: this branch does not add deliveryPool or spreadFactor to the notification config schema, the populator PR (#2799) does. Config validation rejects unknown keys, so until that one merges, a config carrying a deliveryPool block will not load and this tool has nothing to point at. Merge order matters.

Preserved behavior: what explicitly stays the same?

Nothing in the running pipeline changes. The tool is opt-in, and with respect to existing consumer group offsets it is strictly read-only.

Intended change: what's different after this PR?

Adds a replay entrypoint that re-publishes records from the old internal topics onto the delivery topic, addressed exactly as the populator addresses them, so the delivery workers cannot tell a replayed record from a live one. Old records carry no configurationId, so each one is rematched against the bucket's current notification configuration to recover it, which means a configuration removed since the original event drops the record as unmatched. Rerunning is safe and produces duplicates rather than gaps: any error aborts the drain instead of stepping over a record.

Verification: how do we know this worked, or how would we know if it didn't?

Unit tests cover the drainer: offset math at the edges (no committed offset, a committed offset dropped by retention, a partition the old group already finished), the rematch and skip paths, key encoding, and the batch acking that gates the next read. The key helper has its own tests in the populator PR and is exercised here through the drainer's key assertions rather than a second copy of that spec. The two copies of the helper were confirmed byte-identical by hash rather than by eye.

The delivery topic is keyed by destination, optionally spread over
several keys, so that records of one object always land on the same
partition. The populator and the cutover replay both build the key,
hence the shared helper.
At cutover the old per-destination queue processors leave records
behind in the internal topics. DeliveryTopicDrainer replays them as
addressed records so the new delivery worker pool starts with nothing
lost: it reads each old group's committed offsets, consumes from there
up to the head captured at start, rematches every record against the
bucket configuration to recover its configurationId, and produces it
keyed like the populator would.

It never writes a consumer group offset, so a failed run can be rerun
and a rollback to the old pipeline still resumes where it stopped.
Duplicates are the accepted cost of that: any error aborts the drain
rather than stepping over a record.
Thin wrapper around DeliveryTopicDrainer, run once during cutover with
the populator and the queue processors stopped. Exits 0 on a full
drain, non-zero otherwise, so the cutover can gate on it.
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 69.86301% with 88 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.38%. Comparing base (d5ad38f) to head (2f0cf61).

Files with missing lines Patch % Lines
...otification/deliveryWorker/DeliveryTopicDrainer.js 75.19% 65 Missing ⚠️
bin/notificationDeliveryReplay.js 0.00% 23 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
extensions/notification/utils/deliveryKey.js 100.00% <100.00%> (ø)
bin/notificationDeliveryReplay.js 0.00% <0.00%> (ø)
...otification/deliveryWorker/DeliveryTopicDrainer.js 75.19% <75.19%> (ø)

... and 3 files with indirect coverage changes

Components Coverage Δ
Bucket Notification 79.15% <75.83%> (-1.13%) ⬇️
Core Library 80.60% <ø> (-0.56%) ⬇️
Ingestion 70.63% <ø> (ø)
Lifecycle 78.96% <ø> (ø)
Oplog Populator 85.83% <ø> (ø)
Replication 59.70% <ø> (ø)
Bucket Scanner 85.76% <ø> (ø)
@@                 Coverage Diff                 @@
##           development/9.3    #2801      +/-   ##
===================================================
- Coverage            74.70%   74.38%   -0.33%     
===================================================
  Files                  199      202       +3     
  Lines                13629    13921     +292     
===================================================
+ Hits                 10182    10355     +173     
- Misses                3437     3556     +119     
  Partials                10       10              
Flag Coverage Δ
api:retry 8.96% <0.00%> (-0.20%) ⬇️
api:routes 8.78% <0.00%> (-0.20%) ⬇️
bucket-scanner 85.76% <ø> (ø)
ft_test:queuepopulator 8.88% <0.00%> (-1.47%) ⬇️
ingestion 12.29% <0.00%> (-0.27%) ⬇️
lib 7.52% <0.00%> (-0.16%) ⬇️
lifecycle 18.46% <0.00%> (-0.40%) ⬇️
notification 1.02% <0.00%> (-0.03%) ⬇️
oplogPopulator 0.13% <0.00%> (-0.01%) ⬇️
replication 18.11% <0.00%> (-0.40%) ⬇️
unit 51.55% <69.86%> (+0.40%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant