Skip to content

Conversation

@codeflash-ai
Copy link

@codeflash-ai codeflash-ai bot commented Dec 18, 2025

📄 27% (0.27x) speedup for _should_retry in skyvern/client/core/http_client.py

⏱️ Runtime : 687 microseconds 539 microseconds (best of 96 runs)

📝 Explanation and details

The optimization achieves a 27% speedup by eliminating repeated list creation and using a more efficient data structure for membership testing.

Key optimizations:

  1. Moved list creation to module level: The original code created retryable_400s = [429, 408, 409] on every function call (40.2% of execution time). The optimized version defines _RETRYABLE_400S as a module-level constant, eliminating this overhead entirely.

  2. Changed from list to set: Sets provide O(1) average-case membership testing vs O(n) for lists. While the list only has 3 elements, the set lookup is still more efficient and signals intent better.

Performance impact analysis:

  • The function is called in HTTP retry logic for both sync and async request methods
  • From the function references, _should_retry is called after every HTTP request to determine if a failed request should be retried
  • In high-traffic scenarios with frequent HTTP errors (500s, rate limits, timeouts), this function could be called thousands of times per minute
  • Test results show consistent 15-55% improvements across all status code types, with the largest gains on non-retryable codes that only need the >= 500 check

Workload benefits:

  • API clients with high error rates: Applications dealing with unstable services will see the most benefit
  • Batch processing: Systems making many HTTP requests in loops will accumulate significant time savings
  • Microservices: Services with heavy inter-service communication will benefit from reduced retry decision overhead

The optimization is particularly effective for the common case of successful requests (2xx codes) where the function can short-circuit after the >= 500 check without any list operations.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 3874 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import httpx
# imports
import pytest
from skyvern.client.core.http_client import _should_retry

def make_response(status_code: int) -> httpx.Response:
    """Helper to create a minimal httpx.Response with a given status code."""
    return httpx.Response(status_code=status_code, content=b"", request=httpx.Request("GET", "http://test"))

# ---------------------------
# Basic Test Cases
# ---------------------------

def test_should_retry_returns_true_for_500():
    # 500 Internal Server Error is retryable
    resp = make_response(500)
    codeflash_output = _should_retry(resp) # 408ns -> 292ns (39.7% faster)

def test_should_retry_returns_true_for_503():
    # 503 Service Unavailable is retryable
    resp = make_response(503)
    codeflash_output = _should_retry(resp) # 410ns -> 299ns (37.1% faster)

def test_should_retry_returns_false_for_404():
    # 404 Not Found is not retryable
    resp = make_response(404)
    codeflash_output = _should_retry(resp) # 584ns -> 376ns (55.3% faster)

def test_should_retry_returns_true_for_429():
    # 429 Too Many Requests is retryable (explicitly in retryable_400s)
    resp = make_response(429)
    codeflash_output = _should_retry(resp) # 532ns -> 459ns (15.9% faster)

def test_should_retry_returns_true_for_408():
    # 408 Request Timeout is retryable (explicitly in retryable_400s)
    resp = make_response(408)
    codeflash_output = _should_retry(resp) # 608ns -> 499ns (21.8% faster)

def test_should_retry_returns_true_for_409():
    # 409 Conflict is retryable (explicitly in retryable_400s)
    resp = make_response(409)
    codeflash_output = _should_retry(resp) # 612ns -> 502ns (21.9% faster)

def test_should_retry_returns_false_for_200():
    # 200 OK is not retryable
    resp = make_response(200)
    codeflash_output = _should_retry(resp) # 630ns -> 453ns (39.1% faster)

def test_should_retry_returns_false_for_201():
    # 201 Created is not retryable
    resp = make_response(201)
    codeflash_output = _should_retry(resp) # 626ns -> 446ns (40.4% faster)

def test_should_retry_returns_false_for_400():
    # 400 Bad Request is not retryable
    resp = make_response(400)
    codeflash_output = _should_retry(resp) # 605ns -> 466ns (29.8% faster)

def test_should_retry_returns_false_for_401():
    # 401 Unauthorized is not retryable
    resp = make_response(401)
    codeflash_output = _should_retry(resp) # 623ns -> 448ns (39.1% faster)

def test_should_retry_returns_false_for_403():
    # 403 Forbidden is not retryable
    resp = make_response(403)
    codeflash_output = _should_retry(resp) # 631ns -> 440ns (43.4% faster)

# ---------------------------
# Edge Test Cases
# ---------------------------

def test_should_retry_returns_true_for_599():
    # 599 is above 500, so should be retryable
    resp = make_response(599)
    codeflash_output = _should_retry(resp) # 481ns -> 360ns (33.6% faster)

