We manage shipments across many carriers — DHL, FedEx, Poste Italiane, GLS, dozens of regional posts and last-mile players. To surface status updates to merchants and end customers, we need to know what each carrier knows about a parcel, as soon as they know it. Carriers don't push events to us; we poll their tracking APIs and ask "anything new?".
The easiest way to do that is the obvious one: pick an interval N and check every tracker every N minutes. Want the worst-case detection delay to be 10 minutes? Set N = 10. Want 5? Set N = 5. So the seemingly natural move is to keep lowering N until the detection delay is small enough — and we're done.
We're not done. Lowering N costs in two directions that tighten as it shrinks. Internally, every poll travels through the tracking & tracing pipeline — a queued message, a database read, a downstream notification. Halving N doubles the load on every step of that path. Externally, every carrier API has rate limits, and the smaller N gets the more often we trip them; a few seconds of throttling on DHL during peak hours can erase the freshness window we were trying to buy. So N is the wrong knob: it's a single global lever that simultaneously controls cost and freshness for parcels that don't share the same event rhythm. A parcel sitting at customs and a parcel in active out-for-delivery don't deserve the same cadence, and no choice of N makes both right.
This is structurally the same problem that search-engine crawlers have been solving since the late 1990s — when do I look again at something that changes asynchronously — and the classical IR result (Cho & Garcia-Molina) is to model each page's change rate as a Poisson process and allocate fetches sub-linearly in that rate. Great framing when you have a fixed crawl budget. We don't: we have a soft cost constraint and a hard worst-case latency requirement, plus visibly non-Poisson event distributions with strong hour-of-day seasonality. So we took a non-parametric path instead.
Two KPIs guide everything that follows. Detection delay — the wall-clock gap between a carrier emitting an event and us discovering it — is the user-visible quality metric; if a parcel is delivered and our app says "in transit" for two hours, the merchant notices. Polls per tracker per day is the cost metric — it drives our T&T pipeline load and our carrier rate-limit headroom. The whole point of the project is to lower polls per day without raising detection delay, by spending the polls more carefully than a uniform N allows.
The obvious modelling move would be to train a regressor — gradient-boosted trees or an LSTM over the per-tracker sequence — and predict the next event time. We didn't. A trained model is a long-term commitment (training pipeline, monitoring, drift detection, retraining), we don't actually need a point prediction, and even if we did the domain is noisy enough across carriers and statuses that the uncertainty bars would swallow it. So we work with the historical distribution directly. For each (carrier, tracking_status, hour-of-day) cohort, we compute the empirical CDF of inter-event gaps over the last 90 days. The ECDF answers "given the last event just arrived, what's the probability the next arrives within t hours?" — exactly the oracle we want. The scheduler picks a probability threshold, looks up the time, checks; if nothing happened, raises the threshold and tries again.
The system has three stages. A weekly Glue job reads 90 days of tracking events from S3, computes per-cohort ECDFs of inter-event gaps, and writes a single JSON artifact to S3. An SSM parameter holds the path to the current production artifact. Every time a tracker needs scheduling, a Lambda reads the artifact (hot-cached after the first call), looks up the right cohort, and computes the next check timestamp.
The first question was: at what granularity? A single global ECDF is smooth but lumps fast last-mile carriers in with slow international posts; per-tracker would be perfect personalization but completely sample-starved. We simulated three intermediate granularities on the same 90-day window — per carrier, per (carrier, status), per (carrier, status, hour-of-day) — and each finer step captured meaningful variance the previous level missed (hour-of-day picks up real diurnal patterns: carriers process at very different rates at 3am vs 2pm). Beyond the third level, cells went sparse faster than they added signal; that was the sweet spot.
To keep the long tail honest, the artifact stores all four levels — carrier_status_hour, carrier_status, carrier, and global — and the lookup falls back through them in order. New carrier falls straight through to global; a known carrier with a novel status uses the carrier-level row. The same cascade smoothes onboarding: a freshly added carrier starts on the global ECDF and migrates to its own row as soon as the next weekly job has enough samples.
We picked this overall design because ECDFs have two properties we needed at the same time: they're distribution-preserving (the scheduler can walk any percentile, not just the mean), and they're interpretable and auditable (a system that says "we're polling DHL in_transit at 2pm every 47 minutes" is much easier to debug than one that says "the network predicts 47 minutes"). The whole machinery is non-parametric — we don't fit anything, we just count.
The first version was simple. Pick a short list of probability checkpoints — say [10%, 30%, 50%, 80%] — and walk through them. Check at the 10% mark; if nothing's there, advance to 30%, then 50%, then 80%. Multiple checkpoints because we never want to miss an update — one check would be too coarse, every minute would be too wasteful. Easy win at 10% traffic, almost shipped — until we started inspecting individual cohorts and found that the same fixed checkpoint list produced very different schedules depending on the shape of the ECDF.
The fixed checkpoint list says nothing about time — only probability mass — and on degenerate shapes that breaks down. A shared ladder is indexed by probability mass; what actually matters is expected detection delay against each cohort's own CDF — a per-cell quantity. So we replaced the shared ladder with a per-cell ladder picked at training time by a small dynamic program over each cell's percentile array. For every (carrier, status, hour) cell, the DP picks K = 15 indices i₁ < i₂ < … < i₁₅ that minimise the total expected detection delay against that cell's actual CDF:
99∑e=0 (P[next(e)] − P[e])
where next(e) is the smallest chosen index ≥ e (the first scheduler checkpoint that catches an event arriving at percentile e). The last index is forced to P99 so the tail is always covered. Steep-tail cohorts end up with checkpoints concentrated in the tail; flat-early cohorts get them spread further apart in probability mass so adjacent checks don't collapse in wall-clock time.
We ran the scheduler as an A/B test for three months. A hash of the order ID assigns each parcel to either the smart cohort or a fixed-interval baseline; that fraction was ramped from 10% to 90% over April and May as confidence grew, and on top of that the DP-optimal per-cell ladder rolled out over the last week of May.
The headline is in the green lines: the smart cohort polls roughly half what control polls (mean ≈ 3–5 polls per tracker per day vs control's 6–9), and the gap stays stable as smart's share of traffic grows from 10% to 90% — the policy is consistent across populations. Detection delay (orange) tracks closely between the two cohorts, confirming that halving the polling load does not meaningfully hurt the user-visible quality metric. The DP-optimal step indices reaching 100% on June 1 are expected to push the per-tracker poll rate further down; the post-rollout window is just beginning to land.
We replaced a fixed-interval poller with a per-cohort ECDF-driven scheduler and roughly halved the polling load per tracker without meaningfully hurting detection delay — we never trained a model at all. The scheduler is memoryless across trackers — every decision looks at the cohort's historical distribution, not at the individual tracker's own event sequence. A parcel that has emitted three slow in_transit events in a row is treated identically to one that's just entered the same status. There's signal in the per-tracker sequence that the cohort-level ECDF can't capture by construction. That's where we're going next: replace the cohort lookup with a per-tracker model that conditions on the order's own recent gap history — so the ECDF becomes the prior and the sequence model becomes the posterior, keeping the interpretability and the safety cap while raising the ceiling on what each tracker's schedule can be.