Build1 distinct publisher3 min readUpdated
A lab writeup on dev.to turns always-on Kafka sinks into on-demand workers. The interesting part is not the zero, it is the ceiling that the partition count puts on your spike response.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
An engineer writing on dev.to has published a measured account of moving a fleet of Kafka sinks from always-on replicas to KEDA-managed scale-to-zero, driven by consumer-group lag rather than CPU [7][8]. The author states every number in the post comes from a local lab that readers can rerun, with the code on GitHub and the commands in an appendix [13]. That matters because the failure mode it documents is the default configuration in most clusters: an HPA watching CPU on an I/O-bound consumer.
The workload shape is the whole argument. These sinks read change events, apply business logic, and write a query-friendly materialized view into a service-local database [1]. Traffic arrives in bursts from nightly imports or CDC jobs, and the topic is quiet the rest of the day, with sinks idle roughly 22 hours out of 24 [2]. That is about 92 percent of the day spent polling Kafka, holding connections, emitting metrics, and occupying CPU and memory for nothing [15][3]. One small sink is cheap; the author's point is that dozens of them across several regions are not [3].
The reflex fix makes the spike worse, not better. Sink work is I/O-bound, so when a backlog builds, CPU stays flat while lag climbs, and an HPA watching CPU concludes everything is fine and never scales [5]. Lag grows silently and downstream reads start serving stale data [4]. Consumer lag is the signal that actually answers the question the autoscaler is being asked, which is whether work is waiting [6].
KEDA supplies that. A plain HPA cannot go from one replica to zero; KEDA manages the HPA and the scale-to-zero around it, and can take Kafka consumer-group lag as the trigger [7]. The published ScaledObject sets minReplicaCount 0, maxReplicaCount 12 to match the partition count, cooldownPeriod 60, lagThreshold 1000, and activationLagThreshold 0 [8]. The last two are the tuning that decides behaviour: activation at zero wakes the workload the instant lag appears, and the cooldown keeps it up until the backlog is fully drained [17]. Replicas then track roughly totalLag divided by lagThreshold, capped at the maximum [9]. Idle, the sink sits at zero pods; a one-million-message burst produced a 0 to 12 to 0 cycle [10].
Now the arithmetic that the config quietly hides. One million messages against a lagThreshold of 1000 asks for 1,000 replicas, and the cap grants 12 [16]. The lag math is not sizing your response during a real burst; the partition count is [8][16]. Above that ceiling the only remaining lever is how fast a single pod drains, which is set by consumer configuration, not by KEDA - a fleet of slow pods still drains slowly [12].
The author is straight about the limits of the run. All 12 pods wrote to one shared database in the lab, which capped aggregate drain rate, so the result is not 12 times one pod; per-pod throughput is a separate measurement [11]. Scaling the write path is deferred to a follow-up [18], and the post's third move, keeping autoscaling from sabotaging itself, is presented as the remaining piece [14].
What to watch: whether the per-pod throughput numbers land with the same lab reproducibility, and whether the write path holds when the shared database is removed as the bottleneck [11][18]. Until then, treat 12 replicas as a partition-bound ceiling and measure one pod before you trust the fleet.
Follow any of these and your For You feed starts watching them — no settings page required.
Ranked by verification strength, evidence, and original report placement.
The author runs a fleet of Kafka sinks: consumer services that read change events from Kafka, apply business logic, and write the result into a service-local database as a query-friendly materialized view.
Traffic is not steady: changes arrive in bursts, usually from nightly imports or CDC jobs, and the topic is quiet the rest of the day; the sinks are idle roughly 22 hours a day.
Sink work is I/O-bound - the consumer waits on Kafka polls and database writes rather than burning CPU - so during a backlog CPU stays flat while lag climbs, and an HPA watching CPU concludes everything is fine and never scales.
The question a sink autoscaler must answer is whether there is work waiting to be processed, and the only signal that answers it directly is Kafka consumer lag.
Measured result: idle, the sink sits at 0 replicas with no pods; the author fired a one-million-message burst and observed a 0 to 12 to 0 replica cycle.
KEDA gives you pods, but a single pod's throughput is set by the consumer configuration; if each pod drains slowly, a fleet of slow pods still drains slowly.
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Concrete and self-consistent, but single-source and self-reported
The source publishes the actual ScaledObject manifest, the KEDA replica formula, the tuned consumer settings and the bulk upsert SQL, and reports before/after numbers from a lab it claims readers can reproduce from GitHub. It also volunteers the limiting caveat (12 pods sharing one database) rather than hiding it, which raises credibility. Against that: one publisher, one author, no independent replication, no repository link or appendix in the captured text, the results table is absent and the body truncates mid-comparison. That supports 'plausible and well-specified' but not 'verified'.
One practitioner deployment plus a lab, no external uptake data
Adoption evidence is confined to this author: a disclosed fleet of Kafka sinks of unstated size and a local lab exercising KEDA's Kafka scaler. There are no third-party deployments, no vendor or community usage figures, no release or pricing events, and no independent benchmarks in the cluster. The pattern is demonstrated as workable, not shown to be widely adopted.
Slightly understated: caveats stated up front
The headline framing ('always-on to on-demand', 0 → 12 → 0) could have been sold as a throughput win, but the author explicitly refuses that reading, states the shared-database cap, separates scaling shape from per-pod throughput, and defers write-path scaling to a follow-up. The main unclaimed implication is that maxReplicaCount pinned to partition count caps a 1M-message backlog's requested ~1,000 replicas at 12 — a roughly 83x haircut that the post notes only in a YAML comment. Claims therefore sit at or just below what the evidence supports.
Practitioner self-publishing; mild promotional pull, no disclosed vendor tie
The piece is a personal technical post on dev.to with no disclosed employer, vendor sponsorship, product or commercial relationship with KEDA or Kafka. The incentives visible in the material are reputational and audience-building: a claimed reproducible repo, an appendix, and an explicit teaser for a follow-up post on scaling the write path. That is enough to expect favourable framing of the author's own pattern, but nothing in the cluster indicates paid or vendor-aligned motivation.
Moderate: mechanism solid, magnitudes single-sourced
Confidence is split. The architectural claims — CPU is the wrong signal for I/O-bound sinks, KEDA can trigger on consumer lag and scale to zero, replicas follow totalLag / lagThreshold up to maxReplicaCount, per-pod drain rate bounds the fleet — are mechanically checkable and internally consistent, so they warrant fairly high confidence. The quantitative claims (records/s figures, drain times, the 22-hour idle duty cycle, the 0 → 12 → 0 cycle) rest on one self-reported lab with a truncated body, no results table and no independent replication, so their magnitudes warrant low confidence. The blend lands mid-scale.
build
Rate limit your MCP servers, because a retrying agent turns one error into a billing incident1 distinct publisher
build
Your meter now runs on someone else's machine: signed receipts, fsync, and failing open1 distinct publisher
build
Three services you can delete: queue, cache and search in one Postgres1 distinct publisher
build
Thirteen tasks green, then "give up (Recommended)" on the one that needed understanding1 distinct publisher
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 15, 2026