Skip to content

Commit 0000c52

Browse files
yoon-park-rlclaude
andauthored
feat(loadtest): add pool_check.py to verify shared sync connection pool (#822)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4ef3c65 commit 0000c52

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

loadtest/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,16 @@ client + server _request handling_ from provisioning.
2020
| `h2_single_conn.py` | Raw `httpx` HTTP/2 on a single warmed connection (50-request burst). |
2121
| `raw_fetch_test.py` | Raw `httpx` HTTP/1.1 keep-alive baseline. |
2222
| `alpn_check.py` | Confirms the origin negotiates `h2` via TLS ALPN. |
23+
| `pool_check.py` | Verifies that multiple sync `Runloop` instances share a single connection pool (transport object identity check, no real requests). |
2324

2425
The raw-transport probes compare httpx HTTP/2 multiplexing against HTTP/1.1
2526
directly — the same comparison that motivates the SDK's shared HTTP/2 pool.
2627
They're kept so the comparison stays reproducible.
2728

29+
`pool_check.py` is a lightweight sanity check (no API key, no real requests) that
30+
confirms multiple sync `Runloop` instances reuse a single underlying transport
31+
object — preventing file-descriptor exhaustion when many clients are instantiated.
32+
2833
```sh
2934
# Install the SDK from this checkout (editable):
3035
cd /path/to/api-client-python && uv sync

loadtest/pool_check.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Verify that multiple Runloop (sync) instances share a single connection pool.
2+
3+
Spins up N SDK instances and checks that the number of open connections to
4+
api.runloop.ai does not grow linearly with instance count — it should stay at
5+
one (or a small fixed number) because all instances share the same underlying
6+
httpx transport.
7+
8+
Usage:
9+
uv run python loadtest/pool_check.py
10+
11+
No API key is required — we only check the transport object identity and the
12+
OS-level connection count, not make real requests.
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import os
18+
import sys
19+
import resource
20+
21+
from runloop_api_client import Runloop
22+
23+
HOST = "api.runloop.ai"
24+
N = 20
25+
26+
27+
def main() -> None:
28+
fd_before = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
29+
print(f"FD limit: {fd_before}")
30+
print(f"Creating {N} Runloop instances...\n")
31+
32+
clients: list[Runloop] = []
33+
for _ in range(N):
34+
clients.append(Runloop(bearer_token=os.environ.get("RUNLOOP_API_KEY", "dummy")))
35+
36+
# All instances should reference the same shared transport object.
37+
transport_ids: set[int] = set()
38+
for c in clients:
39+
t = getattr(c._client, "_transport", None)
40+
if t is not None:
41+
transport_ids.add(id(t))
42+
43+
print(f"Distinct transport objects across {N} instances: {len(transport_ids)}")
44+
if len(transport_ids) == 1:
45+
print("PASS — all instances share one transport (connection pool).")
46+
else:
47+
print("FAIL — instances have separate transports; FD exhaustion is possible.")
48+
sys.exit(1)
49+
50+
# Close all clients.
51+
for c in clients:
52+
c.close()
53+
54+
print("\nDone.")
55+
56+
57+
if __name__ == "__main__":
58+
main()

0 commit comments

Comments
 (0)