feat(hybridcloud): Tag delivery outcomes and latency with their dispatcher and mode - #121982
Merged
Conversation
The claim_dispatch_rollout selects whole integrations by hash, so at a low percentage its effect on any global total is far smaller than that total's own variance. Races run ~321k/2h with a per-bucket standard deviation of ~60%; a 1% cohort moves them by ~13 per bucket, which is ~60x below one standard deviation. Reading the rollout from global totals needs ~12% of traffic over 2h, or ~3.4% over a day, before the signal clears noise. Segmenting by cohort removes that constraint: comparing the claim cohort's outcome rates against the lease cohort's is valid at any rollout percentage, because it is a rate comparison rather than a shift in a sum. `delivery` was tagged `outcome` and `provider` only. Once a drain task starts it has no idea which dispatcher enqueued it or under which regime, so neither segmentation was possible. Both drain tasks now accept `dispatcher` and `mode` and carry them onto every delivery outcome they record. This also replaces the derived push-vs-scheduler item split on the dashboard, which had to infer push volume by subtracting scheduler claims from the total. Tasks already queued when this deploys arrive without the arguments and tag `unknown` rather than omitting the keys, since a tag absent from some series breaks grouping instead of showing a gap. Requires the `dispatcher` and `mode` tags to be added to the delivery metric's Datadog tag allowlist before this deploys; it is an allowlist, so unlisted tags are dropped at intake and the change is not retroactive. No behavior change: no query, delivery decision, or claim bound is altered.
The outcome counter distinguishes the claim and lease cohorts, but latency is the quantity the claim regime is meant to move, and it was only readable in aggregate. A global p50 cannot show a cohort effect while the rollout is a small share of traffic. `delivery_time_ms` is a native Datadog distribution rather than the usual histogram, so percentiles are queryable per tag combination.
arslnb
approved these changes
Aug 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tags
deliver_webhooks.deliveryanddeliver_webhooks.delivery_time_mswith the dispatcher and dispatch regime of the drain that produced each one.Why
The
claim_dispatch_rolloutselects whole integrations by hash (md5(provider:integration_id)), so at a low percentage its effect on any global total is far smaller than that total's own variance. Measured over 2h:That is roughly 60x below one standard deviation. Reading the rollout from global totals needs ~12% of traffic over a 2h window, or ~3.4% over a day, before the signal clears noise — and that estimate is optimistic, since it treats buckets as independent when traffic is autocorrelated.
Segmenting by cohort removes the constraint. Comparing the claim cohort's rates against the lease cohort's is a rate comparison rather than a shift in a sum, so it stays valid at any rollout percentage, including 1%.
deliverywas taggedoutcomeandprovideronly, anddelivery_time_msregion_sent_to,providerandgithub_event_and_action. Once a drain task starts it has no idea which dispatcher enqueued it or under which regime, so neither segmentation was possible.What
Both drain tasks accept
dispatcherandmode, and all four dispatch sites (push/scheduler × claim/lease) forward them._dispatch_tagsbuilds the pair once per drain and every delivery outcome carries it — 15 emit sites, including the ones indeliver_message,_discard_if_stale, and the parallel result handler._record_delivery_time_metricscarries the same pair. Latency is the quantity the claim regime is meant to move, and it was readable only in aggregate: a global p50 cannot show a cohort effect while the cohort is a small share of traffic. It is measured fromdate_added, so it spans queue wait, retry backoff and the request — the span dispatch acts on. Worth notingdelivery_time_msis routed to the precise backend and is a native Datadog distribution rather than the histogram the othermetrics.distributioncalls in this file produce, so percentiles are genuinely queryable per tag combination; the siblingsend_requesttimer has no median at all.A
DispatchModeStrEnum replaces the bare"claim"/"lease"literals now that the value is a task argument rather than a local metric tag.This also supersedes the derived push-vs-scheduler item split currently on the dashboard, which infers push volume by subtracting scheduler claims from the delivery total — an estimate that over-credits the scheduler whenever a claimed batch is not fully drained.
Deploy notes
Both metrics are in allowlist mode (
exclude_tags_mode: false), sodispatcherandmodeare dropped at intake until they are added:deliverydispatcher, modeaddeddelivery_time_msgithub_event_and_action, provider, region_sent_to, sentry_regionProjected from the fan-out
dispatchalready exhibits — it carries both tags today, so applying its dispatcher/mode volume shares to each series' own delivery volume gives 3.44x during the ramp, against a 4x ceiling the low-volume tail never reaches. Rare outcomes (max_age,attempts_exceed) put the truedeliverybase somewhat above a 24h sample.The peak is transient. Both modes are live only while the rollout is partial; at 100% every mailbox is
mode:claim, the tag collapses to a single value, and the count falls back to thedispatchersplit alone.delivery_time_msis the more expensive of the two — it is a percentile-enabled distribution and already carries the high-cardinalitygithub_event_and_action. Listingdispatcheralone holds it to 313 and still separates push, which fires on arrival, from the scheduler's 10s cycle — at the cost of the rollout A/B. Both are listed here on the assumption that the structural difference is worth measuring alongside the rollout; dropping one is a config change, not a code change.Deploy order does not matter. Both metrics keep working unchanged while the tags are unlisted, and queries that do not group by them are unaffected. Adding the tags before this deploys is a no-op since nothing emits them yet; adding them after costs only the window in between, which simply has no cohort dimension. The tags only need to be in place before the rollout ramps.
Tasks already queued when this deploys arrive without the arguments and tag
unknownrather than omitting the keys — a tag absent from some series fails agroup_byfor the whole query rather than showing a gap.New task kwargs reach old workers during the rolling window, as with #121221.
Scope
No behavior change: no query, delivery decision, or claim bound is altered. Tests cover attribution on the sequential and parallel drains for both metrics, the race path (the rollout's primary benefit signal), and the
unknownfallback; the four dispatch tests now also assert what they forward.Part of CW-1830. Follow-up to #121882.