Lasier
A sync/async circuit breaker implementation in Python
Install / Use
/learn @luizalabs/LasierREADME
Lasier
A sync/async circuit breaker implementation
According to Nygard on your masterpiece book Release It!:
[...] circuit breakers protect overeager gadget hounds from burning their houses down. The principle is the same: detect excess usage, fail first, and open the circuit. More abstractly, the circuit breaker exists to allow one subsystem (an electrical circuit) to fail (excessive current draw, possibly from a short circuit) without destroying the entire system (the house). Furthermore, once the danger has passed, the circuit breaker can be reset to restore full function to the system.
Requirements
- Python >= 3.7
Instalation
Using pip:
pip install lasier
Usage
To use lasier circuit breaker you'll need a rule and a cache (the circuit state storage) instance
Rule
A Rule is the mechanism that define where circuit will open or close.
MaxFailuresRule
Rule to open circuit based on maximum number of failures
from lasier.circuit_breaker.rules import MaxFailuresRule
rule = MaxFailuresRule(
max_failures=500,
failure_cache_key='my_cb'
)
Arguments
| Argument | Definition | |----------|------------| | max_failures | Maximum number of errors | | failure_cache_key | Cache key where the number of errors is incremented |
PercentageFailuresRule
Rule to open circuit based on a percentage of failures
from lasier.circuit_breaker.rules import PercentageFailuresRule
rule = PercentageFailuresRule(
max_failures_percentage=60,
failure_cache_key='my_cb',
min_accepted_requests=100,
request_cache_key='my_cb_request'
)
Arguments
| Argument | Definition | |----------|------------| | max_failures_percentage | Maximum percentage of errors | | failure_cache_key | Cache key where the number of errors is incremented | | min_accepted_requests | Minimum number of requests accepted to not open circuit breaker | | request_cache_key | Cache key where the number of requests is incremented |
Circuit Breaker
You can use the Lasier circuit breaker with a context_manager f.ex:
from lasier.circuit_breaker.sync import CircuitBreaker
...
def some_protected_func():
with CircuitBreaker(
rule=rule,
cache=cache,
failure_exception=ValueError,
catch_exceptions=(KeyError, TypeError)
):
# some process
Or a decorator, f.ex:
from lasier.circuit_breaker.asyncio import circuit_breaker
...
@circuit_breaker(
rule=rule,
cache=cache,
failure_exception=ValueError,
catch_exceptions=(KeyError, TypeError)
)
async def some_protected_func():
# some process
The sync and async implementations follow the same interface, so you only need to change the import path:
lasier.circuit_breaker.sync: for sync implementataionlasier.circuit_breaker.asyncio: for async implementataion
Arguments
| Argument | Definition | |----------|------------| | rule | Instance of class rule. | | cache | Instance of the circuit breaker state storage. | | failure_exception | Exception to be raised when it exceeds the maximum number of errors and when the circuit is open. | | failure_timeout | This value is set on first error. It is used to validate the number of errors by time. (seconds, default 60) | | circuit_timeout | Time that the circuit will be open. (seconds, default 60) | | catch_exceptions | List of exceptions catched to increase the number of errors. |
WARNING: The args
failure_timeoutandcircuit_timeoutwill be used on state storage commands so if you'll use libs that expects milliseconds instead of seconds ontimeoutarguments maybe you'll get yourself in trouble
Circuit state storage
Lasier works with a storage to register the current state of the circuit, number of failures, etc. That storage respects the follow interface:
from lasier.types import Timeout # Timeout = Optional[Union[int, float]]
class Storage:
def add(self, key: str, value: int, timeout: Timeout = None) -> None:
pass
def set(self, key: str, value: int, timeout: Timeout = None) -> None:
pass
def incr(self, key: str) -> int:
pass
def get(self, key: str) -> int:
pass
def expire(key: str, timeout: Timeout = None) -> None:
pass
def delete(self, key: str) -> None:
pass
def flushdb(self) -> None:
pass
For
asynccircuit breaker, lasier works with that same interface however with async syntax, f.ex:async def set(self, key=str, value=int, timeout=Optional[int])
So you can use any cache/storage that respects that interface.
Adapters
If you'll use Lasier with redis-py as cache, you can use lasier.adapters.caches.redis.RedisAdapter
from lasier.adapters.caches import RedisAdapter
from redis import Redis
cache = RedisAdapter(Redis(host='localhost', port=6479, db=0))
Implemented Adapters
| Lib | Adapter |
|--------------------| --------------------------------------------- |
| redis-py | lasier.adapters.caches.RedisAdapter |
| django-cache | lasier.adapters.caches.DjangoAdapter |
| django-cache-async | lasier.adapters.caches.DjangoAsyncAdapter |
| aiocache | lasier.adapters.caches.AiocacheAdapter |
Related Skills
node-connect
352.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
claude-opus-4-5-migration
111.1kMigrate prompts and code from Claude Sonnet 4.0, Sonnet 4.5, or Opus 4.1 to Opus 4.5
frontend-design
111.1kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
model-usage
352.0kUse CodexBar CLI local cost usage to summarize per-model usage for Codex or Claude, including the current (most recent) model or a full model breakdown. Trigger when asked for model-level usage/cost data from codexbar, or when you need a scriptable per-model summary from codexbar cost JSON.