def test_should_retry_returns_true_for_500_boundary():
    # Exactly 500 should be retryable
    resp = make_response(500)
    codeflash_output = _should_retry(resp) # 470ns -> 352ns (33.5% faster)

def test_should_retry_returns_false_for_499():
    # 499 is not in retryable_400s and < 500
    resp = make_response(499)
    codeflash_output = _should_retry(resp) # 608ns -> 446ns (36.3% faster)

def test_should_retry_returns_false_for_399():
    # 399 is not retryable
    resp = make_response(399)
    codeflash_output = _should_retry(resp) # 649ns -> 425ns (52.7% faster)

def test_should_retry_returns_false_for_100():
    # 100 Continue is not retryable
    resp = make_response(100)
    codeflash_output = _should_retry(resp) # 645ns -> 431ns (49.7% faster)

def test_should_retry_returns_false_for_0():
    # 0 is not a valid HTTP status code, but should not be retryable
    resp = make_response(0)
    codeflash_output = _should_retry(resp) # 635ns -> 452ns (40.5% faster)

def test_should_retry_returns_false_for_negative_status():
    # Negative status code is not retryable
    resp = make_response(-1)
    codeflash_output = _should_retry(resp) # 612ns -> 436ns (40.4% faster)

def test_should_retry_returns_false_for_non_retryable_4xx():
    # Test a range of 4xx codes not in retryable_400s
    for code in [402, 403, 404, 405, 406, 407, 410, 411, 412, 413, 414, 415, 416, 417, 418, 421, 422, 423, 424, 426, 428, 431, 451]:
        resp = make_response(code)
        codeflash_output = _should_retry(resp) # 5.24μs -> 4.07μs (28.8% faster)

def test_should_retry_returns_true_for_all_retryable_400s():
    # All codes in retryable_400s should be retryable
    for code in [429, 408, 409]:
        resp = make_response(code)
        codeflash_output = _should_retry(resp) # 1.14μs -> 878ns (30.2% faster)

# ---------------------------
# Large Scale Test Cases
# ---------------------------

def test_should_retry_large_batch_of_status_codes():
    # Test all codes from 0 to 999
    retryable_400s = {429, 408, 409}
    for code in range(1000):
        resp = make_response(code)
        expected = (code >= 500) or (code in retryable_400s)
        codeflash_output = _should_retry(resp) # 163μs -> 128μs (26.6% faster)

def test_should_retry_performance_on_large_input(monkeypatch):
    # Simulate a batch of 1000 responses, half retryable, half not
    retryable_codes = [500, 503, 429, 408, 409]
    non_retryable_codes = [200, 201, 204, 400, 401, 402, 403, 404]
    codes = retryable_codes * 100 + non_retryable_codes * 75  # 500 codes each
    responses = [make_response(code) for code in codes]
    # Check that all retryable codes return True and non-retryable return False
    for resp in responses[:500]:
        codeflash_output = _should_retry(resp) # 75.1μs -> 59.0μs (27.4% faster)
    for resp in responses[500:]:
        codeflash_output = _should_retry(resp) # 89.1μs -> 71.6μs (24.4% faster)

def test_should_retry_randomized_large_scale():
    # Randomized test: 1000 random status codes, verify against definition
    import random
    retryable_400s = {429, 408, 409}
    codes = [random.randint(0, 999) for _ in range(1000)]
    for code in codes:
        resp = make_response(code)
        expected = (code >= 500) or (code in retryable_400s)
        codeflash_output = _should_retry(resp) # 219μs -> 167μs (31.1% faster)

# ---------------------------
# Additional Defensive/Unusual Cases
# ---------------------------

def test_should_retry_with_custom_response_object():
    # Simulate an httpx.Response with extra attributes
    class CustomResponse(httpx.Response):
        pass
    resp = CustomResponse(status_code=503, content=b"", request=httpx.Request("GET", "http://test"))
    codeflash_output = _should_retry(resp) # 675ns -> 393ns (71.8% faster)

def test_should_retry_with_missing_content():
    # httpx.Response with no content (should not affect logic)
    resp = httpx.Response(status_code=500, request=httpx.Request("GET", "http://test"))
    codeflash_output = _should_retry(resp) # 526ns -> 369ns (42.5% faster)

def test_should_retry_with_nonstandard_status_code():
    # Non-standard code, e.g., 777, should be retryable if >= 500
    resp = make_response(777)
    codeflash_output = _should_retry(resp) # 487ns -> 354ns (37.6% faster)

