Skip to content

S3C-11127: add the notification delivery worker and its producer pool - #2800

Draft
anurag4DSB wants to merge 6 commits into
development/9.3from
poc/S3C-11127-w2-worker
Draft

S3C-11127: add the notification delivery worker and its producer pool#2800
anurag4DSB wants to merge 6 commits into
development/9.3from
poc/S3C-11127-w2-worker

Conversation

@anurag4DSB

@anurag4DSB anurag4DSB commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Intent: why does this change exist?

The delivery pool needs something to read the addressed records and dispatch them, so that one shared consumer group serves every destination instead of the current one group per destination.

System impact: what's affected, including downstream?

Everything is new and lives under extensions/notification/deliveryWorker/, plus two package.json scripts. Nothing invokes it unless it is deployed, and it only has work to do once the populator's delivery pool flag is on. The notification_delivery_replay script points at the replay tool from its own PR, so it is inert until that lands. The existing notification queue processor is not touched.

Preserved behavior: what explicitly stays the same?

Additive only. The per-destination consumer path, the destination config shape and the message format the customer receives are all unchanged.

Intended change: what's different after this PR?

Adds a worker that consumes the shared delivery topic under a single consumer group, keeps a pool of producers keyed per destination, and holds the consumer offset until each send reaches a terminal outcome, so an offset never advances past a record still in flight. Terminal means either delivered or dropped: a send that fails or exceeds deliveryTimeoutMs is counted in dropped_total with a reason and the offset then advances, so a stalled destination costs bounded, counted loss rather than an unbounded backlog. There is no retry or dead letter path here. Each worker honours a probe port handed to it by the deployment, since one host runs several of them.

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

Unit tests cover the worker and the producer pool: the offset is held until the delivery report, each drop reason is counted, ordering is derived per destination and object with the payload parsed once, and for the pool, single-flight creation, idle reaping that spares in-flight producers, capacity eviction and the message timeout reaching the producer topic config. 32 tests in the two new spec files, 192 passing across tests/unit/notification, eslint clean. This PR itself ships no functional tests; that suite lands separately (#2802), where this worker was driven against a live broker, including an end to end run from the real populator through this worker to the destination topics with correct per-destination routing.

The delivery worker consumes a topic shared by every destination, so it
needs a producer per destination created on demand rather than the single
producer of the per-destination queue processor.

DeliveryKafkaProducer bounds how long librdkafka retries a message, since
the worker holds the consumer offset until the delivery report arrives and
an unbounded retry would block that offset forever.

DeliveryProducerPool creates producers on demand, serves concurrent
requests made during connect from a single producer, and closes producers
once idle. A producer with deliveries in flight is never closed: its
delivery reports still release consumer offsets.
One worker serves every destination: the destination id and the
notification configuration id ride in each record, so no bucket
notification configuration lookup and no mongo or zookeeper connection is
needed here.

Two details are load bearing. The consumer callback is held until the
delivery report arrives, so an offset is only committed once the
notification has left the process, and a delivery failure is counted and
dropped rather than passed back as an error, which the consumer would
raise as a consumer level error. Ordering is by destination and object
instead of the default kafka key, which would serialize a whole
destination behind one lane.

The consumer reads from the earliest offset: librdkafka defaults to
latest, and a worker joining with a fresh group would skip everything
already sitting in the topic.
Covers the commit timing (the callback waits for the delivery report), the
drop reasons and their counters, the ordering key and the parse-once
stash, and for the pool the single producer per destination during
connect, idle reaping that spares busy producers, capacity eviction, and
the message timeout landing in the producer topic config.
Several workers can run from a single rendered config file, on one host or
inside one container, and only one process can bind a given port, so the
DELIVERY_POOL_PROBE_PORT environment variable now wins over the configured
port and the deployment can hand each worker process its own.

A probe server that fails to bind no longer takes the worker down either.
A worker that cannot serve its probe routes still delivers notifications,
and dying instead would turn a port clash into a crash loop.
Aligns the environment variable with the name the federation supervisord
template exports per worker program. Behaviour is unchanged: a valid
integer replaces the configured probe server port and the rest of the
probe server config is kept, anything else falls back to the configured
port with a warning.
Restores the environment variable name the federation supervisord template
exports and the contract pins. The resolution itself is unchanged: a valid
integer replaces the configured probe server port and the rest of the probe
server config is kept, anything else falls back to the configured port,
with a warning when the value was non-empty.
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.63844% with 84 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.44%. Comparing base (d5ad38f) to head (5403bde).

Files with missing lines Patch % Lines
extensions/notification/deliveryWorker/task.js 0.00% 42 Missing ⚠️
...ions/notification/deliveryWorker/DeliveryWorker.js 64.42% 37 Missing ⚠️
...otification/deliveryWorker/DeliveryProducerPool.js 97.01% 4 Missing ⚠️
...tification/deliveryWorker/DeliveryKafkaProducer.js 90.90% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

Files with missing lines Coverage Δ
...ensions/notification/deliveryWorker/probeConfig.js 100.00% <100.00%> (ø)
...tification/deliveryWorker/DeliveryKafkaProducer.js 90.90% <90.90%> (ø)
...otification/deliveryWorker/DeliveryProducerPool.js 97.01% <97.01%> (ø)
...ions/notification/deliveryWorker/DeliveryWorker.js 64.42% <64.42%> (ø)
extensions/notification/deliveryWorker/task.js 0.00% <0.00%> (ø)

... and 3 files with indirect coverage changes

Components Coverage Δ
Bucket Notification 78.14% <72.63%> (-2.14%) ⬇️
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    #2800      +/-   ##
===================================================
- Coverage            74.70%   74.44%   -0.27%     
===================================================
  Files                  199      204       +5     
  Lines                13629    13936     +307     
===================================================
+ Hits                 10182    10374     +192     
- Misses                3437     3552     +115     
  Partials                10       10              
Flag Coverage Δ
api:retry 8.95% <0.00%> (-0.21%) ⬇️
api:routes 8.77% <0.00%> (-0.21%) ⬇️
bucket-scanner 85.76% <ø> (ø)
ft_test:queuepopulator 8.87% <0.00%> (-1.48%) ⬇️
ingestion 12.27% <0.00%> (-0.29%) ⬇️
lib 7.51% <0.00%> (-0.17%) ⬇️
lifecycle 18.44% <0.00%> (-0.42%) ⬇️
notification 1.02% <0.00%> (-0.03%) ⬇️
oplogPopulator 0.13% <0.00%> (-0.01%) ⬇️
replication 18.09% <0.00%> (-0.42%) ⬇️
unit 51.63% <72.63%> (+0.48%) ⬆️

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.

Comment thread package.json
"notification_processor": "node extensions/notification/queueProcessor/task.js",
"notification_delivery_worker": "node extensions/notification/deliveryWorker/task.js",
"notification_delivery_replay": "node bin/notificationDeliveryReplay.js"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script references bin/notificationDeliveryReplay.js which does not exist in the repo or in this PR. Running npm run notification_delivery_replay will crash with MODULE_NOT_FOUND.

If the replay entry point is coming in a follow-up PR, remove this line until then to avoid a broken script in the shipped package.json.

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