nogara-redis_failover 0.8.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.travis.yml +5 -0
- data/.yardopts +6 -0
- data/Changes.md +116 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +190 -0
- data/Rakefile +9 -0
- data/bin/redis_node_manager +7 -0
- data/examples/config.yml +14 -0
- data/examples/multiple_environments_config.yml +15 -0
- data/lib/redis_failover.rb +22 -0
- data/lib/redis_failover/cli.rb +119 -0
- data/lib/redis_failover/client.rb +441 -0
- data/lib/redis_failover/errors.rb +47 -0
- data/lib/redis_failover/manual_failover.rb +40 -0
- data/lib/redis_failover/node.rb +190 -0
- data/lib/redis_failover/node_manager.rb +352 -0
- data/lib/redis_failover/node_watcher.rb +79 -0
- data/lib/redis_failover/runner.rb +28 -0
- data/lib/redis_failover/util.rb +83 -0
- data/lib/redis_failover/version.rb +3 -0
- data/misc/redis_failover.png +0 -0
- data/redis_failover.gemspec +26 -0
- data/spec/cli_spec.rb +75 -0
- data/spec/client_spec.rb +100 -0
- data/spec/node_manager_spec.rb +112 -0
- data/spec/node_spec.rb +84 -0
- data/spec/node_watcher_spec.rb +58 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/config/multiple_environments.yml +15 -0
- data/spec/support/config/multiple_environments_with_chroot.yml +17 -0
- data/spec/support/config/single_environment.yml +7 -0
- data/spec/support/config/single_environment_with_chroot.yml +8 -0
- data/spec/support/node_manager_stub.rb +65 -0
- data/spec/support/redis_stub.rb +105 -0
- data/spec/util_spec.rb +21 -0
- metadata +210 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Changes.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
0.8.9
|
2
|
+
-----------
|
3
|
+
- Handle errors raised by redis 3.x client (tsilen)
|
4
|
+
|
5
|
+
0.8.8
|
6
|
+
-----------
|
7
|
+
- Use a stack for handling nested blocks in RedisFailover::Client (inspired by connection_pool gem)
|
8
|
+
- Fix an issue with #multi and Redis 3.x.
|
9
|
+
|
10
|
+
0.8.7
|
11
|
+
-----------
|
12
|
+
- Support TTL operation (#24)
|
13
|
+
|
14
|
+
0.8.6
|
15
|
+
-----------
|
16
|
+
- No longer buffer output (kyohsuke)
|
17
|
+
- Update redis/zk gem versions to latest (rudionrails)
|
18
|
+
|
19
|
+
0.8.5
|
20
|
+
-----------
|
21
|
+
- Lock-down gemspec to version 1.1.x of redis-namespace to play nicely with redis 2.2.x.
|
22
|
+
- Fix RedisFailover::Client#manual_failover regression (oleriesenberg)
|
23
|
+
|
24
|
+
0.8.4
|
25
|
+
-----------
|
26
|
+
- Lock-down gemspec to redis 2.2.x in light of upcoming redis 3.x release. Once sufficient testing
|
27
|
+
has been done with the 3.x release, I will relax the constraint in the gemspec.
|
28
|
+
- Add environment-scoped configuration file support (oleriesenberg)
|
29
|
+
|
30
|
+
0.8.3
|
31
|
+
-----------
|
32
|
+
- Added a way to gracefully shutdown/reconnect a RedisFailover::Client. (#13)
|
33
|
+
- Upgraded to latest ZK version that supports forking.
|
34
|
+
- Handle case where the same RedisFailover::Client is referenced by a #multi block (#14)
|
35
|
+
|
36
|
+
0.8.2
|
37
|
+
-----------
|
38
|
+
- Fix method signature for RedisFailover::Client#respond_to_missing? (#12)
|
39
|
+
|
40
|
+
0.8.1
|
41
|
+
-----------
|
42
|
+
- Added YARD documentation.
|
43
|
+
- Improve ZooKeeper client connection management.
|
44
|
+
- Upgrade to latest ZK gem stable release.
|
45
|
+
|
46
|
+
0.8.0
|
47
|
+
-----------
|
48
|
+
- Added manual failover support (can be initiated via RedisFailover::Client#manual_failover)
|
49
|
+
- Misc. cleanup
|
50
|
+
|
51
|
+
0.7.0
|
52
|
+
-----------
|
53
|
+
- When new master promotion occurs, make existing slaves point to new candidate before promoting new master.
|
54
|
+
- Add support for specifying command-line options in a config.yml file for Node Manager.
|
55
|
+
- Upgrade to 0.9 version of ZK client and cleanup ZK connection error handling.
|
56
|
+
|
57
|
+
0.6.0
|
58
|
+
-----------
|
59
|
+
- Add support for running multiple Node Manager processes for added redundancy (#4)
|
60
|
+
- Add support for specifying a redis database in RedisFailover::Client (#5)
|
61
|
+
- Improved Node Manager command-line option parsing
|
62
|
+
|
63
|
+
0.5.4
|
64
|
+
-----------
|
65
|
+
- No longer use problematic ZK#reopen.
|
66
|
+
|
67
|
+
0.5.3
|
68
|
+
-----------
|
69
|
+
- Handle more ZK exceptions as candidates for reconnecting the client on error.
|
70
|
+
- Add safety check to actively purge redis clients if a RedisFailover::Client hasn't heard from the Node Manager in a certain time window.
|
71
|
+
|
72
|
+
0.5.2
|
73
|
+
-----------
|
74
|
+
- Always try to create path before setting current state in Node Manager.
|
75
|
+
- More explicit rescuing of exceptions.
|
76
|
+
|
77
|
+
0.5.1
|
78
|
+
-----------
|
79
|
+
- More logging around exceptions
|
80
|
+
- Handle re-watching on client session expirations / disconnections
|
81
|
+
- Use an ephemeral node for the list of redis servers
|
82
|
+
|
83
|
+
0.5.0
|
84
|
+
-----------
|
85
|
+
- redis_failover is now built on top of ZooKeeper! This means redis_failover enjoys all of the reliability, redundancy, and data consistency offered by ZooKeeper. The old fragile HTTP-based approach has been removed and will no longer be supported in favor of ZooKeeper. This does mean that in order to use redis_failover, you must have ZooKeeper installed and running. Please see the README for steps on how to do this if you don't already have ZooKeeper running in your production environment.
|
86
|
+
|
87
|
+
0.4.0
|
88
|
+
-----------
|
89
|
+
- No longer force newly available slaves to master if already slaves of that master
|
90
|
+
- Honor a node's slave-serve-stale-data configuration option; do not mark a sync-with-master-in-progress slave as available if its slave-serve-stale-data is disabled
|
91
|
+
- Change reachable/unreachable wording to available/unavailable
|
92
|
+
- Added node reconciliation, i.e. if a node comes back up, make sure that the node manager and the node agree on current role
|
93
|
+
- More efficient use of redis client connections
|
94
|
+
- Raise proper error for unsupported operations (i.e., those that don't make sense for a failover client)
|
95
|
+
- Properly handle any hanging node operations in the failover server
|
96
|
+
|
97
|
+
0.3.0
|
98
|
+
-----------
|
99
|
+
- Integrated travis-ci
|
100
|
+
- Added background monitor to client for proactively detecting changes to current set of redis nodes
|
101
|
+
|
102
|
+
0.2.0
|
103
|
+
-----------
|
104
|
+
- Added retry support for contacting failover server from client
|
105
|
+
- Client now verifies proper master/slave role before attempting operation
|
106
|
+
- General edge case cleanup for NodeManager
|
107
|
+
|
108
|
+
0.1.1
|
109
|
+
-----------
|
110
|
+
|
111
|
+
- Fix option parser require
|
112
|
+
|
113
|
+
0.1.0
|
114
|
+
-----------
|
115
|
+
|
116
|
+
- First release
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ryan LeCompte
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,190 @@
|
|
1
|
+
# Automatic Redis Failover
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/ryanlecompte/redis_failover.png?branch=master)](http://travis-ci.org/ryanlecompte/redis_failover)
|
4
|
+
|
5
|
+
redis_failover attempts to provides a full automatic master/slave failover solution for Ruby. Redis does not provide
|
6
|
+
an automatic failover capability when configured for master/slave replication. When the master node dies,
|
7
|
+
a new master must be manually brought online and assigned as the slave's new master. This manual
|
8
|
+
switch-over is not desirable in high traffic sites where Redis is a critical part of the overall
|
9
|
+
architecture. The existing standard Redis client for Ruby also only supports configuration for a single
|
10
|
+
Redis server. When using master/slave replication, it is desirable to have all writes go to the
|
11
|
+
master, and all reads go to one of the N configured slaves.
|
12
|
+
|
13
|
+
This gem (built using [ZK][]) attempts to address these failover scenarios. A redis failover Node Manager daemon runs as a background
|
14
|
+
process and monitors all of your configured master/slave nodes. When the daemon starts up, it
|
15
|
+
automatically discovers the current master/slaves. Background watchers are setup for each of
|
16
|
+
the redis nodes. As soon as a node is detected as being offline, it will be moved to an "unavailable" state.
|
17
|
+
If the node that went offline was the master, then one of the slaves will be promoted as the new master.
|
18
|
+
All existing slaves will be automatically reconfigured to point to the new master for replication.
|
19
|
+
All nodes marked as unavailable will be periodically checked to see if they have been brought back online.
|
20
|
+
If so, the newly available nodes will be configured as slaves and brought back into the list of available
|
21
|
+
nodes. Note that detection of a node going down should be nearly instantaneous, since the mechanism
|
22
|
+
used to keep tabs on a node is via a blocking Redis BLPOP call (no polling). This call fails nearly
|
23
|
+
immediately when the node actually goes offline. To avoid false positives (i.e., intermittent flaky
|
24
|
+
network interruption), the Node Manager will only mark a node as unavailable if it fails to communicate with
|
25
|
+
it 3 times (this is configurable via --max-failures, see configuration options below). Note that you can
|
26
|
+
deploy multiple Node Manager daemons for added redundancy.
|
27
|
+
|
28
|
+
This gem provides a RedisFailover::Client wrapper that is master/slave aware. The client is configured
|
29
|
+
with a list of ZooKeeper servers. The client will automatically contact the ZooKeeper cluster to find out
|
30
|
+
the current state of the world (i.e., who is the current master and who are the current slaves). The client
|
31
|
+
also sets up a ZooKeeper watcher for the set of redis nodes controlled by the Node Manager daemon. When the daemon
|
32
|
+
promotes a new master or detects a node as going down, ZooKeeper will notify the client near-instantaneously so
|
33
|
+
that it can rebuild its set of Redis connections. The client also acts as a load balancer in that it will automatically
|
34
|
+
dispatch Redis read operations to one of N slaves, and Redis write operations to the master.
|
35
|
+
If it fails to communicate with any node, it will go back and fetch the current list of available servers, and then
|
36
|
+
optionally retry the operation.
|
37
|
+
|
38
|
+
[ZK]: https://github.com/slyphon/zk
|
39
|
+
|
40
|
+
## Architecture Diagram
|
41
|
+
|
42
|
+
![redis_failover architecture diagram](https://github.com/ryanlecompte/redis_failover/raw/master/misc/redis_failover.png)
|
43
|
+
|
44
|
+
## Installation
|
45
|
+
|
46
|
+
redis_failover has an external dependency on ZooKeeper. You must have a running ZooKeeper cluster already available in order to use redis_failover. ZooKeeper provides redis_failover with its high availability and data consistency between Redis::Failover clients and the Node Manager daemon. Please see the requirements section below for more information on installing and setting up ZooKeeper if you don't have it running already.
|
47
|
+
|
48
|
+
Add this line to your application's Gemfile:
|
49
|
+
|
50
|
+
gem 'redis_failover'
|
51
|
+
|
52
|
+
And then execute:
|
53
|
+
|
54
|
+
$ bundle
|
55
|
+
|
56
|
+
Or install it yourself as:
|
57
|
+
|
58
|
+
$ gem install redis_failover
|
59
|
+
|
60
|
+
## Node Manager Daemon Usage
|
61
|
+
|
62
|
+
The Node Manager is a simple process that should be run as a background daemon. The daemon supports the
|
63
|
+
following options:
|
64
|
+
|
65
|
+
Usage: redis_node_manager [OPTIONS]
|
66
|
+
|
67
|
+
Specific options:
|
68
|
+
-n, --nodes NODES Comma-separated redis host:port pairs
|
69
|
+
-z, --zkservers SERVERS Comma-separated ZooKeeper host:port pairs
|
70
|
+
-p, --password PASSWORD Redis password
|
71
|
+
--znode-path PATH Znode path override for storing redis server list
|
72
|
+
--max-failures COUNT Max failures before manager marks node unavailable
|
73
|
+
-C, --config PATH Path to YAML configuration file
|
74
|
+
-E, --environment ENV Config environment to use
|
75
|
+
-h, --help Display all options
|
76
|
+
|
77
|
+
To start the daemon for a simple master/slave configuration, use the following:
|
78
|
+
|
79
|
+
redis_node_manager -n localhost:6379,localhost:6380 -z localhost:2181,localhost:2182,localhost:2183
|
80
|
+
|
81
|
+
The configuration parameters can also be specified in a config.yml file. An example configuration
|
82
|
+
would look like the following:
|
83
|
+
|
84
|
+
---
|
85
|
+
:max_failures: 2
|
86
|
+
:nodes:
|
87
|
+
- localhost:6379
|
88
|
+
- localhost:1111
|
89
|
+
- localhost:2222
|
90
|
+
- localhost:3333
|
91
|
+
:zkservers:
|
92
|
+
- localhost:2181
|
93
|
+
- localhost:2182
|
94
|
+
- localhost:2183
|
95
|
+
:password: foobar
|
96
|
+
|
97
|
+
You would then simpy start the Node Manager via the following:
|
98
|
+
|
99
|
+
redis_node_manager -C config.yml
|
100
|
+
|
101
|
+
You can also scope the configuration to a particular environment (e.g., staging/development). See the examples
|
102
|
+
directory for configuration file samples.
|
103
|
+
|
104
|
+
The Node Manager will automatically discover the master/slaves upon startup. Note that it is
|
105
|
+
a good idea to run more than one instance of the Node Manager daemon in your environment. At
|
106
|
+
any moment, a single Node Manager process will be designated to monitor the redis servers. If
|
107
|
+
this Node Manager process dies or becomes partitioned from the network, another Node Manager
|
108
|
+
will be promoted as the primary monitor of redis servers. You can run as many Node Manager
|
109
|
+
processes as you'd like for added redundancy.
|
110
|
+
|
111
|
+
## Client Usage
|
112
|
+
|
113
|
+
The redis failover client must be used in conjunction with a running Node Manager daemon. The
|
114
|
+
client supports various configuration options, however the only mandatory option is the list of
|
115
|
+
ZooKeeper servers:
|
116
|
+
|
117
|
+
client = RedisFailover::Client.new(:zkservers => 'localhost:2181,localhost:2182,localhost:2183')
|
118
|
+
|
119
|
+
The client actually employs the common redis and redis-namespace gems underneath, so this should be
|
120
|
+
a drop-in replacement for your existing pure redis client usage.
|
121
|
+
|
122
|
+
The full set of options that can be passed to RedisFailover::Client are:
|
123
|
+
|
124
|
+
:zkservers - comma-separated ZooKeeper host:port pairs (required)
|
125
|
+
:znode_path - the Znode path override for redis server list (optional)
|
126
|
+
:password - password for redis nodes (optional)
|
127
|
+
:db - db to use for redis nodes (optional)
|
128
|
+
:namespace - namespace for redis nodes (optional)
|
129
|
+
:logger - logger override (optional)
|
130
|
+
:retry_failure - indicate if failures should be retried (default true)
|
131
|
+
:max_retries - max retries for a failure (default 3)
|
132
|
+
|
133
|
+
## Manual Failover
|
134
|
+
|
135
|
+
Manual failover can be initiated via RedisFailover::Client#manual_failover. This schedules a manual failover with the
|
136
|
+
currently active Node Manager. Once the Node Manager receives the request, it will either failover to the specific
|
137
|
+
server passed to #manual_failover, or it will pick a random slave to become the new master. Here's an example:
|
138
|
+
|
139
|
+
client = RedisFailover::Client.new(:zkservers => 'localhost:2181,localhost:2182,localhost:2183')
|
140
|
+
client.manual_failover(:host => 'localhost', :port => 2222)
|
141
|
+
|
142
|
+
## Documentation
|
143
|
+
|
144
|
+
redis_failover uses YARD for its API documentation. Refer to the generated [API documentation](http://rubydoc.info/github/ryanlecompte/redis_failover/master/frames) for full coverage.
|
145
|
+
|
146
|
+
## Requirements
|
147
|
+
|
148
|
+
- redis_failover is actively tested against MRI 1.9.2/1.9.3 and JRuby 1.6.7 (1.9 mode only). Other rubies may work, although I don't actively test against them.
|
149
|
+
- redis_failover requires a ZooKeeper service cluster to ensure reliability and data consistency. ZooKeeper is very simple and easy to get up and running. Please refer to this [Quick ZooKeeper Guide](https://github.com/ryanlecompte/redis_failover/wiki/Quick-ZooKeeper-Guide) to get up and running quickly if you don't already have ZooKeeper as a part of your environment.
|
150
|
+
|
151
|
+
## Considerations
|
152
|
+
|
153
|
+
- Note that by default the Node Manager will mark slaves that are currently syncing with their master as "available" based on the configuration value set for "slave-serve-stale-data" in redis.conf. By default this value is set to "yes" in the configuration, which means that slaves still syncing with their master will be available for servicing read requests. If you don't want this behavior, just set "slave-serve-stale-data" to "no" in your redis.conf file.
|
154
|
+
|
155
|
+
## Limitations
|
156
|
+
|
157
|
+
- Note that it's still possible for the RedisFailover::Client instances to see a stale list of servers for a very small window. In most cases this will not be the case due to how ZooKeeper handles distributed communication, but you should be aware that in the worst case the client could write to a "stale" master for a small period of time until the next watch event is received by the client via ZooKeeper.
|
158
|
+
|
159
|
+
- Note that currently multiple Node Managers are currently used for redundancy purposes only. The Node Managers do not communicate with each other to perform any type of election or voting to determine if they all agree on promoting a new master. Right now Node Managers that are not "active" just sit and wait until they can grab the lock to become the single decision-maker for which Redis servers are available or not. This means that a scenario could present itself where a Node Manager thinks the Redis master is available, however the actual RedisFailover::Client instances think they can't reach the Redis master (either due to network partitions or the Node Manager flapping due to machine failure, etc). We are exploring ways to improve this situation.
|
160
|
+
|
161
|
+
## Resources
|
162
|
+
|
163
|
+
- Check out Steve Whittaker's [redis-failover-test](https://github.com/swhitt/redis-failover-test) project which shows how to test redis_failover in a non-trivial configuration using Vagrant/Chef.
|
164
|
+
- To learn more about Redis master/slave replication, see the [Redis documentation](http://redis.io/topics/replication).
|
165
|
+
- To learn more about ZooKeeper, see the official [ZooKeeper](http://zookeeper.apache.org/) site.
|
166
|
+
- See the [Quick ZooKeeper Guide](https://github.com/ryanlecompte/redis_failover/wiki/Quick-ZooKeeper-Guide) for a quick guide to getting ZooKeeper up and running with redis_failover.
|
167
|
+
- To learn more about how ZooKeeper handles network partitions, see [ZooKeeper Failure Scenarios](http://wiki.apache.org/hadoop/ZooKeeper/FailureScenarios)
|
168
|
+
|
169
|
+
|
170
|
+
## License
|
171
|
+
|
172
|
+
Please see LICENSE for licensing details.
|
173
|
+
|
174
|
+
## Author
|
175
|
+
|
176
|
+
Ryan LeCompte
|
177
|
+
|
178
|
+
[@ryanlecompte](https://twitter.com/ryanlecompte)
|
179
|
+
|
180
|
+
## Acknowledgements
|
181
|
+
|
182
|
+
Special thanks to [Eric Lindvall](https://github.com/eric) and [Jonathan Simms](https://github.com/slyphon) for their invaluable ZooKeeper advice and guidance!
|
183
|
+
|
184
|
+
## Contributing
|
185
|
+
|
186
|
+
1. Fork it
|
187
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
188
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
189
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
190
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/examples/config.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Sample configuration file for Node Manager.
|
2
|
+
# redis_node_manager -C config.yml
|
3
|
+
---
|
4
|
+
:max_failures: 2
|
5
|
+
:nodes:
|
6
|
+
- localhost:6379
|
7
|
+
- localhost:1111
|
8
|
+
- localhost:2222
|
9
|
+
- localhost:3333
|
10
|
+
:zkservers:
|
11
|
+
- localhost:2181
|
12
|
+
- localhost:2182
|
13
|
+
- localhost:2183
|
14
|
+
:password: foobar
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'zk'
|
2
|
+
require 'set'
|
3
|
+
require 'yaml'
|
4
|
+
require 'redis'
|
5
|
+
require 'thread'
|
6
|
+
require 'logger'
|
7
|
+
require 'timeout'
|
8
|
+
require 'optparse'
|
9
|
+
require 'multi_json'
|
10
|
+
require 'securerandom'
|
11
|
+
|
12
|
+
require 'redis_failover/cli'
|
13
|
+
require 'redis_failover/util'
|
14
|
+
require 'redis_failover/node'
|
15
|
+
require 'redis_failover/errors'
|
16
|
+
require 'redis_failover/client'
|
17
|
+
require 'redis_failover/runner'
|
18
|
+
require 'redis_failover/version'
|
19
|
+
require 'redis_failover/node_manager'
|
20
|
+
require 'redis_failover/node_watcher'
|
21
|
+
require 'redis_failover/manual_failover'
|
22
|
+
|