|
| 1 | +# Faulty |
| 2 | + |
| 3 | +Fault-tolerance tools for ruby based on [circuit-breakers][martin fowler]. |
| 4 | + |
| 5 | +```ruby |
| 6 | +users = Faulty.circuit(:api).try_run do |
| 7 | + api.users |
| 8 | +end.or_default([]) |
| 9 | +``` |
| 10 | + |
| 11 | +## Installation |
| 12 | + |
| 13 | +Add it to your `Gemfile`: |
| 14 | + |
| 15 | +```ruby |
| 16 | +gem 'faulty' |
| 17 | +``` |
| 18 | + |
| 19 | +Or install it manually: |
| 20 | + |
| 21 | +```sh |
| 22 | +gem 'faulty' |
| 23 | +``` |
| 24 | + |
| 25 | +During your app startup, call `Faulty.init`. For Rails, you would do this in |
| 26 | +`config/initializers/faulty.rb`. |
| 27 | + |
| 28 | +## Setup |
| 29 | + |
| 30 | +Use the default configuration options: |
| 31 | + |
| 32 | +```ruby |
| 33 | +Faulty.init |
| 34 | +``` |
| 35 | + |
| 36 | +Or specify your own configuration: |
| 37 | + |
| 38 | +```ruby |
| 39 | +Faulty.init do |config| |
| 40 | + config.storage = Faulty::Storage::Redis.new |
| 41 | + |
| 42 | + listener = Faulty::Events::CallbackListener.new |
| 43 | + config.listeners = [ |
| 44 | + Faulty::Events::CallbackListener.new do |events| |
| 45 | + events.circuit_open do |payload| |
| 46 | + puts 'Circuit was opened' |
| 47 | + end |
| 48 | + end |
| 49 | + ] |
| 50 | +end |
| 51 | +``` |
| 52 | + |
| 53 | +For a full list of configuration options, see the Configuration section. |
| 54 | + |
| 55 | +## Basic Usage |
| 56 | + |
| 57 | +## Configuration |
| 58 | + |
| 59 | +## Faulty's Circuit Breaker Algorithm |
| 60 | + |
| 61 | +Faulty implements a version of circuit breakers inspired by |
| 62 | +[Martin Fowler's post][martin fowler] on the subject. A few notable features of |
| 63 | +Faulty's implementation are: |
| 64 | + |
| 65 | +- Rate-based failure thresholds |
| 66 | +- Integrated caching inspired by Netflix's [Hystrix][hystrix] with automatic |
| 67 | + cache jitter and error fallback. |
| 68 | +- Event-based monitoring |
| 69 | + |
| 70 | +## Circuit Options |
| 71 | + |
| 72 | +## Caching |
| 73 | + |
| 74 | +Faulty integrates caching into it's circuits in a way that is particularly |
| 75 | +suited to fault-tolerance. To make use of caching, you must specify the `cache` |
| 76 | +configuration option when initializing Faulty. If you're using Rails, this is |
| 77 | +automatically set to the Rails cache. |
| 78 | + |
| 79 | +Once your cache is configured, you can use the `cache` parameter when running |
| 80 | +a circuit: |
| 81 | + |
| 82 | +```ruby |
| 83 | +feed = Faulty.circuit(:rss_feeds) |
| 84 | + .try_run(cache: "rss_feeds/#{feed}" do |
| 85 | + fetch_feed(feed) |
| 86 | + end.or_default([]) |
| 87 | +``` |
| 88 | + |
| 89 | +By default a circuit has the following options: |
| 90 | + |
| 91 | +- `cache_expires_in`: 86400 (1 day). This is sent to the cache backend and |
| 92 | + defines how long the cache entry should be stored. After this time elapses, |
| 93 | + queries will result in a cache miss. |
| 94 | +- `cache_refreshes_after`: 900 (15 minutes). This is used internally by Faulty |
| 95 | + to indicate when a cache should be refreshed. It does not affect how long the |
| 96 | + cache entry is stored. |
| 97 | +- `cache_refresh_jitter`: 180 (3 minutes = 20% of `cache_refreshes_after`). The |
| 98 | + maximum number of seconds to randomly add or subtract from |
| 99 | + `cache_refreshes_after` when determining whether to refresh a cache entry. |
| 100 | + This mitigates the "thundering herd" effect caused by many processes |
| 101 | + simultaneously refreshing the cache. |
| 102 | + |
| 103 | +This code will attempt to fetch an RSS feed protected by a circuit. If the feed |
| 104 | +is within the cache refresh period, then the result will be returned from the |
| 105 | +cache and the block will not be executed regardless of the circuit state. |
| 106 | + |
| 107 | +If the cache is hit, but outside its refresh period, then Faulty will check the |
| 108 | +circuit state. If the circuit is closed or half-open, then it will run the |
| 109 | +block. If the block is successful, then it will update the circuit, write to the |
| 110 | +cache and return the new value. |
| 111 | + |
| 112 | +However, if the cache is hit and the block fails, then that failure is noted |
| 113 | +in the circuit and Faulty returns the cached value. |
| 114 | + |
| 115 | +If the circuit is open and the cache is hit, then Faulty will always return the |
| 116 | +cached value. |
| 117 | + |
| 118 | +If the cache query results in a miss, then faulty operates as normal. In the |
| 119 | +code above, the block will be executed. If the block succeeds, the cache is |
| 120 | +refreshed. If the block fails, the default of `[]` will be returned. |
| 121 | + |
| 122 | +## Fault Tolerance |
| 123 | + |
| 124 | +## Event Handling |
| 125 | + |
| 126 | +## Scopes |
| 127 | + |
| 128 | +## Implementing a Storage Backend |
| 129 | + |
| 130 | +## Implementing a Cache Backend |
| 131 | + |
| 132 | +## Implementing a Event Listener |
| 133 | + |
| 134 | +[martin fowler]: https://www.martinfowler.com/bliki/CircuitBreaker.html |
| 135 | +[hystrix]: https://github.com/Netflix/Hystrix/wiki/How-it-Works |
0 commit comments