Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions loadtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ client + server _request handling_ from provisioning.
| `h2_single_conn.py` | Raw `httpx` HTTP/2 on a single warmed connection (50-request burst). |
| `raw_fetch_test.py` | Raw `httpx` HTTP/1.1 keep-alive baseline. |
| `alpn_check.py` | Confirms the origin negotiates `h2` via TLS ALPN. |
| `pool_check.py` | Verifies that multiple sync `Runloop` instances share a single connection pool (transport object identity check, no real requests). |

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

`pool_check.py` is a lightweight sanity check (no API key, no real requests) that
confirms multiple sync `Runloop` instances reuse a single underlying transport
object — preventing file-descriptor exhaustion when many clients are instantiated.

```sh
# Install the SDK from this checkout (editable):
cd /path/to/api-client-python && uv sync
Expand Down
60 changes: 60 additions & 0 deletions loadtest/pool_check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
"""Verify that multiple Runloop (sync) instances share a single connection pool.

Spins up N SDK instances and checks that the number of open connections to
api.runloop.ai does not grow linearly with instance count — it should stay at
one (or a small fixed number) because all instances share the same underlying
httpx transport.

Usage:
uv run python loadtest/pool_check.py

No API key is required — we only check the transport object identity and the
OS-level connection count, not make real requests.
"""

from __future__ import annotations

import os
import resource
import subprocess
import sys

from runloop_api_client import Runloop

HOST = "api.runloop.ai"
N = 20



def main() -> None:
fd_before = resource.getrlimit(resource.RLIMIT_NOFILE)[0]
print(f"FD limit: {fd_before}")
print(f"Creating {N} Runloop instances...\n")

clients: list[Runloop] = []
for i in range(N):
clients.append(Runloop(bearer_token=os.environ.get("RUNLOOP_API_KEY", "dummy")))

# All instances should reference the same shared transport object.
transport_ids = set()
for c in clients:
t = getattr(c._client, "_transport", None)
if t is not None:
transport_ids.add(id(t))

print(f"Distinct transport objects across {N} instances: {len(transport_ids)}")
if len(transport_ids) == 1:
print("PASS — all instances share one transport (connection pool).")
else:
print("FAIL — instances have separate transports; FD exhaustion is possible.")
sys.exit(1)

# Close all clients.
for c in clients:
c.close()

print("\nDone.")


if __name__ == "__main__":
main()
Loading