Pressure as a Shared Vocabulary
Pressure as a Shared Vocabulary
Platform repos tend to grow domain-specific solutions for the same cross-cutting problem. Capacity management is a good example — qhorus needs to know when an actor is overloaded, engine needs to know when to stop routing, and the work module needs to know when to redistribute. Three consumers, three potential implementations, zero shared vocabulary.
The capacity signal SPI addresses this by putting the shared types in platform-api and the default behaviour in platform/. The architecture has three layers, and it’s worth walking through each one because the pattern is reusable far beyond capacity management.
The signal layer
Every capacity-aware subsystem implements CapacitySignalSource and reports normalised pressure for the actors it tracks. The key word is normalised — every source reduces its internal complexity (queue depth, response time, channel load) to a single 0.0–1.0 scalar. This is a deliberate loss of dimensionality. The aggregator doesn’t need to know what causes pressure, only how much.
The aggregator takes max() across all sources for each actor. Max is the right default — the most-stressed subsystem sets the floor for capacity decisions. If the work queue is at 0.72 but the channel load is at 0.91, the actor’s effective capacity is 0.91 — the channel is the bottleneck regardless of what the work queue thinks.
The policy layer
The RedistributionPolicy evaluates aggregated pressure against three thresholds. The default policy is deliberately simple: compress at 0.7, redistribute at 0.85, escalate at 0.95. All three are MicroProfile Config properties — operators tune them without code.
What makes the three-tier model useful is that each tier maps to a different operational response. COMPRESS means “defer non-urgent work but don’t move anything” — the actor handles it by slowing down. REDISTRIBUTE means “move work to someone else” — the system acts. ESCALATE means “a human needs to look at this” — automated responses are exhausted. Consumers observe CapacityPressureEvent CDI events and act on the tier.
The scheduling boundary
The spec originally had @Scheduled on the monitor, but platform/ doesn’t have quarkus-scheduler — it’s a foundational module with only quarkus-arc. The monitor exposes a sweep() method instead. Consumer apps that have the scheduler wire the trigger.
This is the right boundary: the platform defines what capacity means and how to evaluate it; the consumer decides when and how often to check. A qhorus deployment checking every 10 seconds has different needs than an engine checking every minute — the platform shouldn’t dictate either.
The concurrency detail
The review caught one issue worth noting. The original refresh() used clear() then putAll() on a ConcurrentHashMap — correct for single-threaded access but creates a window where concurrent readers see an empty cache. The fix was a volatile reference swap: build the new map completely, then assign it atomically. Readers always see either the old complete snapshot or the new one, never an empty intermediate.