Introduction#

Purgatory is naturally handling many circuits, so circuits are created using a factory that keep the default configuration for the circuits and for state.

Note that all the parameters of the factory are optionals.

The circuitbreaker will track for exceptions that can be raised in a portion of code, using a decorator or a async with context.

Example of a circuit breaker that is used with the httpx library:

import httpx
from purgatory import AsyncCircuitBreakerFactory

circuitbreaker = AsyncCircuitBreakerFactory(
   default_threshold=5,
   default_ttl=30,
   exclude=[
      (httpx.HTTPStatusError, lambda exc: exc.response.is_client_error),
   ]
)

The parameter default_threshold define the default value for all the failure threshold of created circuits.

The parameter default_ttl define the default number of seconds to keep the circuit opened when the threshold has been reached.

The parameter exclude is a list of exceptions, that can be excluded by the circuit breaker while detecting failure.

Now, we can use our circuit breaker to do an http call:

async with await circuitbreaker.get_breaker("www.example.com"):
   async with httpx.AsyncClient() as client:
       r = await client.get('https://www.example.com/')

At this stage, the method get_breaker will automatically register an circuit named www.example.com.

Note

If a circuit with this name has already been registered, it will be retrieved to restore it states and its configuration.

The default configuration can also be override:

async with await circuitbreaker.get_breaker("www.example.net", threshold=7, ttl=42):
   async with httpx.AsyncClient() as client:
       r = await client.get('https://www.example.net/')

Important

If another circuit breaker www.example.net is instanciated, with different threshold and ttl, then they are not taken into account.

The first circuit that register the circuit is configuring it.

Using a redis backend, updating the threshold or the ttl require to flush the keys.

Function can also be decorated as part of a circuit, first example, using a decorator.

@circuitbreaker("www.example.com")
async def get_page():
   async with httpx.AsyncClient() as client:
       r = await client.get('https://www.example.com/')

Note

Using a decorator may be elegant but have restriction on circuit name. Now they are completly static.