Skip to content

Client

Client entities for generating requests with retry and timeout logic.

Client components for request handling simulation.

This package provides client abstractions with timeout handling, retry policies, connection pooling, and response tracking.

Client

Client(
    name: str,
    target: Entity,
    timeout: float | None = None,
    retry_policy: RetryPolicy | None = None,
    on_success: Callable[[Event, Event], None]
    | None = None,
    on_failure: Callable[[Event, str], None] | None = None,
)

Bases: Entity

Client that sends requests and handles responses.

Provides a clean interface for making requests to target entities with support for timeouts and retry policies. Tracks in-flight requests and collects statistics for analysis.

The client handles the request lifecycle: 1. Send request to target 2. Wait for response (with optional timeout) 3. On success: invoke success callback, record stats 4. On timeout: retry according to policy, or invoke failure callback

Attributes:

Name Type Description
name

Client identifier for logging.

target Entity

The entity to send requests to.

timeout float | None

Request timeout in seconds (None = no timeout).

retry_policy RetryPolicy

Policy for retry behavior.

Initialize the client.

Parameters:

Name Type Description Default
name str

Client identifier.

required
target Entity

Entity to send requests to.

required
timeout float | None

Request timeout in seconds (None = no timeout).

None
retry_policy RetryPolicy | None

Retry policy for failed requests (default NoRetry).

None
on_success Callable[[Event, Event], None] | None

Callback(request, response) on successful response.

None
on_failure Callable[[Event, str], None] | None

Callback(request, reason) on final failure.

None

Raises:

Type Description
ValueError

If timeout is negative.

target property

target: Entity

The target entity for requests.

timeout property

timeout: float | None

Request timeout in seconds.

retry_policy property

retry_policy: RetryPolicy

The retry policy for failed requests.

in_flight_count property

in_flight_count: int

Number of requests currently in flight.

average_response_time property

average_response_time: float

Average response time in seconds.

stats property

stats: ClientStats

Frozen snapshot of client statistics.

send_request

send_request(
    payload: Any = None,
    event_type: str = "request",
    on_success: Callable[[Event, Event], None]
    | None = None,
    on_failure: Callable[[Event, str], None] | None = None,
) -> Event

Create a request event to send to the target.

Creates an event that, when scheduled, will send a request to the target entity. The event is ready to be scheduled with sim.schedule().

Parameters:

Name Type Description Default
payload Any

Optional payload data for the request.

None
event_type str

Type string for the request event.

'request'
on_success Callable[[Event, Event], None] | None

Override success callback for this request.

None
on_failure Callable[[Event, str], None] | None

Override failure callback for this request.

None

Returns:

Type Description
Event

Event ready to be scheduled.

handle_event

handle_event(
    event: Event,
) -> (
    Generator[float, None, list[Event] | Event | None]
    | list[Event]
    | Event
    | None
)

Handle client events.

Processes: - Outgoing requests: forward to target with tracking - Timeout events: handle timeout and retry - Response events: complete the request

Parameters:

Name Type Description Default
event Event

The event to handle.

required

Returns:

Type Description
Generator[float, None, list[Event] | Event | None] | list[Event] | Event | None

Events to schedule or generator for async processing.

get_response_time_percentile

get_response_time_percentile(percentile: float) -> float

Calculate a percentile of observed response times.

Parameters:

Name Type Description Default
percentile float

Percentile value between 0 and 1.

required

Returns:

Type Description
float

Response time at the given percentile, or 0 if no data.

ClientStats dataclass

ClientStats(
    requests_sent: int = 0,
    responses_received: int = 0,
    timeouts: int = 0,
    retries: int = 0,
    failures: int = 0,
)

Frozen snapshot of Client statistics.

Connection dataclass

Connection(
    id: int,
    created_at: Instant,
    last_used_at: Instant,
    is_active: bool = False,
)

Represents a connection from the pool.

Attributes:

Name Type Description
id int

Unique connection identifier.

created_at Instant

When the connection was created.

last_used_at Instant

When the connection was last used.

is_active bool

Whether the connection is currently in use.

ConnectionPool

ConnectionPool(
    name: str,
    target: Entity,
    min_connections: int = 0,
    max_connections: int = 10,
    connection_timeout: float = 5.0,
    idle_timeout: float = 60.0,
    connection_latency: LatencyDistribution | None = None,
    on_acquire: Callable[[Connection], None] | None = None,
    on_release: Callable[[Connection], None] | None = None,
    on_timeout: Callable[[], None] | None = None,
)

Bases: Entity

Manages a pool of reusable connections to a target.

The connection pool maintains warm connections that can be reused across requests, reducing the overhead of establishing new connections. When the pool is exhausted, new connections are created up to the maximum limit. Requests block when at capacity until a connection is released.

