{"id":17132753,"url":"https://github.com/awochna/breaker","last_synced_at":"2025-04-13T06:31:59.083Z","repository":{"id":57480764,"uuid":"80089370","full_name":"awochna/breaker","owner":"awochna","description":"Circuit breaker for HTTP requests in Elixir","archived":false,"fork":false,"pushed_at":"2019-08-06T20:46:05.000Z","size":88,"stargazers_count":36,"open_issues_count":2,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-03-15T13:09:33.922Z","etag":null,"topics":["circuit-breaker","distributed-computing","elixir","http-requests"],"latest_commit_sha":null,"homepage":null,"language":"Elixir","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/awochna.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":"code_of_conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-26T05:43:33.000Z","updated_at":"2024-03-15T13:09:33.923Z","dependencies_parsed_at":"2022-09-26T17:41:19.820Z","dependency_job_id":null,"html_url":"https://github.com/awochna/breaker","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awochna%2Fbreaker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awochna%2Fbreaker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awochna%2Fbreaker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/awochna%2Fbreaker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/awochna","download_url":"https://codeload.github.com/awochna/breaker/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248665748,"owners_count":21142123,"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-computing","elixir","http-requests"],"created_at":"2024-10-14T19:28:11.091Z","updated_at":"2025-04-13T06:31:58.797Z","avatar_url":"https://github.com/awochna.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Breaker #\n\n[![Hex.pm](https://img.shields.io/hexpm/v/breaker.svg)](https://hex.pm/packages/breaker)\n[![Build Status](https://travis-ci.org/awochna/breaker.svg?branch=master)](https://travis-ci.org/awochna/breaker)\n[![Coverage Status](https://coveralls.io/repos/github/awochna/breaker/badge.svg?branch=master)](https://coveralls.io/github/awochna/breaker?branch=master)\n[![Inline docs](https://inch-ci.org/github/awochna/breaker.svg)](https://inch-ci.org/github/awochna/breaker)\n[![Ebert](https://ebertapp.io/github/awochna/breaker.svg)](https://ebertapp.io/github/awochna/breaker)\n\nA Circuit Breaker in Elixir for making async HTTP(S) requests to external resources.\nUses [HTTPotion](https://github.com/myfreeweb/httpotion) to make requests.\n\nThe following README documentation is for the `master` branch.\nMaybe you're looking for the [1.0.0 docs](http://hexdocs.pm/breaker/1.0.0/)?\n\n## Installation ##\n\nAdd this project as a dependency in your mix.exs file:\n\n```\ndefp deps do\n  [\n    {:breaker, \"~\u003e 1.0.0\"}\n  ]\nend\n```\n\nAnd then run:\n\n    $ mix deps.get\n\n## Simple Usage ##\n\nTo create a circuit breaker for an external resource, do something like the following:\n\n```\n{:ok, user_service} = Breaker.start_link([url: \"http://example.com/users/\"])\n```\n\nThen, you can use it and Breaker to make HTTP calls:\n\n### GET example ###\n\nYou can make a request for some data you know you'll need later:\n\n```\n# Makes a GET request to \"http://example.com/users/42\"\nuser_request = Breaker.get(user_service, \"/42\")\n\n# do some other things, then later, when you need it\n\nuser = Task.await(user_request)\n```\n\n### POST example ###\n\nSay you need to create a new user and ensure the response from the other\nservice was good.\n\n```\nbody = build_new_user_body(new_user)\nrequest = Breaker.post(user_service, \"/\", [body: body])\n\n# do some other things,\n\n# then ensure you got a good response from your request,\n# otherwise put it in Redis or something for later\nresponse = Task.await(request)\ncond do\n  Breaker.error?(response) -\u003e\n    # put this request in Redis for later\n  # other possible responses, like 403 or 422\n  response.status_code == 200 -\u003e\n    # yay, continue\nend\n```\n\n### Other HTTP Methods ###\n\nBreaker has a function for each of the HTTP methods: `GET`, `POST`, `PUT`, `PATCH`, `HEAD`, `DELETE`, and `OPTIONS`.\n\nThey follow the same easy convention as HTTPotion: `Breaker.get/3`, `Breaker.put/3`, etc.\n\n### Naming your Breaker ###\n\n`Breaker.start_link` can accept an extra parameter and will pass it directly to GenServer as a name to register the process.\n\n```\nBreaker.start_link([url: \"http://example.com/users/\"], :user_service)\n# Now you can just use the registered name\nuser_request = Breaker.get(:user_service, \"/42\")\n```\n\nThis makes it easier to use application-wide breakers and supervision trees.\n\n### Other Helpful Functions ###\n\n* `Breaker.open?/1` takes a breaker and returns a boolean, asking if it is open (won't allow network flow)\n* `Breaker.error?/1` takes a response and returns a boolean, asking if the response was some sort of error (Status Code of 500, timeout, `Breaker.OpenCircuitError`)\n* `Breaker.trip/1` sets the breaker's status to open, disallowing network flow.\n* `Breaker.reset/1` sets the breaker's status to closed, allowing network flow.\n\nYou probably don't want to make use of `Breaker.trip/1` and `Breaker.reset/1` because the breaker's status will be recalculated after a request and override what you've manually set.\n\n## Configuration ##\n\nYou can configure your new breaker with a few different options.\nThe following options affect each request made:\n\n* `url`: Required, the base URL for your external serivce, like \"http://your-domain.com/users/\" for your user service, or \"http://users.your-domain.com/\"\n* `headers`: Any headers (like in HTTPotion) that should be included in EVERY request made by the circuit breaker.\nThis could be something like an authentication token or a service identifier for logs.\nThe default is `[]`.\n* `timeout`: The number of milliseconds before giving up on a request. This is passed to HTTPotion and has a default of `3000`, or 3 seconds.\n\nThe following options affect how the breaker's status is calculated:\n\n* `error_threshold`: The percent (as a float) of requests that are allowed to be bad (bad = 500 or timeout).\nThe default is `0.05` or 5%.\nOnce this threshold is passed, the breaker trips and more requests will return `%Breaker.OpenCircuitError{}` responses.\n* `bucket_length`: The breaker uses multiple buckets in a health window to determine the `error_rate`.\nThis setting specifies, in milliseconds, how long a bucket should be.\nThe default is `1000` or 1 second.\n* `window_length`: The length (in buckets) of the health window.\nThis number, multiplied by `bucket_length` is the total number of milliseconds used to calculate health.\nThe default is `10`.\n\n## Understanding 'buckets' and 'windows' ##\n\nThe breaker uses multiple 'buckets' in a 'window' to determine health and roll out old requests.\nBuckets are measured in time (milliseconds) and windows are measured in buckets.\nThis means that using the defaults, health is calculated based on responses received in the last 10 seconds of operation.\nI highly encourage you to play with these settings to accomodate your individual traffic.\n\nTo give an example, say your application is happily going along, processing requests and making requests of an external service, the User Serivce.\nIt's making an average of 1 request per second, using the default `bucket_length` and `window_length`.\nThen, it hits a 500 error.\nAt this point, it's error rate was 0%, but just jumped to 10%, above the default `error_threshold`.\nNow, when you make a new request, the breaker is open.\nInstead of waiting up to 3 seconds to get a 500 error or timeout, the request fails fast, returning a `%Breaker.OpenCircuitError{}`.\nIn about 9 more seconds, the bucket that contained our 500 error will be rotated out, closing the circuit and leaving us with a clean slate.\n\nIf our very next request now times out or gives a 500 (because the external service still isn't working properly), then we have an error rate of 100% and the circuit opens for another 10 seconds.\n\nIf, instead, the service had recovered while the circuit was open, then we only have (at most) about 10 seconds of missed requests.\nHopefully, we have designed our application such that those requests are not absolutely required, or we've stashed them in a queue somewhere for processing later.\n\nThe above is a greatly simplified example because normally you'll want to create a breaker for something that you'll need to make calls against at a much higher rate.\nBasically, keep the following things in mind when configuring your breaker:\n\n* `bucket_length` should be long enough that, on average, you'll have 5 or more requests in that period of time. For the above example, we'd probably want `5000`.\n* `window_length` should be high enough that you give the external service time to recover, but low enough that errors from awhile ago aren't bogging down your application's performance or features now.\nThe default might just be good enough unless you've found a reason to change it.\n* `error_threshold` should be low enough that your users aren't dealing with a really slow experience.\nIt will need to be higher if `bucket_length` and `window_length` aren't enough to get a good sample of the requets.\nIn the above example, it would have probably been acceptable to have an `error_threshold` of something like `0.2` (20%) so I can tolerate more than 1 error before breaking the circuit.\n\n## Contributing ##\n\nBug reports are welcome and contributions are encouraged.\nIf something isn't working the way it should or a convention isn't being followed, that's a bug.\nIf there isn't documentation for something, I consider that a bug, too.\n\nPlease note that this project is released with a Contributor Code of Conduct. By participating in this project, you agree to abide by its terms.\n\n## License ##\n\nThis project is released under the MIT license, as detailed in the included `license.txt` file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawochna%2Fbreaker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fawochna%2Fbreaker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fawochna%2Fbreaker/lists"}