fix(ci_visibility): handle rate limiting errors#17170
Conversation
Performance SLOsComparing candidate gnufede/ci-visibility-handle-rate-limiting (f58cc0b) with baseline main (174c1ba) 📈 Performance Regressions (2 suites)📈 iastaspects - 118/118✅ add_aspectTime: ✅ 102.527µs (SLO: <130.000µs 📉 -21.1%) vs baseline: +1.2% Memory: ✅ 43.905MB (SLO: <46.000MB -4.6%) vs baseline: +5.6% ✅ add_inplace_aspectTime: ✅ 100.049µs (SLO: <130.000µs 📉 -23.0%) vs baseline: -3.0% Memory: ✅ 43.863MB (SLO: <46.000MB -4.6%) vs baseline: +5.5% ✅ add_inplace_noaspectTime: ✅ 28.290µs (SLO: <40.000µs 📉 -29.3%) vs baseline: -0.4% Memory: ✅ 44.273MB (SLO: <46.000MB -3.8%) vs baseline: +6.6% ✅ add_noaspectTime: ✅ 48.586µs (SLO: <70.000µs 📉 -30.6%) vs baseline: -1.0% Memory: ✅ 43.886MB (SLO: <46.000MB -4.6%) vs baseline: +5.5% ✅ bytearray_aspectTime: ✅ 249.060µs (SLO: <400.000µs 📉 -37.7%) vs baseline: -2.3% Memory: ✅ 44.025MB (SLO: <46.000MB -4.3%) vs baseline: +6.0% ✅ bytearray_extend_aspectTime: ✅ 656.825µs (SLO: <800.000µs 📉 -17.9%) vs baseline: -0.2% Memory: ✅ 43.983MB (SLO: <46.000MB -4.4%) vs baseline: +5.8% ✅ bytearray_extend_noaspectTime: ✅ 264.887µs (SLO: <400.000µs 📉 -33.8%) vs baseline: -2.0% Memory: ✅ 43.871MB (SLO: <46.000MB -4.6%) vs baseline: +5.6% ✅ bytearray_noaspectTime: ✅ 139.254µs (SLO: <300.000µs 📉 -53.6%) vs baseline: -2.5% Memory: ✅ 44.330MB (SLO: <46.000MB -3.6%) vs baseline: +6.7% ✅ bytes_aspectTime: ✅ 219.583µs (SLO: <300.000µs 📉 -26.8%) vs baseline: -1.1% Memory: ✅ 43.963MB (SLO: <46.000MB -4.4%) vs baseline: +5.7% ✅ bytes_noaspectTime: ✅ 132.348µs (SLO: <200.000µs 📉 -33.8%) vs baseline: -2.3% Memory: ✅ 44.025MB (SLO: <46.000MB -4.3%) vs baseline: +6.1% ✅ bytesio_aspectTime: ✅ 3.760ms (SLO: <5.000ms 📉 -24.8%) vs baseline: -1.0% Memory: ✅ 43.982MB (SLO: <46.000MB -4.4%) vs baseline: +5.7% ✅ bytesio_noaspectTime: ✅ 315.097µs (SLO: <420.000µs 📉 -25.0%) vs baseline: -0.4% Memory: ✅ 43.901MB (SLO: <46.000MB -4.6%) vs baseline: +5.8% ✅ capitalize_aspectTime: ✅ 87.820µs (SLO: <300.000µs 📉 -70.7%) vs baseline: -0.6% Memory: ✅ 44.040MB (SLO: <46.000MB -4.3%) vs baseline: +6.0% ✅ capitalize_noaspectTime: ✅ 245.684µs (SLO: <300.000µs 📉 -18.1%) vs baseline: -3.5% Memory: ✅ 44.294MB (SLO: <46.000MB -3.7%) vs baseline: +6.5% ✅ casefold_aspectTime: ✅ 88.201µs (SLO: <500.000µs 📉 -82.4%) vs baseline: -0.7% Memory: ✅ 43.886MB (SLO: <46.000MB -4.6%) vs baseline: +5.7% ✅ casefold_noaspectTime: ✅ 304.090µs (SLO: <500.000µs 📉 -39.2%) vs baseline: -1.3% Memory: ✅ 43.887MB (SLO: <46.000MB -4.6%) vs baseline: +5.7% ✅ decode_aspectTime: ✅ 86.811µs (SLO: <100.000µs 📉 -13.2%) vs baseline: +0.6% Memory: ✅ 43.918MB (SLO: <46.000MB -4.5%) vs baseline: +5.8% ✅ decode_noaspectTime: ✅ 152.281µs (SLO: <210.000µs 📉 -27.5%) vs baseline: -1.0% Memory: ✅ 43.844MB (SLO: <46.000MB -4.7%) vs baseline: +5.6% ✅ encode_aspectTime: ✅ 84.279µs (SLO: <200.000µs 📉 -57.9%) vs baseline: ~same Memory: ✅ 43.825MB (SLO: <46.000MB -4.7%) vs baseline: +5.4% ✅ encode_noaspectTime: ✅ 140.857µs (SLO: <200.000µs 📉 -29.6%) vs baseline: -1.1% Memory: ✅ 44.004MB (SLO: <46.000MB -4.3%) vs baseline: +5.9% ✅ format_aspectTime: ✅ 14.617ms (SLO: <19.200ms 📉 -23.9%) vs baseline: -0.3% Memory: ✅ 43.958MB (SLO: <46.000MB -4.4%) vs baseline: +5.4% ✅ format_map_aspectTime: ✅ 16.369ms (SLO: <21.500ms 📉 -23.9%) vs baseline: ~same Memory: ✅ 44.187MB (SLO: <46.000MB -3.9%) vs baseline: +5.7% ✅ format_map_noaspectTime: ✅ 372.836µs (SLO: <500.000µs 📉 -25.4%) vs baseline: -1.7% Memory: ✅ 44.222MB (SLO: <46.000MB -3.9%) vs baseline: +6.3% ✅ format_noaspectTime: ✅ 303.853µs (SLO: <500.000µs 📉 -39.2%) vs baseline: -2.3% Memory: ✅ 44.019MB (SLO: <46.000MB -4.3%) vs baseline: +6.0% ✅ index_aspectTime: ✅ 137.731µs (SLO: <300.000µs 📉 -54.1%) vs baseline: 📈 +10.2% Memory: ✅ 44.018MB (SLO: <46.000MB -4.3%) vs baseline: +5.8% ✅ index_noaspectTime: ✅ 40.562µs (SLO: <300.000µs 📉 -86.5%) vs baseline: +0.8% Memory: ✅ 44.005MB (SLO: <46.000MB -4.3%) vs baseline: +5.8% ✅ join_aspectTime: ✅ 209.008µs (SLO: <300.000µs 📉 -30.3%) vs baseline: -1.7% Memory: ✅ 43.962MB (SLO: <46.000MB -4.4%) vs baseline: +5.6% ✅ join_noaspectTime: ✅ 143.989µs (SLO: <300.000µs 📉 -52.0%) vs baseline: ~same Memory: ✅ 43.967MB (SLO: <46.000MB -4.4%) vs baseline: +5.8% ✅ ljust_aspectTime: ✅ 576.596µs (SLO: <700.000µs 📉 -17.6%) vs baseline: 📈 +15.0% Memory: ✅ 43.939MB (SLO: <46.000MB -4.5%) vs baseline: +5.6% ✅ ljust_noaspectTime: ✅ 252.355µs (SLO: <300.000µs 📉 -15.9%) vs baseline: -1.7% Memory: ✅ 43.901MB (SLO: <46.000MB -4.6%) vs baseline: +5.8% ✅ lower_aspectTime: ✅ 294.503µs (SLO: <500.000µs 📉 -41.1%) vs baseline: -2.2% Memory: ✅ 43.855MB (SLO: <46.000MB -4.7%) vs baseline: +5.4% ✅ lower_noaspectTime: ✅ 231.058µs (SLO: <300.000µs 📉 -23.0%) vs baseline: -2.4% Memory: ✅ 43.804MB (SLO: <46.000MB -4.8%) vs baseline: +5.4% ✅ lstrip_aspectTime: ✅ 0.278ms (SLO: <3.000ms 📉 -90.7%) vs baseline: +0.6% Memory: ✅ 43.879MB (SLO: <46.000MB -4.6%) vs baseline: +5.6% ✅ lstrip_noaspectTime: ✅ 0.175ms (SLO: <3.000ms 📉 -94.2%) vs baseline: -1.4% Memory: ✅ 44.018MB (SLO: <46.000MB -4.3%) vs baseline: +5.9% ✅ modulo_aspectTime: ✅ 14.283ms (SLO: <18.750ms 📉 -23.8%) vs baseline: -0.3% Memory: ✅ 44.000MB (SLO: <46.000MB -4.3%) vs baseline: +5.7% ✅ modulo_aspect_for_bytearray_bytearrayTime: ✅ 14.838ms (SLO: <19.350ms 📉 -23.3%) vs baseline: +0.3% Memory: ✅ 44.142MB (SLO: <46.000MB -4.0%) vs baseline: +5.9% ✅ modulo_aspect_for_bytesTime: ✅ 14.406ms (SLO: <18.900ms 📉 -23.8%) vs baseline: ~same Memory: ✅ 44.050MB (SLO: <46.000MB -4.2%) vs baseline: +5.7% ✅ modulo_aspect_for_bytes_bytearrayTime: ✅ 14.612ms (SLO: <19.150ms 📉 -23.7%) vs baseline: ~same Memory: ✅ 44.095MB (SLO: <46.000MB -4.1%) vs baseline: +5.8% ✅ modulo_noaspectTime: ✅ 0.353ms (SLO: <3.000ms 📉 -88.2%) vs baseline: -3.2% Memory: ✅ 43.921MB (SLO: <46.000MB -4.5%) vs baseline: +5.8% ✅ replace_aspectTime: ✅ 18.362ms (SLO: <24.000ms 📉 -23.5%) vs baseline: -0.4% Memory: ✅ 44.179MB (SLO: <46.000MB -4.0%) vs baseline: +5.7% ✅ replace_noaspectTime: ✅ 279.398µs (SLO: <400.000µs 📉 -30.2%) vs baseline: -0.5% Memory: ✅ 43.968MB (SLO: <46.000MB -4.4%) vs baseline: +5.8% ✅ repr_aspectTime: ✅ 318.267µs (SLO: <420.000µs 📉 -24.2%) vs baseline: -0.9% Memory: ✅ 43.890MB (SLO: <46.000MB -4.6%) vs baseline: +5.7% ✅ repr_noaspectTime: ✅ 46.978µs (SLO: <90.000µs 📉 -47.8%) vs baseline: ~same Memory: ✅ 44.025MB (SLO: <46.000MB -4.3%) vs baseline: +5.9% ✅ rstrip_aspectTime: ✅ 387.578µs (SLO: <500.000µs 📉 -22.5%) vs baseline: -1.1% Memory: ✅ 43.882MB (SLO: <46.000MB -4.6%) vs baseline: +5.4% ✅ rstrip_noaspectTime: ✅ 181.730µs (SLO: <300.000µs 📉 -39.4%) vs baseline: -2.0% Memory: ✅ 43.925MB (SLO: <46.000MB -4.5%) vs baseline: +5.8% ✅ slice_aspectTime: ✅ 183.473µs (SLO: <300.000µs 📉 -38.8%) vs baseline: +1.1% Memory: ✅ 43.920MB (SLO: <46.000MB -4.5%) vs baseline: +5.4% ✅ slice_noaspectTime: ✅ 54.387µs (SLO: <90.000µs 📉 -39.6%) vs baseline: +0.8% Memory: ✅ 43.848MB (SLO: <46.000MB -4.7%) vs baseline: +5.3% ✅ stringio_aspectTime: ✅ 3.807ms (SLO: <5.000ms 📉 -23.9%) vs baseline: -0.4% Memory: ✅ 43.987MB (SLO: <46.000MB -4.4%) vs baseline: +5.9% ✅ stringio_noaspectTime: ✅ 375.441µs (SLO: <500.000µs 📉 -24.9%) vs baseline: +7.9% Memory: ✅ 43.823MB (SLO: <46.000MB -4.7%) vs baseline: +5.6% ✅ strip_aspectTime: ✅ 271.734µs (SLO: <350.000µs 📉 -22.4%) vs baseline: -0.1% Memory: ✅ 43.906MB (SLO: <46.000MB -4.6%) vs baseline: +5.6% ✅ strip_noaspectTime: ✅ 175.175µs (SLO: <240.000µs 📉 -27.0%) vs baseline: -0.1% Memory: ✅ 43.960MB (SLO: <46.000MB -4.4%) vs baseline: +5.8% ✅ swapcase_aspectTime: ✅ 334.281µs (SLO: <500.000µs 📉 -33.1%) vs baseline: -0.2% Memory: ✅ 43.813MB (SLO: <46.000MB -4.8%) vs baseline: +5.3% ✅ swapcase_noaspectTime: ✅ 267.494µs (SLO: <400.000µs 📉 -33.1%) vs baseline: -0.6% Memory: ✅ 43.847MB (SLO: <46.000MB -4.7%) vs baseline: +5.5% ✅ title_aspectTime: ✅ 317.567µs (SLO: <500.000µs 📉 -36.5%) vs baseline: -3.3% Memory: ✅ 43.906MB (SLO: <46.000MB -4.6%) vs baseline: +5.6% ✅ title_noaspectTime: ✅ 252.620µs (SLO: <400.000µs 📉 -36.8%) vs baseline: -3.5% Memory: ✅ 43.919MB (SLO: <46.000MB -4.5%) vs baseline: +5.8% ✅ translate_aspectTime: ✅ 490.509µs (SLO: <700.000µs 📉 -29.9%) vs baseline: -0.1% Memory: ✅ 43.824MB (SLO: <46.000MB -4.7%) vs baseline: +5.5% ✅ translate_noaspectTime: ✅ 424.491µs (SLO: <500.000µs 📉 -15.1%) vs baseline: -1.6% Memory: ✅ 44.270MB (SLO: <46.000MB -3.8%) vs baseline: +6.5% ✅ upper_aspectTime: ✅ 294.626µs (SLO: <500.000µs 📉 -41.1%) vs baseline: -1.3% Memory: ✅ 43.827MB (SLO: <46.000MB -4.7%) vs baseline: +5.5% ✅ upper_noaspectTime: ✅ 230.152µs (SLO: <400.000µs 📉 -42.5%) vs baseline: -1.8% Memory: ✅ 43.889MB (SLO: <46.000MB -4.6%) vs baseline: +5.5% 📈 iastaspectsospath - 24/24✅ ospathbasename_aspectTime: ✅ 509.093µs (SLO: <700.000µs 📉 -27.3%) vs baseline: 📈 +19.2% Memory: ✅ 43.583MB (SLO: <46.000MB -5.3%) vs baseline: +4.9% ✅ ospathbasename_noaspectTime: ✅ 430.939µs (SLO: <700.000µs 📉 -38.4%) vs baseline: -1.6% Memory: ✅ 43.832MB (SLO: <46.000MB -4.7%) vs baseline: +6.0% ✅ ospathjoin_aspectTime: ✅ 625.002µs (SLO: <700.000µs 📉 -10.7%) vs baseline: -0.9% Memory: ✅ 43.682MB (SLO: <46.000MB -5.0%) vs baseline: +5.2% ✅ ospathjoin_noaspectTime: ✅ 631.580µs (SLO: <700.000µs -9.8%) vs baseline: -0.6% Memory: ✅ 43.850MB (SLO: <46.000MB -4.7%) vs baseline: +5.8% ✅ ospathnormcase_aspectTime: ✅ 349.665µs (SLO: <700.000µs 📉 -50.0%) vs baseline: -1.3% Memory: ✅ 43.681MB (SLO: <46.000MB -5.0%) vs baseline: +5.2% ✅ ospathnormcase_noaspectTime: ✅ 356.702µs (SLO: <700.000µs 📉 -49.0%) vs baseline: -1.9% Memory: ✅ 43.669MB (SLO: <46.000MB -5.1%) vs baseline: +5.5% ✅ ospathsplit_aspectTime: ✅ 479.720µs (SLO: <700.000µs 📉 -31.5%) vs baseline: -2.5% Memory: ✅ 43.982MB (SLO: <46.000MB -4.4%) vs baseline: +6.3% ✅ ospathsplit_noaspectTime: ✅ 494.189µs (SLO: <700.000µs 📉 -29.4%) vs baseline: -1.3% Memory: ✅ 43.910MB (SLO: <46.000MB -4.5%) vs baseline: +5.8% ✅ ospathsplitdrive_aspectTime: ✅ 374.364µs (SLO: <700.000µs 📉 -46.5%) vs baseline: -0.5% Memory: ✅ 43.679MB (SLO: <46.000MB -5.0%) vs baseline: +5.3% ✅ ospathsplitdrive_noaspectTime: ✅ 72.686µs (SLO: <700.000µs 📉 -89.6%) vs baseline: ~same Memory: ✅ 43.844MB (SLO: <46.000MB -4.7%) vs baseline: +5.8% ✅ ospathsplitext_aspectTime: ✅ 454.170µs (SLO: <700.000µs 📉 -35.1%) vs baseline: -2.2% Memory: ✅ 43.614MB (SLO: <46.000MB -5.2%) vs baseline: +5.1% ✅ ospathsplitext_noaspectTime: ✅ 463.453µs (SLO: <700.000µs 📉 -33.8%) vs baseline: -1.7% Memory: ✅ 43.818MB (SLO: <46.000MB -4.7%) vs baseline: +5.9% 🟡 Near SLO Breach (2 suites)🟡 djangosimple - 30/30✅ appsecTime: ✅ 19.731ms (SLO: <22.300ms 📉 -11.5%) vs baseline: ~same Memory: ✅ 69.324MB (SLO: <73.500MB -5.7%) vs baseline: +5.6% ✅ exception-replay-enabledTime: ✅ 1.323ms (SLO: <1.450ms -8.7%) vs baseline: -0.4% Memory: ✅ 67.807MB (SLO: <71.500MB -5.2%) vs baseline: +5.6% ✅ iastTime: ✅ 19.859ms (SLO: <22.250ms 📉 -10.7%) vs baseline: +0.6% Memory: ✅ 69.167MB (SLO: <75.000MB -7.8%) vs baseline: +5.4% ✅ profilerTime: ✅ 15.131ms (SLO: <16.550ms -8.6%) vs baseline: -0.2% Memory: ✅ 60.401MB (SLO: <61.000MB 🟡 -1.0%) vs baseline: +5.4% ✅ resource-renamingTime: ✅ 19.666ms (SLO: <21.750ms -9.6%) vs baseline: +0.2% Memory: ✅ 69.368MB (SLO: <73.500MB -5.6%) vs baseline: +5.7% ✅ span-code-originTime: ✅ 20.022ms (SLO: <28.200ms 📉 -29.0%) vs baseline: +0.4% Memory: ✅ 69.479MB (SLO: <75.000MB -7.4%) vs baseline: +5.4% ✅ tracerTime: ✅ 19.731ms (SLO: <21.750ms -9.3%) vs baseline: +0.3% Memory: ✅ 69.265MB (SLO: <75.000MB -7.6%) vs baseline: +5.5% ✅ tracer-and-profilerTime: ✅ 21.042ms (SLO: <23.500ms 📉 -10.5%) vs baseline: -0.1% Memory: ✅ 71.428MB (SLO: <75.000MB -4.8%) vs baseline: +5.3% ✅ tracer-dont-create-db-spansTime: ✅ 19.813ms (SLO: <21.500ms -7.8%) vs baseline: +0.6% Memory: ✅ 69.245MB (SLO: <75.000MB -7.7%) vs baseline: +5.5% ✅ tracer-minimalTime: ✅ 16.772ms (SLO: <17.500ms -4.2%) vs baseline: -0.5% Memory: ✅ 69.088MB (SLO: <75.000MB -7.9%) vs baseline: +5.3% ✅ tracer-nativeTime: ✅ 19.678ms (SLO: <21.750ms -9.5%) vs baseline: +0.7% Memory: ✅ 69.304MB (SLO: <72.500MB -4.4%) vs baseline: +5.6% ✅ tracer-no-cachesTime: ✅ 17.675ms (SLO: <19.650ms 📉 -10.0%) vs baseline: +0.2% Memory: ✅ 69.196MB (SLO: <75.000MB -7.7%) vs baseline: +5.5% ✅ tracer-no-databasesTime: ✅ 19.336ms (SLO: <20.100ms -3.8%) vs baseline: +0.5% Memory: ✅ 69.088MB (SLO: <75.000MB -7.9%) vs baseline: +5.2% ✅ tracer-no-middlewareTime: ✅ 19.438ms (SLO: <21.500ms -9.6%) vs baseline: +0.2% Memory: ✅ 69.137MB (SLO: <75.000MB -7.8%) vs baseline: +5.4% ✅ tracer-no-templatesTime: ✅ 19.695ms (SLO: <22.000ms 📉 -10.5%) vs baseline: +0.8% Memory: ✅ 69.131MB (SLO: <73.500MB -5.9%) vs baseline: +5.3% 🟡 recursivecomputation - 8/8✅ deepTime: ✅ 311.377ms (SLO: <320.950ms -3.0%) vs baseline: +0.1% Memory: ✅ 37.454MB (SLO: <38.750MB -3.3%) vs baseline: +5.6% ✅ deep-profiledTime: ✅ 333.053ms (SLO: <359.150ms -7.3%) vs baseline: +0.4% Memory: ✅ 43.824MB (SLO: <46.000MB -4.7%) vs baseline: +5.4% ✅ mediumTime: ✅ 7.292ms (SLO: <7.400ms 🟡 -1.5%) vs baseline: +0.2% Memory: ✅ 36.333MB (SLO: <38.000MB -4.4%) vs baseline: +5.7% ✅ shallowTime: ✅ 1.016ms (SLO: <1.050ms -3.2%) vs baseline: +1.7% Memory: ✅ 36.255MB (SLO: <38.000MB -4.6%) vs baseline: +5.5%
|
Codeowners resolved as |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2737e0b490
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if reset_value > now: | ||
| # Unix timestamp: wait until that point in time | ||
| result.retry_after_seconds = float(reset_value - now) | ||
| else: | ||
| # Duration in seconds | ||
| result.retry_after_seconds = float(reset_value) |
There was a problem hiding this comment.
Treat expired reset timestamps as zero backoff
The X-RateLimit-Reset parsing branch treats any value <= now as a duration, so an absolute Unix timestamp that is equal to or slightly behind local time gets interpreted as billions of seconds. In practice, if the backend returns timestamp-style reset values (especially around second boundaries or on repeated 429s), this can make time.sleep() block for decades and effectively hang CI visibility uploads instead of retrying promptly. This should compute max(reset_value - now, 0) for timestamp-style values rather than reinterpreting past timestamps as durations.
Useful? React with 👍 / 👎.
Description
HTTP 429 (Too Many Requests) responses from the Datadog backend were falling through to the
>= 400branch in the backend connector, classifying them as non-retriableCODE_4XXerrors. This caused CI visibility data to be silently dropped whenever the backend applied rate limiting.This fix:
RATE_LIMITEDtoErrorTypewith a distinct internal value ("rate_limited")RATE_LIMITEDRATE_LIMITEDtoRETRIABLE_ERRORSso they are retried up to the existing retry limitX-RateLimit-Resetresponse header to use as the retry delay when present (supports both Unix timestamps and durations in seconds), falling back to exponential backoff otherwiseRATE_LIMITEDtostatus_code_4xx_responsein telemetry metrics for cross-language consistencyTesting
Unit tests added in
tests/testing/internal/test_http.pycovering:X-RateLimit-Resetas Unix timestamp → correct computed delayX-RateLimit-Resetas duration in seconds → value used directlyX-RateLimit-Reset→ falls back to exponential backoffTelemetry test updated in
tests/testing/internal/test_telemetry.pyto verifyRATE_LIMITEDmaps to"status_code"inendpoint_payload.requests_errorsand to"status_code_4xx_response"in per-request error metrics.Risks
Low. The change only affects the retry path for a previously unhandled status code. All other status codes follow the same logic as before.
Additional Notes
RATE_LIMITEDintentionally uses the internal value"rate_limited"(distinct fromCODE_4XX) to avoid Python enum aliasing, which would have madeCODE_4XXan alias and caused all 4xx responses to be retried. The mapping to the canonical telemetry value is done explicitly at emission time inrecord_error().