Attributes:

Name Type Description
name

Pool identifier for logging.

target Entity

The entity to connect to.

min_connections int

Minimum number of warm connections to maintain.

max_connections int

Maximum number of total connections.

connection_timeout float

Time to wait for a connection before failing.

idle_timeout float

Time before idle connections are closed.

connection_latency float

Time to establish a new connection.

Initialize the connection pool.

Parameters:

Name Type Description Default
name str

Pool identifier.

required
target Entity

Entity to connect to.

required
min_connections int

Minimum pool size (default 0).

0
max_connections int

Maximum pool size (default 10).

10
connection_timeout float

Wait timeout in seconds (default 5.0).

5.0
idle_timeout float

Idle connection timeout in seconds (default 60.0).

60.0
connection_latency LatencyDistribution | None

Time to create a new connection.

None
on_acquire Callable[[Connection], None] | None

Callback when connection is acquired.

None
on_release Callable[[Connection], None] | None

Callback when connection is released.

None
on_timeout Callable[[], None] | None

Callback when acquisition times out.

None

Raises:

Type Description
ValueError

If parameters are invalid.

target property

target: Entity

The target entity for connections.

min_connections property

min_connections: int

Minimum number of connections to maintain.

max_connections property

max_connections: int

Maximum number of connections allowed.

connection_timeout property

connection_timeout: float

Timeout in seconds for acquiring a connection.

idle_timeout property

idle_timeout: float

Timeout in seconds before idle connections are closed.

active_connections property

active_connections: int

Number of connections currently in use.

idle_connections property

idle_connections: int

Number of idle connections in the pool.

total_connections property

total_connections: int

Total number of open connections.

pending_requests property

pending_requests: int

Number of requests waiting for a connection.

stats property

stats: ConnectionPoolStats

Frozen snapshot of connection pool statistics.

average_wait_time property

average_wait_time: float

Average time spent waiting for a connection.

acquire

acquire() -> Generator[float, None, Connection]

Acquire a connection from the pool.

This generator yields while waiting for a connection to become available or while creating a new connection. It returns a Connection object that must be released with release() when done.

Yields:

Type Description
float

Time to wait (for connection creation or waiting in queue).

Returns:

Type Description
Connection

A Connection object.

Raises:

Type Description
TimeoutError

If no connection becomes available within timeout.

release

release(connection: Connection) -> list[Event]

Release a connection back to the pool.

The connection becomes available for reuse. If there are waiters, the connection is immediately given to the next waiter.

Parameters:

Name Type Description Default
connection Connection

The connection to release.

required

Returns:

Type Description
list[Event]

Events to schedule (for idle timeout checks).

handle_event

handle_event(
    event: Event,
) -> (
    Generator[float, None, list[Event] | Event | None]
    | list[Event]
    | Event
    | None
)

Handle pool events.

Processes: - Idle timeout events: close connections that have been idle too long

Parameters:

Name Type Description Default
event Event

The event to handle.

required

Returns:

Type Description
Generator[float, None, list[Event] | Event | None] | list[Event] | Event | None

Events to schedule or None.

warmup

warmup() -> Event

Create minimum connections during simulation startup.

Returns an event that, when scheduled, will create the minimum number of connections specified in min_connections.

Returns:

Type Description
Event

Event to schedule for warmup.

close_all

close_all() -> None

Close all connections in the pool.

This should be called during simulation teardown.

ConnectionPoolStats dataclass

ConnectionPoolStats(
    connections_created: int = 0,
    connections_closed: int = 0,
    acquisitions: int = 0,
    releases: int = 0,
    timeouts: int = 0,
    total_wait_time: float = 0.0,
)

Frozen snapshot of ConnectionPool statistics.

PooledClient

PooledClient(
    name: str,
    connection_pool: ConnectionPool,
    timeout: float | None = None,
    retry_policy: RetryPolicy | None = None,
    on_success: Callable[[Event, Event], None]
    | None = None,
    on_failure: Callable[[Event, str], None] | None = None,
)

Bases: Entity

Client that uses connection pooling for requests.

Acquires a connection from the pool before each request, ensuring connections are reused efficiently. Supports the same timeout and retry features as the basic Client.

Attributes:

Name Type Description
name

Client identifier for logging.

connection_pool ConnectionPool

Pool to acquire connections from.

timeout float | None

Request timeout in seconds (None = no timeout).

retry_policy RetryPolicy

Policy for retry behavior.

Initialize the pooled client.

Parameters:

Name Type Description Default
name str

Client identifier.

required
connection_pool ConnectionPool

Pool to acquire connections from.

required
timeout float | None

Request timeout in seconds (None = no timeout).

