Skip to content

Active-window batcher residency is unbounded and escalation multiplies it #1275

Description

@SimonHeybrock

#1271 bounds the rate-aware batcher's retained gated overflow (data time + bytes). That was the unbounded term behind the TBL OOM (#378), but it is the smaller half of batcher memory at high rates: nothing bounds the active window — the per-stream buckets holding one batch length of in-flight traffic.

At DREAM's specified ceiling of 1e8 events/s (~763 MB/s across banks), peak residency in healthy, keeping-up operation:

batch length active window overflow (bounded by #1271)
1.0 s ~545 MB ≤ 512 MB, typically ~0
8.0 s ~5450 MB ≤ 512 MB, typically ~0

Residency scales linearly with batch length, and AdaptiveMessageBatcher escalates the batch length because processing is slow — so the footprint grows 8x (base → ceiling of the escalation grid) exactly when the service is already struggling. The overflow bound cannot help: this data is inside the window the batcher is legitimately assembling, not backlog.

The same mismatch exists one stage upstream: BackgroundMessageSource.max_queue_size bounds a count of consume batches, while payloads span four orders of magnitude (~8 kB monitor counts to 33.5 MB ad00 frames), so the queue's footprint varies by the same factor (noted in its docstring since #1271).

Candidate directions, each with real trade-offs — this needs a design decision, not a quick patch:

  • Cap AdaptiveMessageBatcher.max_level against a memory budget instead of the fixed 3, so escalation cannot multiply residency past what the host affords.
  • Bound total batcher residency (window + overflow) rather than only the overflow, shedding within the window under pressure.
  • Shed upstream at the consumer, in bytes rather than batch count, so oversized traffic never reaches the batcher.

Interacts with the freshness policy established in #1271 (core/rate_aware_batcher.py module docstring): any bound here should shed stale-first and stay structurally selective (gated stream kinds only), and with the escalation policy, whose whole purpose is to trade latency for throughput — a memory cap on escalation re-introduces the overload it was escaping, so the failure mode it picks must be chosen deliberately.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:backendServices, Kafka, message pipeline, preprocessors, job handlingdesignOpen architecture question, no agreed solution yetperformanceLatency, throughput, CPU or memory cost

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions