Ganesha: a circuit breaker for PHP service calls
:elephant: A Circuit Breaker pattern implementation for PHP applications.
At a glance
- What is it?
- Ganesha is a PHP implementation of the circuit breaker pattern, wrapping a downstream call in a closed, open or half open state so that a failing dependency stops being called. It keeps its statistics in a pluggable storage adapter, ships two strategies, rate and count, and integrates with Guzzle as middleware.
- Who is it for?
- Adopt Ganesha if you run PHP services that call flaky dependencies and want per dependency failure tracking with a shared storage, especially since the Guzzle middleware means an existing client can be protected with one line.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 126 days ago.
- What is it written in?
- Mainly PHP, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A breaker around one downstream service, not around your app
Ganesha is a PHP implementation of the circuit breaker pattern, with multi strategies to avoid cascading failures and support for various storages to record statistics. The pattern it implements is the one described in Martin Fowler's article, and the README says Ganesha follows the states and transitions described there faithfully.
The scope is narrow and deliberate. Each method receives a string, named $service in the examples, to identify the service, and the README is explicit that $service is the service name of the API, the endpoint name or similar, and that Ganesha detects system failure for each $service separately. So a breaker is per dependency, and the statistics for one dependency never mix with another's.
The integration surface is three calls:
$ganesha->isAvailable($service);
$ganesha->success($service);
$ganesha->failure($service);isAvailable returns true if the circuit state is Closed, and false otherwise. The other two report the outcome of the call you just made.
That is the whole API, and it is the reason the library can be introduced into an existing codebase without a refactor. The README says you can easily integrate Ganesha into your existing code base because it provides just simple interfaces, and that the Guzzle middleware behaves transparently, so the existing HTTP client keeps its shape.
The README also describes the project as one of the circuit breaker implementations in PHP that is actively developed, production-ready, well-tested and well-documented, and those are the maintainer's own claims rather than independently checked facts, so the rest of this article sticks to what the code and the documentation actually show.
The builder decides the strategy, the adapter and the thresholds
Everything is configured through a builder, and the usage example shows the whole configuration surface at once:
$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
->adapter(new Ackintosh\Ganesha\Storage\Adapter\Redis($redis))
->failureRateThreshold(50)
->intervalToHalfOpen(10)
->minimumRequests(10)
->timeWindow(30)
->build();
$service = 'external_api';
if (!$ganesha->isAvailable($service)) {
die('external api is not available');
}
try {
echo \Api::send($request)->getBody();
$ganesha->success($service);
} catch (\Api\RequestTimedOutException $e) {
// If an error occurred, it must be recorded as failure.
$ganesha->failure($service);
die($e->getMessage());
}The structure of that snippet is the intended usage pattern. You check availability before you call, you report success after the call returns, and you report failure in the catch block. The comment in the example, that an error must be recorded as failure, is the instruction that matters most in practice, because a call that throws and is not recorded leaves the breaker blind to a failure that just happened.
The builder starts with a strategy, withRateStrategy() here and withCountStrategy() elsewhere, and takes a storage adapter. The four numeric settings are the thresholds that decide when the breaker trips, and they are documented in the strategies section: failureRateThreshold as a percentage, minimumRequests as a floor on traffic, timeWindow as the evaluation interval, and intervalToHalfOpen as the delay before the breaker probes the service again.
The API is plain PHP with no framework coupling, which makes it usable from anything that speaks PSR style HTTP, not only from a Laravel or Symfony application.
Two strategies: a failure rate over a window, or a raw count
The strategies section is where the behavioural differences show up, and the two strategies are the main design decision you make.
The rate strategy evaluates a percentage of failures over a time window. The configuration is annotated in the README, and the annotations matter:
$ganesha = Ackintosh\Ganesha\Builder::withRateStrategy()
// The interval in time (seconds) that evaluate the thresholds.
->timeWindow(30)
// The failure rate threshold in percentage that changes CircuitBreaker's state to `OPEN`.
->failureRateThreshold(50)
// The minimum number of requests to detect failures.
// Even if `failureRateThreshold` exceeds the threshold,
// CircuitBreaker remains in `CLOSED` if `minimumRequests` is below this threshold.
->minimumRequests(10)
// The interval (seconds) to change CircuitBreaker's state from `OPEN` to `HALF_OPEN`.
->intervalToHalfOpen(5)
// The storage adapter instance to store various statistics to detect failures.
->adapter(new Ackintosh\Ganesha\Storage\Adapter\Memcached($memcached))
->build();The minimumRequests guard is the detail that makes the rate strategy usable. A single failed request in a quiet period would otherwise be a 100 percent failure rate, so the breaker stays closed until a minimum number of requests has been seen, and only then does the rate threshold apply.
The count strategy is the simpler alternative, and the disable example in the README refers to it as a Count strategy with a threshold of 3. A count strategy trips after a fixed number of failures and ignores traffic volume, which is the right choice for a dependency whose calls are infrequent and where every failure is meaningful, and the wrong choice for a high traffic endpoint where a fixed count is reached by chance.
There is also a note about the time window that the README describes as the storage adapter implementing either a sliding time window or another variant, which is the detail that decides how a burst of failures ages out of the statistics.
Events, a global disable switch and a reset
Ganesha exposes its state changes as events, and the mechanism is a static subscribe call with a callback that receives the event, the service and a message.
The documented events are Ganesha::EVENT_TRIPPED when the circuit state transitions to Open, Ganesha::EVENT_CALMED_DOWN when the state goes back to Closed, and Ganesha::EVENT_STORAGE_ERROR, which appears in the example switch. The example shows the intended use, wiring each event into a monitoring system:
$ganesha->subscribe(function ($event, $service, $message) {
switch ($event) {
case Ganesha::EVENT_TRIPPED:
\YourMonitoringSystem::warn(
"Ganesha has tripped! It seems that a failure has occurred in {$service}. {$message}."
);
break;
case Ganesha::EVENT_CALMED_DOWN:
\YourMonitoringSystem::info(
"The failure in {$service} seems to have calmed down :). {$message}."
);
break;
case Ganesha::EVENT_STORAGE_ERROR:
\YourMonitoringSystem::error($message);
break;
default:
break;
}
});The storage error event is the one worth wiring up carefully. If the storage backing the statistics is itself unavailable, the breaker cannot know anything, and an event is the only signal you get that your fault tolerance layer has lost its memory.
The disable switch is global and static. When Ganesha is disabled it keeps recording success and failure statistics, but it does not trip even if the failure count reaches the threshold, and isAvailable returns true. The README's example makes the point explicitly by recording three failures against a count threshold of 3 and then showing isAvailable returning bool(true).
The reset method clears the statistics saved in a storage, and it is the operational escape hatch: after you fix the dependency, or after a false positive, you can clear the counters rather than waiting for the window to age out.
Storage adapters, the Guzzle middleware and a runnable example
The storage layer is where the statistics live, and the adapters are pluggable, which is what lets several processes share one breaker state. The README shows Redis and Memcached adapters in different examples, and the repository layout adds more: there is a docker-compose.yml with services for memcached, redis and mongo, and the environment variables in the client service name the servers the example uses, GANESHA_EXAMPLE_SERVER, GANESHA_EXAMPLE_MEMCACHED, GANESHA_EXAMPLE_REDIS and GANESHA_EXAMPLE_MONGO.
The example is the thing to run first. The README points at an examples directory showing how Ganesha behaves when a failure occurs, says it is easily executable, and says all you need is Docker. The compose file builds a client from examples/client, mounts the repository into it and runs it with a working directory of /tmp/ganesha.
The integrations are listed in the table of contents and each is a separate section: Ganesha with Guzzle, Ganesha with OpenAPI Generator and Ganesha with Symfony HttpClient. The Guzzle one is the reason the README says the middleware behaves transparently, since a Guzzle client that already talks to a flaky API gains failure tracking and an open circuit without the call sites changing.
There is also a section on customizing storage keys, which matters when you share one Redis instance between applications, since the keys are how the statistics are namespaced per service.
The package is on Packagist as ackintosh/ganesha, the minimum PHP version is 8.1, and the licence is MIT. The repository carries psalm.xml for static analysis, phpunit.xml for tests, a .php-cs-fixer.dist.php for style, a docs directory and a CLAUDE.md.
Maintenance: a 4.0 major and a quiet patch line
The release history shows one significant recent change and a long quiet period before it. The tags are 4.0.0 on 2025-02-22, whose title is a bump of the minimum supported PHP version to 8.1, 3.2.0 on 2025-02-11, which added service name retrieval from RejectedException, and 3.1.2 from 2023-08-21, which loosened the requirement for psr/http-message.
That last entry is worth pausing on. A loosening of a PSR dependency requirement is exactly the kind of change that breaks downstream code, and it is also the kind of change that releases often note and move on from. If you are integrating Ganesha behind a Guzzle client, the version you pick determines which psr/http-message constraint you inherit.
The last push to the repository was on 2026-06-20, and the repository is not archived, so there has been work on the branch after the 4.0.0 tag. There is no newer release tag than February 2025 in the listing, which means that if you are evaluating this today, the published version is more than a year old and the branch is ahead of it.
The badges give a picture of the project's engineering: tests, coverage through coveralls, a Scrutinizer code quality score and the minimum PHP version. The README also links to a set of articles and videos about the pattern, and to a page listing companies using Ganesha.
None of this changes the assessment of what the library is. It is a small, focused implementation of a well understood pattern, with the integration work done for you, and the things to check before adopting it are the PHP version floor, the storage backend you already run, and whether your call sites report success and failure on every path.
Editorial conclusion
Adopt Ganesha if you run PHP services that call flaky dependencies and want per dependency failure tracking with a shared storage, especially since the Guzzle middleware means an existing client can be protected with one line. Do not adopt it for a single call site where an exception handler is enough, and do not adopt it if your failures are not correlated, because the statistics are keyed per service name and a count strategy with a low threshold will trip on ordinary errors. Verify first that your PHP version is 8.1 or later, that the storage adapter you choose is one your infrastructure already runs, since Redis, Memcached and other backends are pluggable, and that the failure and success calls sit on every exit path of your request, since a call that neither succeeds nor fails leaves the statistics wrong.
Frequently asked questions
What does Ganesha do in PHP?
Ganesha is a PHP implementation of the circuit breaker pattern. You check isAvailable for a service name before calling it, then call success or failure with the same name afterwards, and the library keeps statistics in a pluggable storage so that a failing dependency can be taken out of the loop.
What is the difference between the rate and count strategies in Ganesha?
The rate strategy evaluates a failure percentage over a time window and only applies the threshold once minimumRequests has been seen, while the count strategy trips after a fixed number of failures. The README's disable example refers to a count strategy with a threshold of 3.
How do I install Ganesha?
Install Composer and then require the package with the Composer command, php composer.phar require ackintosh/ganesha, or add ackintosh/ganesha to your own composer.json. The package is published on Packagist and requires PHP 8.1 or later, which is the floor set by the 4.0.0 release.
Which storage backends does Ganesha support?
The statistics are stored through pluggable adapters, and the README shows Redis and Memcached adapters, with the repository's docker-compose.yml also starting a mongo service for the examples. The statistics are keyed per service name, and the README has a section on customizing storage keys.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/ackintosh-ganesha)Was this useful?