None
retry_policy RetryPolicy | None

Retry policy for failed requests (default NoRetry).

None
on_success Callable[[Event, Event], None] | None

Callback(request, response) on successful response.

None
on_failure Callable[[Event, str], None] | None

Callback(request, reason) on final failure.

None

Raises:

Type Description
ValueError

If timeout is negative.

connection_pool property

connection_pool: ConnectionPool

The connection pool used for requests.

timeout property

timeout: float | None

Request timeout in seconds.

retry_policy property

retry_policy: RetryPolicy

The retry policy for failed requests.

in_flight_count property

in_flight_count: int

Number of requests currently in flight.

average_response_time property

average_response_time: float

Average response time in seconds.

stats property

stats: PooledClientStats

Frozen snapshot of pooled client statistics.

set_clock

set_clock(clock: Clock) -> None

Inject clock and propagate to connection pool.

send_request

send_request(
    payload: Any = None,
    event_type: str = "request",
    on_success: Callable[[Event, Event], None]
    | None = None,
    on_failure: Callable[[Event, str], None] | None = None,
) -> Event

Create a request event to send through the pool.

Creates an event that, when scheduled, will acquire a connection from the pool and send a request to the target. The event is ready to be scheduled with sim.schedule().

Parameters:

Name Type Description Default
payload Any

Optional payload data for the request.

None
event_type str

Type string for the request event.

'request'
on_success Callable[[Event, Event], None] | None

Override success callback for this request.

None
on_failure Callable[[Event, str], None] | None

Override failure callback for this request.

None

Returns:

Type Description
Event

Event ready to be scheduled.

handle_event

handle_event(
    event: Event,
) -> (
    Generator[float, None, list[Event] | Event | None]
    | list[Event]
    | Event
    | None
)

Handle client events.

Processes: - Outgoing requests: acquire connection, forward to target - Timeout events: handle timeout and retry - Response events: complete the request, release connection

Parameters:

Name Type Description Default
event Event

The event to handle.

required

Returns:

Type Description
Generator[float, None, list[Event] | Event | None] | list[Event] | Event | None

Events to schedule or generator for async processing.

get_response_time_percentile

get_response_time_percentile(percentile: float) -> float

Calculate a percentile of observed response times.

Parameters:

Name Type Description Default
percentile float

Percentile value between 0 and 1.

required

Returns:

Type Description
float

Response time at the given percentile, or 0 if no data.

PooledClientStats dataclass

PooledClientStats(
    requests_sent: int = 0,
    responses_received: int = 0,
    timeouts: int = 0,
    retries: int = 0,
    failures: int = 0,
    connection_wait_timeouts: int = 0,
)

Frozen snapshot of PooledClient statistics.

DecorrelatedJitter

DecorrelatedJitter(
    max_attempts: int, base_delay: float, max_delay: float
)

AWS-style decorrelated jitter backoff.

A more sophisticated jitter algorithm that decorrelates retry times across multiple clients. This helps prevent synchronized retries (thundering herd) more effectively than simple jitter.

The delay formula is

delay = random(base_delay, min(max_delay, previous_delay * 3))

Reference

https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/

Attributes:

Name Type Description
max_attempts int

Maximum number of total attempts.

base_delay float

Minimum delay between attempts.

max_delay float

Maximum delay cap.

Initialize decorrelated jitter policy.

Parameters:

Name Type Description Default
max_attempts int

Maximum total attempts (must be >= 1).

required
base_delay float

Minimum delay in seconds (must be > 0).

required
max_delay float

Maximum delay cap in seconds (must be >= base_delay).

required

Raises:

Type Description
ValueError

If parameters are invalid.

max_attempts property

max_attempts: int

Maximum number of total attempts.

base_delay property

base_delay: float

Minimum delay between attempts.

max_delay property

max_delay: float

Maximum delay cap.

should_retry

should_retry(
    attempt: int, error: Exception | None = None
) -> bool

Check if another retry should be attempted.

Parameters:

Name Type Description Default
attempt int

Current attempt number (1-based).

required
error Exception | None

The error that occurred (ignored for this policy).

None

Returns:

Type Description
bool

True if attempt < max_attempts.

get_delay

get_delay(attempt: int) -> float

Calculate decorrelated jitter delay.

Uses the formula: random(base, min(max, prev * 3))

Parameters:

Name Type Description Default
attempt int

Current attempt number (1-based).

required

Returns:

Type Description
float

Delay in seconds for this attempt.

reset

reset() -> None

Reset the previous delay to base_delay.

Call this when starting a new request sequence.

ExponentialBackoff

ExponentialBackoff(
    max_attempts: int,
    initial_delay: float,
    max_delay: float,
    multiplier: float = 2.0,
    jitter: float = 0.0,
)

Exponential backoff with optional jitter.

Increases delay exponentially between retries, with optional random jitter to prevent thundering herd problems. This is the recommended retry strategy for most distributed systems.

The delay formula is

delay = min(initial_delay * (multiplier ^ (attempt - 1)), max_delay) delay += random(0, jitter)

Attributes:

Name Type Description
max_attempts int

Maximum number of total attempts.

initial_delay float

Delay for the first retry.

max_delay float

Maximum delay cap.

multiplier float

Factor to multiply delay by each attempt.

jitter float

Maximum random delay to add.

Initialize exponential backoff policy.

Parameters:

Name Type Description Default
max_attempts int

Maximum total attempts (must be >= 1).

required
initial_delay float

Initial delay in seconds (must be > 0).

required
max_delay float

Maximum delay cap in seconds (must be >= initial_delay).

required
multiplier float

Delay multiplier per attempt (must be >= 1).

2.0
jitter float

Maximum random jitter to add (must be >= 0).

0.0

Raises:

Type Description
ValueError

If parameters are invalid.

max_attempts property

max_attempts: int

Maximum number of total attempts.

initial_delay property

initial_delay: float

Initial delay for first retry.

max_delay property

max_delay: float

Maximum delay cap.

multiplier property

multiplier: float

Delay multiplier per attempt.

jitter property

jitter: float

Maximum random jitter.

should_retry

should_retry(
    attempt: int, error: Exception | None = None
) -> bool

Check if another retry should be attempted.

Parameters:

Name Type Description Default
attempt int

Current attempt number (1-based).

required
error Exception | None

The error that occurred (ignored for this policy).

None

Returns:

Type Description
bool

True if attempt < max_attempts.

get_delay

get_delay(attempt: int) -> float

Calculate exponential backoff delay with jitter.

Parameters:

Name Type Description Default
attempt int

Current attempt number (1-based).

required

Returns:

Type Description
float

Delay in seconds for this attempt.

FixedRetry

FixedRetry(max_attempts: int, delay: float)

Retry with a fixed delay between attempts.

Simple retry strategy that waits the same amount of time between each retry attempt. Good for transient failures where the delay doesn't need to increase.

Attributes:

Name Type Description
max_attempts int

Maximum number of total attempts (including initial).

delay float

Fixed delay in seconds between attempts.

Initialize fixed retry policy.

Parameters:

Name Type Description Default
max_attempts int

Maximum total attempts (must be >= 1).

required
delay float

Delay in seconds between attempts (must be >= 0).

required

Raises:

Type Description
ValueError

If max_attempts < 1 or delay < 0.

max_attempts property

max_attempts: int

Maximum number of total attempts.

delay property

delay: float

Fixed delay between attempts.

should_retry

should_retry(
    attempt: int, error: Exception | None = None
) -> bool

Check if another retry should be attempted.

Parameters:

Name Type Description Default
attempt int

Current attempt number (1-based).

required
error Exception | None

The error that occurred (ignored for this policy).

None

Returns:

Type Description
bool

True if attempt < max_attempts.

get_delay

get_delay(attempt: int) -> float

Get the fixed delay.

Parameters:

Name Type Description Default
attempt int

Current attempt number (ignored).

required

Returns:

Type Description
float

The configured fixed delay.

NoRetry

Never retry failed requests.

Use when requests should fail immediately without retry attempts. This is the default policy when no retry is configured.

should_retry

should_retry(
    attempt: int, error: Exception | None = None
) -> bool

Never retry.

Parameters:

Name Type Description Default
attempt int

The attempt number (ignored).

required
error Exception | None

The error (ignored).

None

Returns:

Type Description
bool

Always False.

get_delay

get_delay(attempt: int) -> float

No delay since we never retry.

Parameters:

Name Type Description Default
attempt int

The attempt number (ignored).

required

Returns:

Type Description
float

Always 0.

RetryPolicy

Bases: Protocol

Protocol for retry behavior strategies.

Implementations determine whether to retry a failed request and how long to wait before the next attempt.

should_retry

should_retry(
    attempt: int, error: Exception | None = None
) -> bool

Determine if another retry attempt should be made.

Parameters:

Name Type Description Default
attempt int

The attempt number (1 = first attempt, 2 = first retry, etc.).

required
error Exception | None

The error that caused the failure, if any.

None

Returns:

Type Description
bool

True if another attempt should be made.

get_delay

get_delay(attempt: int) -> float

Get the delay before the next retry attempt.

Parameters:

Name Type Description Default
attempt int

The attempt number (1 = first attempt, 2 = first retry, etc.).

required

Returns:

Type Description
float

Delay in seconds before the next attempt.