{"id":17259878,"url":"https://github.com/sigmavirus24/circuitry","last_synced_at":"2025-10-17T23:17:38.486Z","repository":{"id":203492996,"uuid":"709584453","full_name":"sigmavirus24/circuitry","owner":"sigmavirus24","description":"Distributed Circuit Breaker pattern for Go","archived":false,"fork":false,"pushed_at":"2025-03-13T11:39:21.000Z","size":266,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-13T12:33:07.618Z","etag":null,"topics":["circuit-breaker","distributed","distributed-control","distributed-systems","go","golang"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/sigmavirus24/circuitry","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sigmavirus24.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-25T01:29:21.000Z","updated_at":"2025-03-13T11:39:20.000Z","dependencies_parsed_at":"2023-12-25T16:54:39.074Z","dependency_job_id":"b9a0331a-592c-4af3-b5b7-57530124a129","html_url":"https://github.com/sigmavirus24/circuitry","commit_stats":null,"previous_names":["sigmavirus24/circuitry"],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigmavirus24%2Fcircuitry","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigmavirus24%2Fcircuitry/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigmavirus24%2Fcircuitry/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sigmavirus24%2Fcircuitry/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sigmavirus24","download_url":"https://codeload.github.com/sigmavirus24/circuitry/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245003634,"owners_count":20545640,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["circuit-breaker","distributed","distributed-control","distributed-systems","go","golang"],"created_at":"2024-10-15T07:46:27.478Z","updated_at":"2025-10-17T23:17:38.479Z","avatar_url":"https://github.com/sigmavirus24.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# circuitry - Distributed Circuit Breakers for Go\n\nIf you have a distributed task queue that is multi-tenant and you want circuit\nbreakers around a tenant's jobs in that queue, existing Go circuit breakers\nare only useful if you retry that job in the current worker in the context of\nthe existing task. However, that can lead to requiring very low numbers of\nretries and not being able to backoff each time you retry the task because\nthose circuit breakers cannot store state elsewhere and retrieve it at the\nstart. Otherwise, if you use an exponential backoff with a large number of\nretries, this one task can consume a great deal of that one worker's time\nleading to starvation amongst other tenants' tasks.\n\n## Example Usage\n\n1. Start by choosing a storage provider. Circuitry comes with two providers by\n   default:\n\n   * [Redis](./backends/redis/README.md)\n\n   * [DynamoDB](./backends/dynamodb/README.md)\n\n   Once you've configured your chosen backend client, you'll want to set that\n   in your Circuit Breaker Factory's Settings\n\n```go\nimport (\n    \"errors\"\n    \"fmt\"\n    \"time\"\n\n    \"github.com/sigmavirus24/circuitry\"\n    \"github.com/sigmavirus24/circuitry/log\"\n)\n\nfunc CreateCircuitBreakerFactory(backend circuitry.StorageBackender, logger log.Logger) (*circuitry.CircuitBreakerFactory, error) {\n    settings, err := circuitry.NewFactorySettings(\n        circuitry.WithStorageBackend(backend),\n        circuitry.WithLogger(logger),\n        circuitry.WithNameFunc(func(baseName string, circuitContext map[string]any) string {\n            tenantId := circuitContext[\"tenant_id\"] // For demonstration purposes, I strongly suggest this be a stable id\n            return fmt.Sprintf(\"%s/%s\", name, tenantId)\n        })\n        circuitry.WithDefaultFallbackErrorMatcher(),\n        circuitry.WithFailureCountThreshold(15),\n        circuitry.WithCloseThreshold(5),\n        circuitry.WithAllowAfter(30 * time.Minute),\n        circuitry.WithCyclicClearAfter(12 * time.Hour),\n        circuitry.WithStateChangeCallback(func(name string, circuitContext map[string]any, from, to circuitry.CircuitState) {\n            logger.\n                WithFields(circuitContext). // Ensure no sensitive information is logged here\n                WithFields(log.Fields{\n                    \"name\": name,\n                    \"from\": from.String(),\n                    \"to\": to.String(),\n                }).Debug(\"state transition\")\n        })\n    )\n    if err != nil {\n        return nil, err\n    }\n    return circuitry.NewCircuitBreakerFactory(settings), nil\n}\n\nfunc TenantContext(id string) map[string]any {\n    return map[string]any{\n        \"tenant_id\": id,\n        // Include your other context\n    }\n}\n\nfunc Work() (any, error) {\n    // Do your work here\n    return struct{}{}, nil\n}\n\nfunc main() {\n    // Setup your backend and logger\n    factory := CreateCircuitBreakerFactory(backend, logger)\n    tenantIDs := []string{\n        // IDs\n    }\n    for _, tenantID := range tenantIDs {\n        tenantCtx := TenantContext(tenantID)\n        breaker := factory.BreakerFor(\"do-work-example\", tenantCtx)\n        result, workErr, err := breaker.Execute(Work)\n        if errors.Is(err, circuitry.ErrCircuitBreakerOpen) {\n            logger.WithField(\"tenant_id\", tenantID).Warn(\"circuit already open\")\n            continue\n        }\n        if err != nil {\n            logger.WithError(err).WithField(\"tenant_id\", tenantID).Error(\"circuit breaker could not start work\")\n        }\n        if workErr != nil {\n            logger.WithError(err).WithField(\"tenant_id\", tenantID).Error(\"work function failed\")\n        }\n        logger.WithField(\"tenant_id\", tenantID).Debug(\"finished work function successfully\")\n    }\n}\n```\n\nSome of this has been simplified for demonstration purposes.\n\n\n## Why make this?\n\nI've had experience in the past with distributed circuit breakers in other\nlanguages. I needed one for a project I'm working on in Go and couldn't find\none that already existed and there did not appear to be anyway to adapt\nexisting circuit breaker implementations to store and retrieve state from a\nremote backend. As a result, I implemented the pattern for myself with great\ninspiration from go-breaker as explained in the [Credits](#credits).\n\n## Credits\n\nMuch of the design of this library was informed by github.com/sony/go-breaker.\nThe significant differences are as follows:\n\n* Representation of the internal state of the circuit breaker to allow it to\n  be stored/retrieved\n\n* Naming of some constants to make it clearer what they are\n\n* Fix bug in transitions from Half-Open to Open state where generation is\n  incremented and counts are reset incorrectly\n\n* Interfaces for storing the state remotely to create a distributed circuit\n  breaker\n\n* A single interface rather than two interfaces (e.g., instead of a\n  CircuitBreaker and TwoStepCircuitBreaker, one gets a single interface)\n\n* Default implementations for many things\n\n* Options Function style configuration\n\n\n## Roadmap\n\n* Add logging to core module areas that make most sense\n\n* Ensure that we have good primitives and interfaces\n\n  * Does it make sense to rely on `sync.Locker` or should we have locks with\n    heartbeats that can expire if the breaker doesn't keep it alive?\n\n* Ensure that the documentation is sufficient\n\n* Add more examples\n\n* Do we need metrics that this package would expose?\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsigmavirus24%2Fcircuitry","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsigmavirus24%2Fcircuitry","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsigmavirus24%2Fcircuitry/lists"}