redis-ha 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in redis-ha.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 John Maxwell
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,37 @@
1
+ # RedisHA
2
+
3
+ THIS IS CURRENTLY NOT FUNCTIONING - IN DEVELOPMENT ONLY
4
+
5
+ RedisHA is a Highly Available solution for running Redis under situations other than as a cacheing layer, specifically for where single server operations are required and thus sharding is not an option (e.g. Queueing, Lua based ops).
6
+
7
+ RedisHA consists of two parts - a Router and a Manager.
8
+
9
+ ## Router
10
+
11
+ The Router layer acts as a single interface point to your Redis installations, and is essentially a high-performance reverse proxy layer to your Redis installations.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'redis-ha'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install redis-ha
26
+
27
+ ## Usage
28
+
29
+ TODO: Write usage instructions here
30
+
31
+ ## Contributing
32
+
33
+ 1. Fork it
34
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
35
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
36
+ 4. Push to the branch (`git push origin my-new-feature`)
37
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/redis-ha ADDED
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path("../../lib", __FILE__)
4
+ $:.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
+
6
+ require 'redis-ha'
7
+ require 'redis_ha/router'
8
+ require 'optparse'
9
+
10
+ options = {:debug => false, :hashing => false}
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: redis-ha [options]"
13
+
14
+ opts.on("-l", "--listen [PORT]", Integer, "Port to listen on") do |v|
15
+ options[:port] = v || 7379
16
+ end
17
+
18
+ opts.on("-b", "--bind [IP]", String, "Interface to bind to") do |v|
19
+ options[:bind] = v || "0.0.0.0"
20
+ end
21
+
22
+ opts.on("-s", "--servers [hostname:port]", String, "Servers to bind to") do |v|
23
+ options[:servers] = v
24
+ end
25
+
26
+ opts.on("-h", "--hashing", "Enable consistent hashing") do |v|
27
+ options[:hashing] = v
28
+ end
29
+
30
+ opts.on("-d", "--debug", "Enable debug mode") do |v|
31
+ options[:debug] = v
32
+ end
33
+
34
+ end.parse!
35
+
36
+ RedisHA::Router.start(options[:bind], options[:port], options) do |run|
37
+ servers = if options[:servers].include?(",")
38
+ options[:servers].split(",")
39
+ else
40
+ [options[:servers]]
41
+ end
42
+ servers.each do |serv|
43
+ host, port = serv.split(":")
44
+ run.upstream(:relay, host: host, port: port.to_i)
45
+ end
46
+ run.on_data do |data|
47
+ data
48
+ end
49
+
50
+ run.on_response do |upstream, response|
51
+ response if upstream == :relay
52
+ end
53
+
54
+ run.on_finish do |upstream|
55
+ :close if upstream == :relay
56
+ end
57
+ end
data/lib/redis-ha.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'redis_ha/version'
2
+ require 'redis_ha/logger'
3
+
4
+ module RedisHA
5
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_support/concern'
2
+ require 'logger'
3
+
4
+ module RedisHA
5
+ module Logger
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ def logger
10
+ @logger ||= begin
11
+ log = ::Logger.new(STDOUT)
12
+ log.level = (@debug ? ::Logger::DEBUG : ::Logger::INFO)
13
+ log
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,27 @@
1
+ require 'eventmachine'
2
+ require 'socket'
3
+
4
+ require 'redis_ha/router/upstream'
5
+ require 'redis_ha/router/connection'
6
+
7
+ module RedisHA
8
+ module Router
9
+
10
+ def self.start host, port, options={}, &block
11
+ EM.epoll
12
+ EM.run do
13
+ trap("TERM") { stop }
14
+ trap("INT") { stop }
15
+
16
+ EventMachine::start_server(host, port, RedisHA::Router::Connection, options[:debug]) do |serv|
17
+ serv.instance_eval &block
18
+ end
19
+ end
20
+ end
21
+
22
+ def self.stop
23
+ EventMachine.stop
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,101 @@
1
+ module RedisHA
2
+ module Router
3
+ class Connection < EventMachine::Connection
4
+ include RedisHA::Logger
5
+
6
+ def on_data(&block); @on_data = block; end
7
+ def on_response(&block); @on_response = block; end
8
+ def on_finish(&block); @on_finish = block; end
9
+ def on_connect(&block); @on_connect = block; end
10
+
11
+ def initialize(debug_on=false)
12
+ @debug = debug_on
13
+ @upstreams = {}
14
+ end
15
+
16
+ def receive_data data
17
+ logger.debug [:connection, data]
18
+ processed = @on_data.call(data) if @on_data
19
+
20
+ return if processed == :async or processed.nil?
21
+ relay_to_upstreams(processed)
22
+ end
23
+
24
+ def relay_to_upstreams processed
25
+ if processed.is_a? Array
26
+ data, servers = *processed
27
+ servers = servers.collect {|s| @upstreams[s]}.compact
28
+ else
29
+ data = processed
30
+ servers ||= @upstreams.values.compact
31
+ end
32
+
33
+ servers.each do |s|
34
+ s.send_data data unless data.nil?
35
+ end
36
+ end
37
+
38
+ def upstream name, options
39
+ serv = EventMachine::bind_connect(*build_upstream_signature(options)) do |c|
40
+ c.name = name
41
+ c.plexer = self
42
+ c.proxy_incoming_to(self, 10240) if options[:relay_server]
43
+ end
44
+
45
+ self.proxy_incoming_to(serv, 10240) if options[:relay_client]
46
+
47
+ @upstreams[name] = serv
48
+ end
49
+
50
+ def peer
51
+ @peer ||= begin
52
+ peername = get_peername
53
+ peername ? Socket.unpack_sockaddr_in(peername).reverse : nil
54
+ end
55
+ end
56
+
57
+ def relay_from_upstream name, data
58
+ logger.debug [:relay_from_upsteam, name, data]
59
+ data = @on_response.call(name, data) if @on_response
60
+ send_data data unless data.nil?
61
+ end
62
+
63
+ def connected name
64
+ logger.debug [:connected]
65
+ @on_connect.call(name) if @on_connect
66
+ end
67
+
68
+ def unbind
69
+ logger.debug [:unbind, :connection]
70
+
71
+ @upstreams.values.compact.each do |serv|
72
+ serv.close_connection
73
+ end
74
+ end
75
+
76
+ def unbind_backend name
77
+ logger.debug [:unbind_backend, name]
78
+ @upstreams[name] = nil
79
+ close = :close
80
+
81
+ if @on_finish
82
+ close = @on_finish.call(name)
83
+ end
84
+
85
+ if (@upstreams.values.compact.size.zero? && close != :keep) || close == :close
86
+ close_connection_after_writing
87
+ end
88
+ end
89
+
90
+ private
91
+ def build_upstream_signature options
92
+ [options[:bind_host],
93
+ options[:bind_port],
94
+ options[:host],
95
+ options[:port],
96
+ RedisHA::Router::Upstream,
97
+ @debug]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,34 @@
1
+ module RedisHA
2
+ module Router
3
+ class Upstream < EventMachine::Connection
4
+ include RedisHA::Logger
5
+
6
+ attr_accessor :plexer, :name, :debug
7
+
8
+ def initialize debug_on=false
9
+ @debug = debug_on
10
+ @connected = EM::DefaultDeferrable.new
11
+ end
12
+
13
+ def connection_completed
14
+ logger.debug [@name, :conn_complete]
15
+ @plexer.connected(@name)
16
+ @connected.succeed
17
+ end
18
+
19
+ def receive_data data
20
+ logger.debug [@name, data]
21
+ @plexer.relay_from_upstream(@name, data)
22
+ end
23
+
24
+ def send data
25
+ @connected.callback { send_data data }
26
+ end
27
+
28
+ def ubind
29
+ logger.debug [@name, :unbind]
30
+ @plexer.unbind_backend(@name)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,3 @@
1
+ module RedisHA
2
+ VERSION = "0.0.1"
3
+ end
data/redis-ha.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'redis_ha/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "redis-ha"
8
+ spec.version = RedisHA::VERSION
9
+ spec.authors = ["John Maxwell"]
10
+ spec.email = ["john@musicglue.com"]
11
+ spec.description = %q{RedisHA is an attempt at a highly available Redis Installation}
12
+ spec.summary = %q{See Description}
13
+ spec.homepage = "http://musicglue.com"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency "eventmachine"
25
+ spec.add_dependency "activesupport"
26
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-ha
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - John Maxwell
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ version_requirements: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ none: false
22
+ requirement: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ none: false
28
+ prerelease: false
29
+ type: :development
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ version_requirements: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ none: false
38
+ requirement: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ none: false
44
+ prerelease: false
45
+ type: :development
46
+ - !ruby/object:Gem::Dependency
47
+ name: eventmachine
48
+ version_requirements: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ none: false
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ! '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ none: false
60
+ prerelease: false
61
+ type: :runtime
62
+ - !ruby/object:Gem::Dependency
63
+ name: activesupport
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ none: false
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ none: false
76
+ prerelease: false
77
+ type: :runtime
78
+ description: RedisHA is an attempt at a highly available Redis Installation
79
+ email:
80
+ - john@musicglue.com
81
+ executables:
82
+ - redis-ha
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - Gemfile
88
+ - LICENSE.txt
89
+ - README.md
90
+ - Rakefile
91
+ - bin/redis-ha
92
+ - lib/redis-ha.rb
93
+ - lib/redis_ha/logger.rb
94
+ - lib/redis_ha/router.rb
95
+ - lib/redis_ha/router/connection.rb
96
+ - lib/redis_ha/router/upstream.rb
97
+ - lib/redis_ha/version.rb
98
+ - redis-ha.gemspec
99
+ homepage: http://musicglue.com
100
+ licenses:
101
+ - MIT
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ hash: 2179501369200017504
111
+ segments:
112
+ - 0
113
+ version: '0'
114
+ none: false
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ hash: 2179501369200017504
120
+ segments:
121
+ - 0
122
+ version: '0'
123
+ none: false
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 1.8.25
127
+ signing_key:
128
+ specification_version: 3
129
+ summary: See Description
130
+ test_files: []