Shedding Spatial Load by Geographic Priority
Classify streams by whether a later event supersedes an earlier one, shed only within that set, and rank by a configured area priority rather than by event density — prioritising by traffic volume encodes “busy” as “important” and degrades sparse regions invisibly, because they generate too few events to move a fleet-wide metric.
This guide sits under Backpressure & Flow Control for Spatial Consumers, within Queue Management, Retries & Delivery Guarantees. It is the escalation after pausing, and it should be rare enough that its rate is itself an alert.
When to use this pattern
- Pausing has been sustained long enough that the backlog will not clear on its own, which is the precondition — shedding before pausing is throwing away work you had capacity for.
- At least one stream in the mix is genuinely self-superseding, or there is nothing safe to shed.
- The alternative is worse: a backlog that grows until the consumer is evicted, taking every stream with it.
Supersession first, geography second
Complete runnable implementation
import time
from dataclasses import dataclass
from enum import IntEnum
import h3
from prometheus_client import Counter
SHED = Counter("events_shed_total", "Events deliberately dropped",
("stream", "area_class", "reason"))
class Supersession(IntEnum):
"""Whether a later event makes this one redundant."""
SELF_SUPERSEDING = 0 # another arrives shortly and is strictly better
NEVER = 1 # nothing will resend this
# Explicit per stream. A stream absent from this table is NEVER, because the
# safe default for "somebody added a topic and forgot" is to keep the data.
STREAM_POLICY: dict[str, Supersession] = {
"vehicle.position": Supersession.SELF_SUPERSEDING,
"sensor.reading": Supersession.SELF_SUPERSEDING,
"tile.render": Supersession.SELF_SUPERSEDING,
"feature.boundary_edit": Supersession.NEVER,
"geofence.transition": Supersession.NEVER,
"dlq.replay": Supersession.NEVER,
}
# Area priority from configuration — a business decision, not event density.
# Lower rank sheds first.
AREA_PRIORITY: dict[str, int] = {} # H3 res-4 cell -> rank
DEFAULT_AREA_RANK = 50
# A ping already older than this is worthless: its successor has arrived.
STALE_AFTER_SECONDS = 30.0
@dataclass(frozen=True, slots=True)
class ShedDecision:
shed: bool
reason: str
def area_class(lat: float, lon: float) -> tuple[str, int]:
cell = h3.latlng_to_cell(lat, lon, 4)
return cell, AREA_PRIORITY.get(cell, DEFAULT_AREA_RANK)
def decide(stream: str, lat: float, lon: float, occurred_at: float,
pressure: float, now: float | None = None) -> ShedDecision:
"""Shed or keep one event. `pressure` is 0.0-1.0 from the work budget."""
now = now or time.time()
policy = STREAM_POLICY.get(stream, Supersession.NEVER)
# Filter 1. A stream whose events do not supersede is never shed, at any
# pressure — the alternative to keeping it is permanent loss.
if policy is Supersession.NEVER:
return ShedDecision(False, "not-superseding")
cell, rank = area_class(lat, lon)
# A superseded event is free to drop regardless of pressure: its
# replacement has already arrived, so processing it is pure waste.
if now - occurred_at > STALE_AFTER_SECONDS:
SHED.labels(stream=stream, area_class=cell, reason="stale").inc()
return ShedDecision(True, "stale")
# Filter 2. Under pressure, shed from the lowest-priority areas first.
# Pressure 0.8 sheds rank > 80; pressure 1.0 sheds everything sheddable.
if pressure > 0.5 and rank > (1.0 - pressure) * 100:
SHED.labels(stream=stream, area_class=cell, reason="pressure").inc()
return ShedDecision(True, "pressure")
return ShedDecision(False, "kept")
The default of NEVER for an unknown stream is the decision that makes this safe to operate. A topic added by another team inherits the conservative policy rather than the convenient one, and the cost of that mistake is a slower consumer instead of missing cadastral edits.
Parameter reference
| Name | Type | Spatial constraint | Default |
|---|---|---|---|
STREAM_POLICY |
dict | Explicit per stream; unknown streams default to NEVER |
— |
AREA_PRIORITY |
dict | H3 res-4 cell to rank, from configuration not density | {} |
DEFAULT_AREA_RANK |
int |
Applied to unconfigured areas; mid-range, not lowest | 50 |
STALE_AFTER_SECONDS |
float |
Above the stream’s report interval, so only superseded events qualify | 30.0 |
pressure |
float |
From the work budget, 0.0–1.0 | — |
| Shed counter labels | — | Stream, area and reason — shedding must be countable | — |
Gotchas and spatial edge cases
-
An unknown stream must default to never-shed. The convenient default is the dangerous one: a topic added by another team would silently become sheddable, and the first anyone knows is a gap in a dataset nobody was watching. The cost of the safe default is a consumer that sheds less than it could.
-
Area rank at resolution 4 is coarse, deliberately. A configuration file listing millions of cells is not maintainable, and priority is a regional decision rather than a neighbourhood one. Cells at resolution 4 are roughly the size of a metropolitan area, which matches how the decision is actually made.
-
Staleness is not the same as pressure shedding, and both need separate counters. A stale event is free to drop at any pressure because its successor has already arrived; a pressure shed is a deliberate sacrifice. Merging them into one counter makes it impossible to tell a healthy pipeline discarding superseded pings from one in distress.
-
Shedding interacts with deduplication. An event shed at the consumer may already have claimed its deduplication key if the claim happens before dispatch, so the retry finds the key present and suppresses an event that was never processed. Shed before claiming, or release the key on shed — the same ordering problem described in Time-Windowed Deduplication for Moving Assets.
-
The shed rate belongs in the error budget, not beside it. Shedding is deliberate incompleteness, so it spends the completeness objective defined in SLOs & Alerting for Spatial Webhook Pipelines. A pipeline that sheds routinely without that showing up in an objective has hidden a capacity problem in a feature.
-
Sustained shedding is a capacity decision, not an operational one. If pressure sits above the shed threshold for hours every day, the correct response is more consumers or a finer partitioning, not a lower threshold. Alert on the duration of shedding rather than only on its occurrence.
Verification
import time
import pytest
NOW = 1_780_000_000.0
BERLIN = (52.5200, 13.4049)
def test_boundary_edits_are_never_shed_at_any_pressure():
"""The property the first filter exists to guarantee."""
for pressure in (0.0, 0.5, 0.9, 1.0):
decision = decide("feature.boundary_edit", *BERLIN,
occurred_at=NOW, pressure=pressure, now=NOW)
assert decision.shed is False
def test_unknown_stream_defaults_to_never_shed():
"""A topic somebody forgot to classify must keep its data."""
decision = decide("some.new.topic", *BERLIN,
occurred_at=NOW, pressure=1.0, now=NOW)
assert decision.shed is False
def test_stale_pings_are_shed_even_at_zero_pressure():
"""Its successor already arrived; processing it is pure waste."""
decision = decide("vehicle.position", *BERLIN,
occurred_at=NOW - 120, pressure=0.0, now=NOW)
assert decision.shed and decision.reason == "stale"
def test_priority_areas_survive_longer_than_default_ones():
AREA_PRIORITY[h3.latlng_to_cell(*BERLIN, 4)] = 5
priority = decide("vehicle.position", *BERLIN,
occurred_at=NOW, pressure=0.75, now=NOW)
default = decide("vehicle.position", 48.1372, 11.5756,
occurred_at=NOW, pressure=0.75, now=NOW)
assert not priority.shed and default.shed
def test_nothing_is_shed_below_the_pressure_floor():
"""Shedding work you have capacity for is loss, not management."""
decision = decide("vehicle.position", *BERLIN,
occurred_at=NOW, pressure=0.4, now=NOW)
assert decision.shed is False
The second test is the one worth keeping visible in review. It asserts a default rather than a behaviour, and defaults are what get changed by someone tidying a configuration table who does not know that the conservative value was the point.
Related
- Backpressure & Flow Control for Spatial Consumers — the topic this guide belongs to
- Applying Backpressure When a Spatial Consumer Falls Behind — the step that must be exhausted before shedding starts
- SLOs & Alerting for Spatial Webhook Pipelines — where the shed rate spends the completeness budget
- Dead-Letter Queues for Spatial Events — the alternative to shedding for events that cannot be lost