How-To: Hot Config Reload¶
resilience.enabled reacts live to pico-ioc's hot refresh (pico-ioc >= 2.3.0):
data = {"resilience": {"enabled": True}}
container = init(modules=["pico_resilience", "pico_ioc.event_bus", "my_app"],
config=configuration(DictSource(data)))
data["resilience"]["enabled"] = False
container.refresh_config() # publishes ConfigChanged
# from this moment @retryable/@circuit_breaker/@timeout pass through
How it works¶
pico-ioc's contract (ADR-013): already-created singletons keep their values; consumers subscribe to ConfigChanged and re-read. pico-resilience ships ResilienceConfigRefresher, which updates the shared ResilienceSettings IN PLACE — every interceptor sees the new value on its next invocation.
Requirements and limits:
- An
EventBusmust be registered (modulepico_ioc.event_bus). Hot reload is on by default and FAILS FAST at startup if the bus is missing — a config knob that silently stops reloading is worse than a crash. Containers that do not want it opt out explicitly withresilience.hot_reload: false. - The refresh trigger lives outside: pico-actuator's
POST /actuator/refresh, a file watcher, or your own call tocontainer.refresh_config(). - Only
resilience.enabledis config. Per-method policies (attempts, thresholds, timeouts) are decorator arguments — code, not config — and do not hot-reload by design.
Validated end to end¶
tests/test_e2e_ecosystem.py runs the real stack in one container — a pico-fastapi upstream served through httpx.ASGITransport, a declarative pico-httpx client with @retryable stacked on the stub, and @cacheable memoizing the recovered result — and flips resilience.enabled live in both directions.