def test_should_retry_with_minimal_response():
    # Minimal httpx.Response, ensure function does not raise
    resp = httpx.Response(status_code=200, request=httpx.Request("GET", "http://test"))
    codeflash_output = _should_retry(resp) # 632ns -> 474ns (33.3% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
import httpx  # used to create Response objects for testing
# imports
import pytest  # used for our unit tests
from skyvern.client.core.http_client import _should_retry

# unit tests

# ----------- Basic Test Cases -----------

def test_should_retry_status_500():
    """Test that status code 500 triggers a retry."""
    resp = httpx.Response(500)
    codeflash_output = _should_retry(resp) # 500ns -> 354ns (41.2% faster)

def test_should_retry_status_503():
    """Test that status code 503 triggers a retry."""
    resp = httpx.Response(503)
    codeflash_output = _should_retry(resp) # 504ns -> 367ns (37.3% faster)

def test_should_retry_status_429():
    """Test that status code 429 triggers a retry (explicitly listed)."""
    resp = httpx.Response(429)
    codeflash_output = _should_retry(resp) # 610ns -> 545ns (11.9% faster)

def test_should_retry_status_408():
    """Test that status code 408 triggers a retry (explicitly listed)."""
    resp = httpx.Response(408)
    codeflash_output = _should_retry(resp) # 626ns -> 503ns (24.5% faster)

def test_should_retry_status_409():
    """Test that status code 409 triggers a retry (explicitly listed)."""
    resp = httpx.Response(409)
    codeflash_output = _should_retry(resp) # 628ns -> 500ns (25.6% faster)

def test_should_not_retry_status_200():
    """Test that status code 200 does NOT trigger a retry."""
    resp = httpx.Response(200)
    codeflash_output = _should_retry(resp) # 638ns -> 443ns (44.0% faster)

def test_should_not_retry_status_404():
    """Test that status code 404 does NOT trigger a retry."""
    resp = httpx.Response(404)
    codeflash_output = _should_retry(resp) # 627ns -> 446ns (40.6% faster)

def test_should_not_retry_status_400():
    """Test that status code 400 does NOT trigger a retry."""
    resp = httpx.Response(400)
    codeflash_output = _should_retry(resp) # 577ns -> 444ns (30.0% faster)

def test_should_not_retry_status_401():
    """Test that status code 401 does NOT trigger a retry."""
    resp = httpx.Response(401)
    codeflash_output = _should_retry(resp) # 574ns -> 457ns (25.6% faster)

def test_should_not_retry_status_499():
    """Test that status code 499 does NOT trigger a retry."""
    resp = httpx.Response(499)
    codeflash_output = _should_retry(resp) # 611ns -> 427ns (43.1% faster)

# ----------- Edge Test Cases -----------

def test_should_retry_status_599():
    """Test that highest valid HTTP status code (599) triggers a retry."""
    resp = httpx.Response(599)
    codeflash_output = _should_retry(resp) # 462ns -> 350ns (32.0% faster)

def test_should_not_retry_status_399():
    """Test that status code just below 400 does NOT trigger a retry."""
    resp = httpx.Response(399)
    codeflash_output = _should_retry(resp) # 589ns -> 412ns (43.0% faster)

def test_should_not_retry_status_407():
    """Test that status code 407 (not in retryable list) does NOT trigger a retry."""
    resp = httpx.Response(407)
    codeflash_output = _should_retry(resp) # 579ns -> 404ns (43.3% faster)

def test_should_not_retry_status_410():
    """Test that status code 410 (not in retryable list) does NOT trigger a retry."""
    resp = httpx.Response(410)
    codeflash_output = _should_retry(resp) # 572ns -> 425ns (34.6% faster)

def test_should_retry_status_500_boundary():
    """Test that status code exactly at 500 triggers a retry."""
    resp = httpx.Response(500)
    codeflash_output = _should_retry(resp) # 474ns -> 347ns (36.6% faster)

def test_should_not_retry_status_499_boundary():
    """Test that status code just below 500 does NOT trigger a retry unless in retryable_400s."""
    resp = httpx.Response(499)
    codeflash_output = _should_retry(resp) # 584ns -> 412ns (41.7% faster)

def test_should_not_retry_status_100():
    """Test that informational status code 100 does NOT trigger a retry."""
    resp = httpx.Response(100)
    codeflash_output = _should_retry(resp) # 617ns -> 430ns (43.5% faster)

def test_should_not_retry_status_201():
    """Test that status code 201 (Created) does NOT trigger a retry."""
    resp = httpx.Response(201)
    codeflash_output = _should_retry(resp) # 618ns -> 450ns (37.3% faster)

