GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — `bm_fastapi` intermittently fails with `httpx.PoolTimeout` under load · Issue #508 · python/pyperformance

bm_fastapi intermittently fails with httpx.PoolTimeout under load #508

New issue
Copy link
New issue
Copy link
Open
#509
Open
bm_fastapi intermittently fails with httpx.PoolTimeout under load#508
#509
Copy link

Description

@sendaoYan
sendaoYan
opened on Nov 9, 2026
Issue body actions

Summary

The fastapi benchmark (bm_fastapi) intermittently fails with
httpx.PoolTimeout when the machine running the benchmark is under heavy load.
This is a separate problem from the proxy-related 503 (issue #504).

Environment

  • pyperformance 1.14.0
  • Python 3.10
  • fastapi==0.121.0, httpx==0.28.1, uvicorn==0.38.0
  • No proxy involved (trust_env=False / no HTTP_PROXY)

How to reproduce

The benchmark fires CONCURRENCY = 150 concurrent requests against its own
uvicorn server. Reproduce deterministically by adding an artificial per-request
delay (simulating a slow server) and running 150 concurrent requests through an
httpx.AsyncClient with the default pool:

import asyncio, httpx

CONCURRENCY = 150

async def bench(url):
    async with httpx.AsyncClient() as client:  # default max_connections=100
        tasks = [client.get(f"{url}/items/{i}") for i in range(CONCURRENCY)]
        await asyncio.gather(*tasks)

With a server that takes ~6 seconds per response:

  • default pool (100 connections) → httpx.PoolTimeout after ~5.1s
  • limits=httpx.Limits(max_connections=150) → succeeds (~7.1s)

Actual result

httpx.PoolTimeout

The benchmark dies:

RuntimeError: Benchmark died
ERROR: Benchmark fastapi failed: Benchmark died

Root cause

CONCURRENCY = 150, but the httpx.AsyncClient uses httpx's default
max_connections = 100. The 50 requests that exceed the pool size wait in the
pool's queue for a connection to free up. httpx's default pool timeout is 5
seconds (httpx.Timeout(timeout=5.0)), so when the in-flight requests take
longer than 5 seconds (which is exactly when the machine is under load), the 50
queued requests exceed the pool timeout and raise httpx.PoolTimeout.

Expected behavior

The benchmark should complete regardless of how loaded the machine is, because
its own concurrency is a fixed constant: the connection pool should be sized to
match it.

Suggested fix

Size the connection pool to the benchmark's concurrency:

async with httpx.AsyncClient(
    limits=httpx.Limits(max_connections=CONCURRENCY),
) as client:

This removes the queuing entirely, so no request can exceed the pool timeout.

Scope

Only bm_fastapi is affected: it is the benchmark that combines a fixed
CONCURRENCY (150) larger than httpx's default pool (100) with a
httpx.AsyncClient created without explicit pool limits.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402503 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.