{"id":21304511,"url":"https://github.com/vassilevsky/simple_circuit","last_synced_at":"2025-09-12T22:40:44.580Z","repository":{"id":69620252,"uuid":"105068573","full_name":"vassilevsky/simple_circuit","owner":"vassilevsky","description":"Circuit Breaker pattern implementation in Ruby","archived":false,"fork":false,"pushed_at":"2019-01-26T15:38:25.000Z","size":10,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-01-22T08:35:18.724Z","etag":null,"topics":["circuit-breaker","circuit-breaker-pattern","resiliency","resiliency-patterns","ruby-gem","ruby-library","rubygem"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/simple_circuit","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vassilevsky.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","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":"2017-09-27T20:56:43.000Z","updated_at":"2023-01-27T15:17:24.000Z","dependencies_parsed_at":null,"dependency_job_id":"b770079a-f2e1-4221-9be8-1a73eaf60fa2","html_url":"https://github.com/vassilevsky/simple_circuit","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vassilevsky%2Fsimple_circuit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vassilevsky%2Fsimple_circuit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vassilevsky%2Fsimple_circuit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vassilevsky%2Fsimple_circuit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vassilevsky","download_url":"https://codeload.github.com/vassilevsky/simple_circuit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243778288,"owners_count":20346517,"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","circuit-breaker-pattern","resiliency","resiliency-patterns","ruby-gem","ruby-library","rubygem"],"created_at":"2024-11-21T16:08:43.728Z","updated_at":"2025-03-15T19:22:18.080Z","avatar_url":"https://github.com/vassilevsky.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![gem version](https://img.shields.io/gem/v/simple_circuit.svg?style=flat-square)](https://rubygems.org/gems/simple_circuit)\n[![build status](https://img.shields.io/travis/vassilevsky/simple_circuit.svg?style=flat-square)](https://travis-ci.org/vassilevsky/simple_circuit)\n\n# SimpleCircuit\n\nA simple implementation of [Circuit Breaker](https://en.wikipedia.org/wiki/Circuit_breaker_design_pattern) pattern.\n\nUse it when you make calls to an unreliable service. It will not make the service reliable, but it will **fail fast** when the service is down, to prevent overload in your app.\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'simple_circuit' # Circuit breaker to fail fast on external service outages\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install simple_circuit\n\n## Usage\n\nSuppose you have calls to unreliable services like these:\n\n```ruby\nclient = UnreliableServiceClient.new(url: \"https://api.example.io\")\nclient.get_some_info # =\u003e \"foo bar\"\n```\n\nWhen they go down or become unresponsive, your app starts to slow down too. Queues filling up, etc.\n\nIf you'd rather have the calls fail fast, and handle failures fast, use it through a circuit:\n\n```ruby\nclient = UnreliableServiceClient.new(url: \"https://api.example.io\")\ncircuit = SimpleCircuit.new(payload: client)\ncircuit.pass(:get_some_info) # =\u003e \"foo bar\"\ncircuit.pass(:get_other_info, arg1, arg2) # =\u003e \"baz qux\"\n```\n\nYou're passing the same message (`get_some_info`) to `client` object, but now it goes through a _circuit_.\nIt works exactly the same while the circuit is _closed_ (there are no problems in the payload).\n\n```ruby\ncircuit.closed? # =\u003e true\n```\n\nInteresting things begin when `client` starts throwing errors.\n\nThe first few errors (100 by default) are returned as is:\n\n```ruby\ncircuit.pass(:get_some_info) # =\u003e HTTP::TimeoutError\n```\n\nThis is still slow because it's the `client` object still working as usual.\n\nBut after 100 errors, the circuit **breaks**.\n\n```ruby\ncircuit.closed? # =\u003e false\ncircuit.open?   # =\u003e true\n```\n\nThe payload is disconnected from the circuit.\nIt no longer receives the `get_some_info` message.\nInstead, the circuit itself immediately throws the error.\nSo, each call will fail fast.\nThis will prevent overload in your app while the service is down.\nThis will also reduce the load on the service and hopefully allow it to recover faster.\n\nThe circuit will keep trying to connect the payload back and send the message through it, at regular intervals (by default, every minute).\nWhen it succeeds, it will become _closed_ again and will rely all messages to the payload, just like in the beginning.\n\n### Error Counting\n\nThe circuit counts exceptions coming from the payload **by class** and breaks only if **a particular class** of exceptions is received too many times.\nTherefore, it fails fast with the most occurred exception.\n\nFor example, if the payload occasionally throws `MultiJson::ParseError` and then starts throwing `HTTP::TimeoutError` on a regular basis, then the counter of `HTTP::TimeoutError` will reach the maximum first, and the circuit will fast-throw `HTTP::TimeoutError` after breaking.\n\nIt might be non-ideal. I welcome suggestions via issues or pull requests.\n\n### Customization\n\nYou can customize several parameters of circuits. These are the defaults:\n\n```ruby\ncircuit = SimpleCircuit.new(payload: client, max_failures: 100, retry_in: 60, logger: nil)\n```\n\nThe parameters are:\n\n* `max_failures` — How many exceptions from the payload should be ignored (returned as is) before the circuit _breaks_ and starts to _fail fast_.\n* `retry_in` — How many seconds should pass before every retry to connect the payload (to send the original message) when the circuit is _open_ (broken).\n* `logger` — An object that responds to `warn(message)`. It will be called each time the circuit is broken.\n\n## Development\n\nAfter checking out the repo, run `bundle` to install dependencies. Then, run `bundle exec rake` to run the tests.\n\nTo install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `lib/circuit.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/vassilevsky/circuit\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvassilevsky%2Fsimple_circuit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvassilevsky%2Fsimple_circuit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvassilevsky%2Fsimple_circuit/lists"}