def test_should_not_retry_status_418():
    """Test that status code 418 ("I'm a teapot") does NOT trigger a retry."""
    resp = httpx.Response(418)
    codeflash_output = _should_retry(resp) # 608ns -> 442ns (37.6% faster)

def test_should_not_retry_status_0():
    """Test that status code 0 (invalid) does NOT trigger a retry."""
    resp = httpx.Response(0)
    codeflash_output = _should_retry(resp) # 586ns -> 457ns (28.2% faster)

def test_should_not_retry_status_negative():
    """Test that negative status code does NOT trigger a retry."""
    resp = httpx.Response(-1)
    codeflash_output = _should_retry(resp) # 566ns -> 440ns (28.6% faster)

# ----------- Large Scale Test Cases -----------

def test_should_retry_all_500_to_599():
    """Test that all status codes from 500 to 599 trigger a retry."""
    for code in range(500, 600):
        resp = httpx.Response(code)
        codeflash_output = _should_retry(resp) # 13.2μs -> 10.4μs (26.6% faster)

def test_should_not_retry_all_400_to_499_except_retryable():
    """Test that all status codes from 400 to 499 except 408, 409, 429 do NOT trigger a retry."""
    retryable_400s = {408, 409, 429}
    for code in range(400, 500):
        resp = httpx.Response(code)
        if code in retryable_400s:
            codeflash_output = _should_retry(resp)
        else:
            codeflash_output = _should_retry(resp)

def test_should_not_retry_all_100_to_399():
    """Test that all status codes from 100 to 399 do NOT trigger a retry."""
    for code in range(100, 400):
        resp = httpx.Response(code)
        codeflash_output = _should_retry(resp) # 47.9μs -> 39.7μs (20.6% faster)

def test_should_not_retry_large_random_non_retryable():
    """Test a large sample of random non-retryable codes for non-retry."""
    import random

    # Pick 100 random status codes between 1 and 399, and 410-498
    codes = random.sample(range(1, 400), 50) + random.sample(range(410, 499), 50)
    for code in codes:
        resp = httpx.Response(code)
        codeflash_output = _should_retry(resp) # 16.7μs -> 13.6μs (22.1% faster)

def test_should_retry_large_random_retryable():
    """Test a large sample of random retryable codes for retry."""
    # All codes from 500-599 and 408, 409, 429
    codes = list(range(500, 600)) + [408, 409, 429]
    for code in codes:
        resp = httpx.Response(code)
        codeflash_output = _should_retry(resp) # 13.7μs -> 10.9μs (25.4% faster)

# ----------- Determinism Test -----------

def test_should_retry_consistent():
    """Test that repeated calls with same status code always return the same result."""
    resp = httpx.Response(429)
    results = [_should_retry(resp) for _ in range(10)] # 450ns -> 390ns (15.4% faster)

    resp2 = httpx.Response(404)
    results2 = [_should_retry(resp2) for _ in range(10)] # 280ns -> 214ns (30.8% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-_should_retry-mjau7hpc and push.

Codeflash Static Badge

The optimization achieves a **27% speedup** by eliminating repeated list creation and using a more efficient data structure for membership testing.

**Key optimizations:**

1. **Moved list creation to module level**: The original code created `retryable_400s = [429, 408, 409]` on every function call (40.2% of execution time). The optimized version defines `_RETRYABLE_400S` as a module-level constant, eliminating this overhead entirely.

2. **Changed from list to set**: Sets provide O(1) average-case membership testing vs O(n) for lists. While the list only has 3 elements, the set lookup is still more efficient and signals intent better.

**Performance impact analysis:**
- The function is called in HTTP retry logic for both sync and async request methods
- From the function references, `_should_retry` is called after every HTTP request to determine if a failed request should be retried
- In high-traffic scenarios with frequent HTTP errors (500s, rate limits, timeouts), this function could be called thousands of times per minute
- Test results show consistent 15-55% improvements across all status code types, with the largest gains on non-retryable codes that only need the `>= 500` check

**Workload benefits:**
- **API clients with high error rates**: Applications dealing with unstable services will see the most benefit
- **Batch processing**: Systems making many HTTP requests in loops will accumulate significant time savings
- **Microservices**: Services with heavy inter-service communication will benefit from reduced retry decision overhead

The optimization is particularly effective for the common case of successful requests (2xx codes) where the function can short-circuit after the `>= 500` check without any list operations.
@codeflash-ai codeflash-ai bot requested a review from mashraf-222 December 18, 2025 02:44
@codeflash-ai codeflash-ai bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Dec 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant