redis_ring 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 +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +34 -0
- data/MIT-LICENSE.txt +20 -0
- data/Rakefile +11 -0
- data/bin/redis-ring +7 -0
- data/config/redis-ring.sample.yml +32 -0
- data/config/redis.conf.erb +49 -0
- data/lib/redis_ring/application.rb +47 -0
- data/lib/redis_ring/cli.rb +53 -0
- data/lib/redis_ring/configuration.rb +79 -0
- data/lib/redis_ring/shard.rb +61 -0
- data/lib/redis_ring/shard_config.rb +93 -0
- data/lib/redis_ring/version.rb +3 -0
- data/lib/redis_ring/web_interface.rb +16 -0
- data/lib/redis_ring.rb +14 -0
- data/redis_ring.gemspec +27 -0
- data/spec/redis_ring/application_spec.rb +23 -0
- data/spec/redis_ring/configuration_spec.rb +82 -0
- data/spec/redis_ring/shard_config_spec.rb +13 -0
- data/spec/redis_ring/shard_spec.rb +49 -0
- data/spec/spec_helper.rb +8 -0
- metadata +137 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
redis_ring (0.0.1)
|
5
|
+
json
|
6
|
+
sinatra
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: http://rubygems.org/
|
10
|
+
specs:
|
11
|
+
diff-lcs (1.1.2)
|
12
|
+
json (1.5.1)
|
13
|
+
mocha (0.9.12)
|
14
|
+
rack (1.2.1)
|
15
|
+
rspec (2.5.0)
|
16
|
+
rspec-core (~> 2.5.0)
|
17
|
+
rspec-expectations (~> 2.5.0)
|
18
|
+
rspec-mocks (~> 2.5.0)
|
19
|
+
rspec-core (2.5.1)
|
20
|
+
rspec-expectations (2.5.0)
|
21
|
+
diff-lcs (~> 1.1.2)
|
22
|
+
rspec-mocks (2.5.0)
|
23
|
+
sinatra (1.1.3)
|
24
|
+
rack (~> 1.1)
|
25
|
+
tilt (>= 1.2.2, < 2.0)
|
26
|
+
tilt (1.2.2)
|
27
|
+
|
28
|
+
PLATFORMS
|
29
|
+
ruby
|
30
|
+
|
31
|
+
DEPENDENCIES
|
32
|
+
mocha
|
33
|
+
redis_ring!
|
34
|
+
rspec
|
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Adam Pohorecki, http://adam.pohorecki.pl/
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core'
|
5
|
+
require 'rspec/core/rake_task'
|
6
|
+
|
7
|
+
desc "Run all specs"
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.pattern = "./spec/**/*_spec.rb"
|
10
|
+
t.rspec_opts = ["--profile --color"]
|
11
|
+
end
|
data/bin/redis-ring
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# By default RedisRing tries to guess the local IP address
|
2
|
+
# setting host_name should be necessary only if that is not possible
|
3
|
+
#host_name: localhost
|
4
|
+
|
5
|
+
# This is the port on which RedisRing publishes it's HTTP interface
|
6
|
+
# The Redis instances use ring_size next ports
|
7
|
+
base_port: 6400
|
8
|
+
|
9
|
+
# The number of Redis instances to start. Morte means more memory overhead (about 1MB per instance),
|
10
|
+
# but more means also easier moving around parts of the database
|
11
|
+
ring_size: 32
|
12
|
+
|
13
|
+
# By default which redis-server is used. Overwrite this setting if redis-server is not in PATH
|
14
|
+
#redis_path: /path/to/redis-server
|
15
|
+
|
16
|
+
# By default config/redis.conf.erb from the gem is used, but you can also supply your own.
|
17
|
+
# You can also change the redis server settings using the shared_config.conf file in base directory
|
18
|
+
#redis_config_template_path: /path/to/redis.conf.erb
|
19
|
+
|
20
|
+
# You can specify how much should Virtual Memory take in bytes (overall, not per Redis instance)
|
21
|
+
total_vm_size: 8589934592 #8GB
|
22
|
+
vm_page_size: 32
|
23
|
+
|
24
|
+
# This is the root directory under which all files will be stored
|
25
|
+
base_directory: /var/lib/redis
|
26
|
+
|
27
|
+
# Like with Virtual Memory, you can also specify how much memory all the Redis instances should take together
|
28
|
+
total_max_memory: 1073741824 # 1GB
|
29
|
+
|
30
|
+
# This is the password that will be used by all Redis instances. Remember that a huge number of passwords can be
|
31
|
+
# chacked in a second, so use a strong one. By default there is no password.
|
32
|
+
#password: letmein
|
@@ -0,0 +1,49 @@
|
|
1
|
+
daemonize no
|
2
|
+
|
3
|
+
port <%= port %>
|
4
|
+
logfile <%= log_file %>
|
5
|
+
dir <%= working_directory %>
|
6
|
+
vm-swap-file <%= vm_swap_file %>
|
7
|
+
vm-max-memory <%= vm_max_memory %>
|
8
|
+
vm-pages <%= vm_pages %>
|
9
|
+
vm-page-size <%= vm_page_size %>
|
10
|
+
dbfilename <%= db_file_name %>
|
11
|
+
appendfilename <%= aof_file_name %>
|
12
|
+
# slaveof <masterip> <masterport>
|
13
|
+
<% if password %>
|
14
|
+
requirepass <%= password %>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
timeout 300
|
18
|
+
|
19
|
+
loglevel notice
|
20
|
+
|
21
|
+
databases 2048
|
22
|
+
|
23
|
+
save 900 1
|
24
|
+
save 300 10
|
25
|
+
save 60 10000
|
26
|
+
|
27
|
+
rdbcompression yes
|
28
|
+
|
29
|
+
# maxclients 128
|
30
|
+
# maxmemory <bytes>
|
31
|
+
|
32
|
+
appendonly no
|
33
|
+
|
34
|
+
# The name of the append only file (default: "appendonly.aof")
|
35
|
+
|
36
|
+
# appendfsync always
|
37
|
+
appendfsync everysec
|
38
|
+
# appendfsync no
|
39
|
+
|
40
|
+
vm-enabled yes
|
41
|
+
vm-max-threads 4
|
42
|
+
|
43
|
+
glueoutputbuf yes
|
44
|
+
hash-max-zipmap-entries 64
|
45
|
+
hash-max-zipmap-value 512
|
46
|
+
|
47
|
+
activerehashing yes
|
48
|
+
|
49
|
+
include <%= common_config_path %>
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class Application
|
4
|
+
|
5
|
+
attr_reader :shards, :configuration
|
6
|
+
|
7
|
+
def initialize(configuration)
|
8
|
+
@configuration = configuration
|
9
|
+
@shards = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def start
|
13
|
+
self.stop
|
14
|
+
|
15
|
+
@configuration.ring_size.times do |shard_number|
|
16
|
+
shard_conf = ShardConfig.new(shard_number, configuration)
|
17
|
+
@shards[shard_number] = Shard.new(shard_conf)
|
18
|
+
end
|
19
|
+
|
20
|
+
@shards.each do |shard_no, shard|
|
21
|
+
shard.start
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def stop
|
26
|
+
@shards.each do |shard_no, shard|
|
27
|
+
shard.stop
|
28
|
+
end
|
29
|
+
@shards = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def shards_hash
|
33
|
+
shards_hash = {}
|
34
|
+
shards.each do |shard_no, shard|
|
35
|
+
shards_hash[shard_no] = { :host => shard.host, :port => shard.port, :status => shard.status }
|
36
|
+
end
|
37
|
+
|
38
|
+
return { :count => configuration.ring_size, :shards => shards_hash }
|
39
|
+
end
|
40
|
+
|
41
|
+
class << self
|
42
|
+
attr_accessor :instance
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class CLI
|
4
|
+
|
5
|
+
COMMANDS = [:help, :start]
|
6
|
+
|
7
|
+
attr_reader :argv
|
8
|
+
|
9
|
+
def initialize(argv)
|
10
|
+
@argv = argv
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
command = argv[0]
|
15
|
+
if command.nil? || !COMMANDS.include?(command.to_sym)
|
16
|
+
usage
|
17
|
+
exit(1)
|
18
|
+
else
|
19
|
+
send(command, *argv[1..-1])
|
20
|
+
exit(0)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
protected
|
25
|
+
|
26
|
+
def help
|
27
|
+
usage
|
28
|
+
end
|
29
|
+
|
30
|
+
def usage
|
31
|
+
puts <<USAGE
|
32
|
+
Usage:
|
33
|
+
#{$0} command [arguments]
|
34
|
+
|
35
|
+
Commands:
|
36
|
+
help - prints this message
|
37
|
+
|
38
|
+
start [config_file] - starts application
|
39
|
+
USAGE
|
40
|
+
end
|
41
|
+
|
42
|
+
def start(config_file = nil)
|
43
|
+
config = config_file ? Configuration.from_yml_file(config_file) : Configuration.new
|
44
|
+
|
45
|
+
Application.instance = Application.new(config)
|
46
|
+
Application.instance.start
|
47
|
+
|
48
|
+
WebInterface.run!(:port => config.base_port)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class ConfigurationError < StandardError; end
|
4
|
+
class RedisNotFound < ConfigurationError; end
|
5
|
+
class UnknownConfigurationParameter < ConfigurationError; end
|
6
|
+
|
7
|
+
class Configuration
|
8
|
+
|
9
|
+
PARAMETERS = [:host_name, :base_port, :ring_size, :redis_path, :redis_config_template_path,
|
10
|
+
:total_vm_size, :base_directory, :password, :total_max_memory, :vm_page_size]
|
11
|
+
|
12
|
+
attr_reader *PARAMETERS
|
13
|
+
|
14
|
+
def initialize(params = {})
|
15
|
+
set_params(params)
|
16
|
+
set_defaults
|
17
|
+
validate!
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.from_yml_file(file_name)
|
21
|
+
return from_yml(File.read(file_name))
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.from_yml(string)
|
25
|
+
args = YAML::load(string)
|
26
|
+
return new(args)
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
attr_writer *PARAMETERS
|
32
|
+
|
33
|
+
def set_params(params)
|
34
|
+
params.each do |param, value|
|
35
|
+
if PARAMETERS.include?(param.to_sym)
|
36
|
+
self.send("#{param}=", value)
|
37
|
+
else
|
38
|
+
raise UnknownConfigurationParameter.new("Unknown configuration parameter: #{param.inspect}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_defaults
|
44
|
+
self.host_name ||= guess_host_name
|
45
|
+
self.base_port ||= 6400
|
46
|
+
self.ring_size ||= 32
|
47
|
+
self.redis_path ||= locate_redis
|
48
|
+
self.redis_config_template_path ||= default_redis_config_template_path
|
49
|
+
self.total_vm_size ||= 8 * 1024 * 1024 * 1024 # 8GB
|
50
|
+
self.base_directory ||= "/var/lib/redis"
|
51
|
+
self.total_max_memory ||= 1024 * 1024 * 1024 # 1GB
|
52
|
+
self.vm_page_size ||= 32
|
53
|
+
end
|
54
|
+
|
55
|
+
def validate!
|
56
|
+
raise RedisNotFound.new("redis_path is invalid (not found)") unless File.file?(redis_path)
|
57
|
+
end
|
58
|
+
|
59
|
+
def guess_host_name
|
60
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
61
|
+
UDPSocket.open do |s|
|
62
|
+
s.connect '64.233.187.99', 1
|
63
|
+
return s.addr.last
|
64
|
+
end
|
65
|
+
ensure
|
66
|
+
Socket.do_not_reverse_lookup = orig
|
67
|
+
end
|
68
|
+
|
69
|
+
def locate_redis
|
70
|
+
return %x[which redis-server].strip
|
71
|
+
end
|
72
|
+
|
73
|
+
def default_redis_config_template_path
|
74
|
+
File.expand_path('../../../config/redis.conf.erb', __FILE__)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class Shard
|
4
|
+
|
5
|
+
attr_reader :shard_config, :pid
|
6
|
+
|
7
|
+
def initialize(shard_config)
|
8
|
+
@shard_config = shard_config
|
9
|
+
@status = :stopped
|
10
|
+
end
|
11
|
+
|
12
|
+
def shard_number
|
13
|
+
shard_config.shard_number
|
14
|
+
end
|
15
|
+
|
16
|
+
def host
|
17
|
+
shard_config.host
|
18
|
+
end
|
19
|
+
|
20
|
+
def port
|
21
|
+
shard_config.port
|
22
|
+
end
|
23
|
+
|
24
|
+
def status
|
25
|
+
if @status == :stopped
|
26
|
+
return alive? ? :stopping : :stopped
|
27
|
+
elsif @status == :started
|
28
|
+
return alive? ? :running : :dead
|
29
|
+
else
|
30
|
+
raise RuntimeException.new("Unknown status: #{@status.inspect}")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def start
|
35
|
+
shard_config.save
|
36
|
+
@pid = fork_redis_server
|
37
|
+
@status = :started
|
38
|
+
end
|
39
|
+
|
40
|
+
def stop
|
41
|
+
send_kill_signal
|
42
|
+
@status = :stopped
|
43
|
+
end
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def alive?
|
48
|
+
@pid && File.exist?("/proc/#{@pid}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def fork_redis_server
|
52
|
+
spawn(shard_config.redis_path, shard_config.config_file_name)
|
53
|
+
end
|
54
|
+
|
55
|
+
def send_kill_signal
|
56
|
+
system("kill -QUIT #{pid}")
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module RedisRing
|
2
|
+
|
3
|
+
class ShardConfig
|
4
|
+
|
5
|
+
attr_reader :shard_number, :configuration
|
6
|
+
|
7
|
+
def initialize(shard_number, configuration)
|
8
|
+
@shard_number = shard_number
|
9
|
+
@configuration = configuration
|
10
|
+
end
|
11
|
+
|
12
|
+
def render
|
13
|
+
template = ERB.new(File.read(configuration.redis_config_template_path))
|
14
|
+
return template.result(binding)
|
15
|
+
end
|
16
|
+
|
17
|
+
def save
|
18
|
+
FileUtils.mkdir_p(working_directory)
|
19
|
+
|
20
|
+
['configs', 'logs', 'vm_files', 'db_files'].each do |dir_name|
|
21
|
+
FileUtils.mkdir_p(File.join(configuration.base_directory, dir_name))
|
22
|
+
end
|
23
|
+
|
24
|
+
FileUtils.touch(common_config_path) unless File.exist?(common_config_path)
|
25
|
+
|
26
|
+
File.open(config_file_name, 'w') { |f| f.write(render) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def config_file_name
|
30
|
+
File.join(configuration.base_directory, 'configs', "shard-#{shard_number}.conf")
|
31
|
+
end
|
32
|
+
|
33
|
+
def host
|
34
|
+
configuration.host_name
|
35
|
+
end
|
36
|
+
|
37
|
+
def port
|
38
|
+
configuration.base_port + shard_number + 1
|
39
|
+
end
|
40
|
+
|
41
|
+
def redis_path
|
42
|
+
configuration.redis_path
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_file
|
46
|
+
File.expand_path(file('logs', "shard-#{shard_number}.log"), working_directory)
|
47
|
+
end
|
48
|
+
|
49
|
+
def working_directory
|
50
|
+
"#{configuration.base_directory}/work/shard-#{shard_number}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def vm_swap_file
|
54
|
+
file('vm_files', "shard-#{shard_number}.swap")
|
55
|
+
end
|
56
|
+
|
57
|
+
def vm_max_memory
|
58
|
+
configuration.total_max_memory / configuration.ring_size
|
59
|
+
end
|
60
|
+
|
61
|
+
def vm_pages
|
62
|
+
configuration.total_vm_size / configuration.vm_page_size / configuration.ring_size
|
63
|
+
end
|
64
|
+
|
65
|
+
def vm_page_size
|
66
|
+
configuration.vm_page_size
|
67
|
+
end
|
68
|
+
|
69
|
+
def db_file_name
|
70
|
+
file('db_files', "shard-#{shard_number}.rdb")
|
71
|
+
end
|
72
|
+
|
73
|
+
def aof_file_name
|
74
|
+
file('db_files', "shard-#{shard_number}.aof")
|
75
|
+
end
|
76
|
+
|
77
|
+
def password
|
78
|
+
configuration.password
|
79
|
+
end
|
80
|
+
|
81
|
+
def common_config_path
|
82
|
+
File.join(configuration.base_directory, "shared_config.conf")
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
def file(*parts)
|
88
|
+
File.join('..', '..', *parts)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/lib/redis_ring.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'yaml'
|
3
|
+
require 'erb'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
require 'sinatra'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
require 'redis_ring/configuration'
|
10
|
+
require 'redis_ring/shard_config'
|
11
|
+
require 'redis_ring/shard'
|
12
|
+
require 'redis_ring/application'
|
13
|
+
require 'redis_ring/web_interface'
|
14
|
+
require 'redis_ring/cli'
|
data/redis_ring.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "redis_ring/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "redis_ring"
|
7
|
+
s.version = RedisRing::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Adam Pohorecki"]
|
10
|
+
s.email = ["adam@pohorecki.pl"]
|
11
|
+
s.homepage = "http://github.com/psyho/redis_ring"
|
12
|
+
s.summary = %q{A simplistic solution to redis sharding}
|
13
|
+
s.description = %q{RedisRing is a solution to run multiple small Redis instances intead of a single large one.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "redis_ring"
|
16
|
+
|
17
|
+
s.add_dependency 'sinatra'
|
18
|
+
s.add_dependency 'json'
|
19
|
+
|
20
|
+
s.add_development_dependency 'rspec'
|
21
|
+
s.add_development_dependency 'mocha'
|
22
|
+
|
23
|
+
s.files = `git ls-files`.split("\n")
|
24
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
25
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
s.require_paths = ["lib"]
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::Application do
|
4
|
+
|
5
|
+
describe "#shards_hash" do
|
6
|
+
before(:each) do
|
7
|
+
RedisRing::Shard.any_instance.stubs(:fork_redis_server => 123)
|
8
|
+
RedisRing::ShardConfig.any_instance.stubs(:save)
|
9
|
+
RedisRing::ShardConfig.any_instance.stubs(:alive? => true)
|
10
|
+
|
11
|
+
@application = RedisRing::Application.new(RedisRing::Configuration.new)
|
12
|
+
@application.start
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return all shards" do
|
16
|
+
shard_hash = @application.shards_hash
|
17
|
+
|
18
|
+
shard_hash[:count].should == @application.configuration.ring_size
|
19
|
+
shard_hash[:shards].size.should == @application.configuration.ring_size
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::Configuration do
|
4
|
+
describe "defaults" do
|
5
|
+
before(:each) do
|
6
|
+
@config = RedisRing::Configuration.new
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should have default port" do
|
10
|
+
@config.base_port.should_not be_nil
|
11
|
+
@config.base_port.should be_an_instance_of(Fixnum)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should have default host" do
|
15
|
+
@config.host_name.should_not be_nil
|
16
|
+
@config.host_name.should =~ /\d+\.\d+\.\d+.\d+/
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should have default ring_size" do
|
20
|
+
@config.ring_size.should_not be_nil
|
21
|
+
@config.base_port.should be_an_instance_of(Fixnum)
|
22
|
+
@config.base_port.should > 0
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should have default redis_path" do
|
26
|
+
@config.redis_path.should_not be_nil
|
27
|
+
File.exist?(@config.redis_path).should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have default redis_config_template_path" do
|
31
|
+
@config.redis_config_template_path.should_not be_nil
|
32
|
+
File.exist?(@config.redis_config_template_path).should be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should have default total_vm_size" do
|
36
|
+
@config.total_vm_size.should_not be_nil
|
37
|
+
@config.total_vm_size.should > 0
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should have default base_directory" do
|
41
|
+
@config.base_directory.should_not be_nil
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should have no password by default" do
|
45
|
+
@config.password.should be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have default total_memory" do
|
49
|
+
@config.total_max_memory.should_not be_nil
|
50
|
+
@config.total_max_memory.should > 0
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should have default vm_page_size" do
|
54
|
+
@config.vm_page_size.should_not be_nil
|
55
|
+
@config.vm_page_size.should > 0
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should rise RedisNotFound exception if redis-server not found" do
|
60
|
+
lambda {
|
61
|
+
RedisRing::Configuration.new(:redis_path => '/this/does/not/exist')
|
62
|
+
}.should raise_exception(RedisRing::RedisNotFound)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should rise UnknownConfigurationParameter exception if an unknown configuration parameter is given" do
|
66
|
+
lambda {
|
67
|
+
RedisRing::Configuration.new(:unknown_parameter => 'some value')
|
68
|
+
}.should raise_exception(RedisRing::UnknownConfigurationParameter)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should load yml config" do
|
72
|
+
yml_string = <<-YML
|
73
|
+
base_port: 666
|
74
|
+
base_directory: /home/psyho/redis
|
75
|
+
YML
|
76
|
+
|
77
|
+
config = RedisRing::Configuration.from_yml(yml_string)
|
78
|
+
|
79
|
+
config.base_port.should == 666
|
80
|
+
config.base_directory.should == '/home/psyho/redis'
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::ShardConfig do
|
4
|
+
it "should render redis config with default config variables" do
|
5
|
+
config = RedisRing::Configuration.new
|
6
|
+
shard_config = RedisRing::ShardConfig.new(0, config)
|
7
|
+
|
8
|
+
redis_conf = shard_config.render
|
9
|
+
|
10
|
+
redis_conf.should include((config.base_port + 1).to_s)
|
11
|
+
redis_conf.should include(config.base_directory)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require File.expand_path("../../spec_helper", __FILE__)
|
2
|
+
|
3
|
+
describe RedisRing::Shard do
|
4
|
+
|
5
|
+
describe "possible statuses" do
|
6
|
+
before(:each) do
|
7
|
+
@shard = RedisRing::Shard.new(RedisRing::ShardConfig.new(0, RedisRing::Configuration.new))
|
8
|
+
@pid = 123
|
9
|
+
@shard.shard_config.stubs(:save)
|
10
|
+
@shard.stubs(:fork_redis_server => @pid)
|
11
|
+
@shard.stubs(:send_kill_signal)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should be stopped initially" do
|
15
|
+
@shard.status.should == :stopped
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should be running if started and alive" do
|
19
|
+
@shard.stubs(:alive? => true)
|
20
|
+
@shard.start
|
21
|
+
|
22
|
+
@shard.status.should == :running
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should be stopping if started then stopped but still alive" do
|
26
|
+
@shard.stubs(:alive? => true)
|
27
|
+
@shard.start
|
28
|
+
@shard.stop
|
29
|
+
|
30
|
+
@shard.status.should == :stopping
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should be stopped if started then stopped and not alive" do
|
34
|
+
@shard.stubs(:alive? => false)
|
35
|
+
@shard.start
|
36
|
+
@shard.stop
|
37
|
+
|
38
|
+
@shard.status.should == :stopped
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be dead if started but not alive" do
|
42
|
+
@shard.stubs(:alive? => false)
|
43
|
+
@shard.start
|
44
|
+
|
45
|
+
@shard.status.should == :dead
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis_ring
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Adam Pohorecki
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-03-08 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: sinatra
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 0
|
30
|
+
version: "0"
|
31
|
+
type: :runtime
|
32
|
+
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: json
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
prerelease: false
|
49
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
segments:
|
55
|
+
- 0
|
56
|
+
version: "0"
|
57
|
+
type: :development
|
58
|
+
version_requirements: *id003
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: mocha
|
61
|
+
prerelease: false
|
62
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
type: :development
|
71
|
+
version_requirements: *id004
|
72
|
+
description: RedisRing is a solution to run multiple small Redis instances intead of a single large one.
|
73
|
+
email:
|
74
|
+
- adam@pohorecki.pl
|
75
|
+
executables:
|
76
|
+
- redis-ring
|
77
|
+
extensions: []
|
78
|
+
|
79
|
+
extra_rdoc_files: []
|
80
|
+
|
81
|
+
files:
|
82
|
+
- .gitignore
|
83
|
+
- Gemfile
|
84
|
+
- Gemfile.lock
|
85
|
+
- MIT-LICENSE.txt
|
86
|
+
- Rakefile
|
87
|
+
- bin/redis-ring
|
88
|
+
- config/redis-ring.sample.yml
|
89
|
+
- config/redis.conf.erb
|
90
|
+
- lib/redis_ring.rb
|
91
|
+
- lib/redis_ring/application.rb
|
92
|
+
- lib/redis_ring/cli.rb
|
93
|
+
- lib/redis_ring/configuration.rb
|
94
|
+
- lib/redis_ring/shard.rb
|
95
|
+
- lib/redis_ring/shard_config.rb
|
96
|
+
- lib/redis_ring/version.rb
|
97
|
+
- lib/redis_ring/web_interface.rb
|
98
|
+
- redis_ring.gemspec
|
99
|
+
- spec/redis_ring/application_spec.rb
|
100
|
+
- spec/redis_ring/configuration_spec.rb
|
101
|
+
- spec/redis_ring/shard_config_spec.rb
|
102
|
+
- spec/redis_ring/shard_spec.rb
|
103
|
+
- spec/spec_helper.rb
|
104
|
+
has_rdoc: true
|
105
|
+
homepage: http://github.com/psyho/redis_ring
|
106
|
+
licenses: []
|
107
|
+
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
|
111
|
+
require_paths:
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
segments:
|
127
|
+
- 0
|
128
|
+
version: "0"
|
129
|
+
requirements: []
|
130
|
+
|
131
|
+
rubyforge_project: redis_ring
|
132
|
+
rubygems_version: 1.3.7
|
133
|
+
signing_key:
|
134
|
+
specification_version: 3
|
135
|
+
summary: A simplistic solution to redis sharding
|
136
|
+
test_files: []
|
137
|
+
|