Retries#

pjrpc supports request retries based on response code or received exception using customizable backoff strategy. pjrpc provides several built-in backoff algorithms (see pjrpc.client.retry), but you can implement your own one like this:

import dataclasses as dc
import random
from pjrpc.client.retry import Backoff

@dc.dataclass(frozen=True)
class RandomBackoff(Backoff):
    def __call__(self) -> Iterator[float]:
        return (random.random() for _ in range(self.attempts))

Retry strategy can be configured for all client requests by passing a strategy to a client constructor as a retry_strategy argument.

The following example illustrate request retries api usage:

import asyncio
import random

import aiohttp

import pjrpc
from pjrpc.client.backend import aiohttp as pjrpc_client
from pjrpc.client.retry import AsyncRetryMiddleware, ExponentialBackoff, RetryStrategy


async def main():
    default_retry_strategy = RetryStrategy(
        exceptions={TimeoutError},
        backoff=ExponentialBackoff(attempts=3, base=1.0, factor=2.0, jitter=lambda n: random.gauss(mu=0.5, sigma=0.1)),
    )

    async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(total=0.2)) as session:
        async with pjrpc_client.Client(
            'http://localhost:8080/api/v1',
            session=session,
            middlewares=[AsyncRetryMiddleware(default_retry_strategy)],
        ) as client:
            response = await client.send(pjrpc.Request('sum', params=[1, 2], id=1))
            print(f"1 + 2 = {response.result}")

            result = await client.proxy.sum(1, 2)
            print(f"1 + 2 = {result}")


asyncio.run(main())