Download the PHP package gohany/circuitbreaker-symfony-bundle without Composer
On this page you can find all versions of the php package gohany/circuitbreaker-symfony-bundle. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download gohany/circuitbreaker-symfony-bundle
More information about gohany/circuitbreaker-symfony-bundle
Files in gohany/circuitbreaker-symfony-bundle
Package circuitbreaker-symfony-bundle
Short Description Symfony bundle wiring for Gohany/Circuitbreaker (PHP 7.4, Symfony 5.4+).
License MIT
Informations about the package circuitbreaker-symfony-bundle
Gohany CircuitBreaker Symfony Bundle
This bundle wires gohany/circuitbreaker into Symfony, with first-class support for:
- Resilience pipelines (bulkhead → circuit breaker → retry)
- Bulkheads
- fixed concurrency caps
- percent-based caps (lane shares)
- weighted caps (priority lanes)
- optional soft borrowing (ignore lane caps until the pool is under load)
- Doctrine DBAL middleware
- run a pipeline around connect() (gate new connections)
- run a pipeline around queries (observe failures / contribute to circuit)
This README is intentionally recipe-first: pick the implementation that matches the problem you’re solving.
Install
Concepts (what you’re wiring)
- A bulkhead limits concurrency (globally and optionally per “lane”).
- A circuit breaker denies or probes work when downstream is unhealthy.
- A retry policy re-attempts work under controlled rules.
- A pipeline is an ordered stack of stages (bulkhead → circuit breaker → retry) applied around an operation.
The bundle focuses on Symfony configuration + service wiring. The underlying behaviour lives in the core library (gohany/circuitbreaker).
Quickstart (minimal config)
1) Register a Redis client adapter service
2) Configure one pool and one pipeline
Redis client service
The bundle expects a service that implements:
Gohany\Circuitbreaker\Contracts\RedisClientInterface
If you use ext-redis (\Redis), you can register the adapter from the core lib:
Configuration (profiles + shared pools)
Symfony already supports per-environment config (e.g. config/packages/prod/…).
On top of that, this bundle supports profiles selected by env var:
- default env var:
GOHANY_CB_PROFILE - default profile:
default
That makes it easy to run different pipelines in api vs worker while still sharing
the same Redis-backed pool ids, so all processes cooperate on the same concurrency budget.
Example
Different profile, same pool id (shared capacity)
All processes that point at the same Redis and the same pool id share the same
global concurrency cap and lane caps.
Doctrine semantics (connect-gate, query-observe)
Use connect_pipeline to block new connections under load / circuit open.
Use query_pipeline to record failures and drive circuit state, without necessarily blocking already-open connections.
This matches the pattern:
block connecting, but once connected do not attempt to block; failed queries contribute to circuit state
Doctrine composed lanes (parent share + child priority)
Doctrine DBAL middleware can compose multiple acquisitions per action:
- primary lane (existing behavior, from
connect_*/query_*) - optional routing lanes (extra parent/child acquisitions resolved from route/path patterns)
Notes:
- If
routing_lanesis not configured, behavior is unchanged. - If
request_stackis unavailable (CLI/worker), routing lanes are skipped and only the primary lane is used. - Acquisitions are applied in order: primary, then extra lanes.
Common implementation recipes
1) “Protect the DB from connection storms” (connect-gate)
Use a bulkhead + circuit breaker around connect().
- Pros: prevents stampedes and keeps pool size sane under load
- Cons: does not stop already-open connections from issuing queries
2) “Observe query failures to drive circuit state” (query-observe)
Use a circuit breaker around query execution without a bulkhead.
- Pros: circuit reacts to real query outcomes
- Cons: doesn’t add a concurrency cap by itself
3) “Keep API fast while workers drain in the background” (profiles)
Run different profiles per process type while sharing the same Redis-backed pool IDs.
Typical split:
- API: allow
auth.login,payments.chargemore capacity - Worker: allow
jobs.billingmore capacity
Select profile via GOHANY_CB_PROFILE (default env var name).
4) Lane naming strategies (how to get priority lanes)
Lanes are just strings. Good patterns:
- Operation names:
payments.charge,auth.login - Transport/type:
db.connect,db.query,http.payments - Tenant-aware (only if you really want per-tenant lane caps):
tenant:123:db.query
Rule of thumb: prefer stable, low-cardinality lanes. If you need tenant isolation, do it deliberately.
5) HTTP routes: tie a controller to a pool/lane (bulkhead)
If your “routes” are Symfony HTTP routes, the simplest mapping is:
- pool = the shared dependency you’re protecting (e.g.
db-main) - lane = a stable route identifier (often the Symfony route name)
This bundle supports an opt-in controller docblock tag:
If you omit lane, it defaults to Symfony’s _route value (the route name):
That gives you “bulkheads by route” without needing to manually pass lane strings around.
Pool policy examples (fixed number vs percent vs weighted)
All three pool modes are supported in config:
Notes:
mode: weightedis typically what you want for “priority lanes”.soft_borrow_utilization_thresholdlets low-priority lanes borrow when there’s plenty of capacity, but enforces lane preference under load.
“Sub-lanes” (parent share + child weights)
The underlying bulkhead lane policy is flat (a lane is just a string). There isn’t a first-class “lane hierarchy” in PoolPolicy.
To model “40% of the DB pool is reserved for hydra traffic, and within hydra the charges route is highest priority”, compose two bulkheads:
1) A parent pool that allocates a share of the global budget to a coarse lane (e.g. hydra).
2) A child pool that allocates that share among routes using mode: weighted.
Because this bundle’s controller integration can read multiple @Bulkhead(...) tags, you can acquire both permits per request.
Example (1800 total DB-concurrency, hydra gets ~40% = 720, charges is top priority within hydra):
Controller mapping:
If you want the child lane to default to the Symfony route name, omit lane on the second tag.
6) Retry strategies
You have two different “retry” integration points:
A) Pipeline-stage retries (YAML-friendly)
In pipeline stages, configure a retry stage:
This uses core Gohany\Circuitbreaker\Resilience\RtryRetryMiddleware.
B) Circuit-breaker retries (object-based policies)
If you’re using the core circuit breaker directly (e.g. HTTP circuit breaker services), a recommended approach is to provide retries via your circuit policy by implementing RetrySpecProviderInterface.
See Resources\\config\\services.php in this bundle and vendor\\gohany\\circuitbreaker\\examples.md in the core library for examples.
7) HTTP client: decorate PSR-18
If you have a PSR-18 client (e.g. Symfony’s Psr18Client), you can decorate it with circuit breaking.
- Bundle-provided convenience wrapper:
Gohany\CircuitBreakerSymfonyBundle\Http\CircuitBreakerHttpClient - Core implementation:
Gohany\Circuitbreaker\Defaults\Http\CircuitBreakingPsr18Client
For wiring examples, see Resources\\config\\services.php.
8) Multi-circuit requests (layered protection)
Use this when a single request should coordinate multiple circuits, e.g.:
- Provider reliability circuit (
payments_http) - Fraud/lockout circuit (
payments_fraud)
See the “multi-circuit PSR-18 client” example in Resources\\config\\services.php.
9) Observability: emit events to logs/metrics
The core library uses an EmitterInterface (gohany.circuitbreaker.emitter service). Replace it in your app to:
- count
bulkhead.acquire/bulkhead.reject - track
circuit.open/circuit.half_open/circuit.closed - track retry attempts (
retry.attempt,retry.give_up)
This bundle registers a minimal no-op emitter by default; production apps should replace/decorate it.
10) Storage backends: Redis vs PDO
This bundle’s pool/bulkhead story is Redis-first because it’s designed for multi-process shared capacity.
For circuit breaker state/history stores, the core library supports multiple backends (including Redis and PDO). If you want SQL-backed state/history (auditable, queryable), wire the PDO stores from the core library.
See:
vendor\\gohany\\circuitbreaker\\examples.md→ “Storage backends”Resources\\config\\services.php→ “PDO-backed state + history stores”
More examples
- Core library cookbook:
vendor\\gohany\\circuitbreaker\\examples.md - Bundle wiring reference (PHP DI):
Resources\\config\\services.php
All versions of circuitbreaker-symfony-bundle with dependencies
gohany/circuitbreaker Version v1.1.3
symfony/framework-bundle Version ^5.4 || ^6.0 || ^7.0
symfony/dependency-injection Version ^5.4 || ^6.0 || ^7.0
symfony/config Version ^5.4 || ^6.0 || ^7.0
symfony/console Version ^5.4 || ^6.0 || ^7.0
symfony/http-client-contracts Version ^2.5 || ^3.0