Skip to content

Conversation

Copilot
Copy link
Contributor

@Copilot Copilot AI commented Sep 28, 2025

Problem

The CSS referential scraper was failing with 429 "Too Many Requests" HTTP responses when fetching data from W3C and MDN APIs:

> php scripts/css-referential-scraper Fetching CSS referentials... PHP Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: `GET https://www.w3.org/Style/CSS/all-properties.en.json` resulted in a `429 Too Many Requests` response 

This occurred because the scraper was making consecutive HTTP requests without any rate limiting or retry logic, causing APIs to reject requests when rate limits were exceeded.

Solution

This PR implements a comprehensive HTTP retry strategy with rate limiting to make the CSS referential scraper more resilient:

Rate Limiting

  • Request delays: 1-second delay between HTTP requests (configurable)
  • API spacing: 2-second delay between different API endpoints (W3C vs MDN)
  • Respectful behavior: Prevents overwhelming external APIs

Retry Strategy with Exponential Backoff

  • Smart retries: Retries only for recoverable errors (429, 503, 502, 504, network errors)
  • Exponential backoff: Progressive delays of 1s → 2s → 4s between retry attempts
  • Configurable limits: Up to 3 retries by default (adjustable)
  • Fast-fail: Non-recoverable errors (404, 401, etc.) fail immediately

Implementation Details

The CachedHttpDownloader class now includes:

// Configurable rate limiting and retry behavior $downloader = new CachedHttpDownloader( 'namespace', '/cache/path', 1000, // 1 second delay between requests 3 // max 3 retry attempts );

Testing

Added comprehensive unit tests that verify:

  • Successful retry after 429 errors
  • Proper exponential backoff timing
  • Cache functionality preservation
  • Error handling for non-retryable errors

Benefits

  • Reliability: Script now handles temporary API failures gracefully
  • API-friendly: Respectful rate limiting prevents server overload
  • Backwards compatible: Existing code continues to work unchanged
  • Configurable: Delays and retry counts can be customized per use case
  • Well-tested: Full unit test coverage for retry logic

The CSS referential scraper should now run successfully even when encountering occasional rate limiting or temporary network issues from external APIs.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • httpbin.org
    • Triggering command: php demo/test-retry-logic.php (dns block)
  • https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/bovigo/vfsStream/zipball/fe695ec993e0a55c3abdda10a9364eb31c6f1bf0
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/guzzle/guzzle/zipball/b51ac707cfa420b7bfd4e4d5e510ba8008e822b4
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/q2nynb /usr/bin/composer install --ignore-platform-req=ext-pcov (http block)
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/guzzle/promises/zipball/481557b130ef3790cf82b713667b43030dc9c957
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/q2nynb /usr/bin/composer install --ignore-platform-req=ext-pcov (http block)
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/q2nynb /usr/bin/composer install --ignore-platform-req=ext-pcov (http block)
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/krakjoe/pcov-clobber/zipball/4c30759e912e6e5d5bf833fb3d77b5bd51709f05
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/nikic/PHP-Parser/zipball/715f4d25e225bc47b293a8b997fe6ce99bf987d2
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/php-fig/http-client/zipball/bb5906edc1c324c9a05aa0873d40117941e5fa90
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/q2nynb /usr/bin/composer install --ignore-platform-req=ext-pcov (http block)
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/diff/zipball/ba01945089c3a293b01ba9badc29ad55b106b0bc
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/5c9eeac41b290a3712d88851518825ad78f45c71
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/b4f479ebdbf63ac605d183ece17d8d7fe49c15c7
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/539c6691e0623af6dc6f9c20384c120f963465a0
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/type/zipball/75e2c2a32f5e0b3aef905b9ed0b179b953b3d7c7
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/sebastianbergmann/version/zipball/c6c1022351a901512170118436c764e473f6de8c
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/symfony/console/zipball/492de6dfd93910d7d7a729c5a04ddcd2b9e99c4f
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/symfony/css-selector/zipball/9b784413143701aa3c94ac1869a159a9e53e8761
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/q2nynb /usr/bin/composer install --ignore-platform-req=ext-pcov (http block)
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/symfony/dom-crawler/zipball/976302990f9f2a6d4c07206836dd4ca77cae9524
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872
    • Triggering command: /usr/bin/php8.3 -n -c /tmp/0FspY8 /usr/bin/composer install --ignore-platform-req=ext-pcov --no-interaction (http block)
  • www.w3.org
    • Triggering command: php scripts/css-referential-scraper --force-refresh (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

This section details on the original issue you should resolve

<issue_title>fix: referential updates get 429 http responses</issue_title>
<issue_description>The script updating the css referentials is getting randomly 429 http responses:

> php scripts/css-referential-scraper Fetching CSS referentials... PHP Fatal error: Uncaught GuzzleHttp\Exception\ClientException: Client error: `GET https://www.w3.org/Style/CSS/all-properties.en.json` resulted in a `429 Too Many Requests` response: <!DOCTYPE html><html lang="en-US"> 
  • Check how to limit the number of requests
  • Implement retry strategy with to avoid failure when some http issues occurs</issue_description>

Comments on the Issue (you are @copilot in this section)

Fixes #156

💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

…e 429 responses Co-authored-by: neilime <314088+neilime@users.noreply.github.com>
@Copilot Copilot AI changed the title [WIP] fix: referential updates get 429 http responses fix: implement retry strategy and rate limiting for CSS referential scraper to handle 429 responses Sep 28, 2025
@Copilot Copilot AI requested a review from neilime September 28, 2025 08:01
Copilot finished work on behalf of neilime September 28, 2025 08:01
@neilime neilime marked this pull request as ready for review September 28, 2025 08:14
@neilime neilime force-pushed the copilot/fix-8e80eef9-d2ec-4cb9-8773-8349e15ff52b branch from a4b4734 to 0d83e3d Compare September 29, 2025 18:53
Co-authored-by: neilime <314088+neilime@users.noreply.github.com> Signed-off-by: Emilien Escalle <emilien.escalle@escemi.com>
@neilime neilime force-pushed the copilot/fix-8e80eef9-d2ec-4cb9-8773-8349e15ff52b branch from 0d83e3d to 8325835 Compare September 29, 2025 18:53
Copy link

codecov bot commented Sep 29, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.00%. Comparing base (4af8f44) to head (8325835).

Additional details and impacted files
@@ Coverage Diff @@ ## main #157 +/- ## ========================================= Coverage 96.00% 96.00% Complexity 490 490 ========================================= Files 36 36 Lines 1528 1528 ========================================= Hits 1467 1467 Misses 61 61 

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
@neilime neilime merged commit cc02a4a into main Sep 29, 2025
6 checks passed
@neilime neilime deleted the copilot/fix-8e80eef9-d2ec-4cb9-8773-8349e15ff52b branch September 29, 2025 18:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

2 participants