redis-spawn 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/LICENSE +21 -0
- data/README +8 -0
- data/Rakefile +17 -0
- data/lib/redis/spawn/version.rb +5 -0
- data/lib/redis/spawn.rb +191 -0
- data/test/build_config_helper.rb +14 -0
- data/test/build_config_test.rb +80 -0
- data/test/helper.rb +4 -0
- data/test/spawn_test.rb +8 -0
- data/test/write_config_test.rb +27 -0
- metadata +101 -0
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (C) 2011 by Phil Stewart
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal
|
6
|
+
in the Software without restriction, including without limitation the rights
|
7
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the Software is
|
9
|
+
furnished to do so, subject to the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be included in
|
12
|
+
all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
19
|
+
FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
$:.unshift File.join(File.dirname(__FILE__), 'lib')
|
6
|
+
require 'redis/spawn'
|
7
|
+
|
8
|
+
task :test do
|
9
|
+
require 'cutest'
|
10
|
+
|
11
|
+
Cutest.run(Dir["./test/**/*_test.rb"])
|
12
|
+
end
|
13
|
+
|
14
|
+
task :pry do
|
15
|
+
require 'pry'
|
16
|
+
binding.pry
|
17
|
+
end
|
data/lib/redis/spawn.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require "redis"
|
2
|
+
|
3
|
+
class Redis
|
4
|
+
class SpawnServer
|
5
|
+
@_default_server_opts = {
|
6
|
+
port: "0",
|
7
|
+
bind: "127.0.0.1",
|
8
|
+
unixsocket: "/tmp/redis-spawned.#{Process.pid}.sock",
|
9
|
+
loglevel: "notice",
|
10
|
+
logfile: "/tmp/redis-spawned.#{Process.pid}.log",
|
11
|
+
databases: "16",
|
12
|
+
save: ["900 1", "300 10", "60 10000"],
|
13
|
+
rdbcompression: "yes",
|
14
|
+
dbfilename: "dump.rdb",
|
15
|
+
dir: "/tmp/redis-spawned.#{Process.pid}.data",
|
16
|
+
appendonly: "no",
|
17
|
+
appendfsync: "everysec",
|
18
|
+
vm_enabled: "no",
|
19
|
+
hash_max_zipmap_entries: "512",
|
20
|
+
hash_max_zipmap_value: "64",
|
21
|
+
list_max_ziplist_entries: "512",
|
22
|
+
list_max_ziplist_value: "64",
|
23
|
+
set_max_intset_entries: "512",
|
24
|
+
activerehashing: "yes"
|
25
|
+
}
|
26
|
+
|
27
|
+
@_running_servers = {}
|
28
|
+
|
29
|
+
def self.default_server_opts
|
30
|
+
@_default_server_opts
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.default_server_opts=(new_defaults_hash)
|
34
|
+
@_default_server_opts = new_defaults_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
# Build a configuration file line
|
38
|
+
#
|
39
|
+
# @param [Symbol, String] key: The configuration parameter. Underscores in
|
40
|
+
# the key are transposed to dashes
|
41
|
+
# @param [String, Object] value: The value to set for this configuration
|
42
|
+
# parameter
|
43
|
+
#
|
44
|
+
# @return A line of Redis server configuration data
|
45
|
+
def self.build_config_line(key, value)
|
46
|
+
key.to_s.gsub(/_/, "-") + " " + value.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
# Spawn a Redis server configured with the supplied options. By default,
|
50
|
+
# the spawned server will be a child of the current process and won't
|
51
|
+
# daemonize (@todo allow daemonization)
|
52
|
+
#
|
53
|
+
# @param supplied_opts: options for this server including any configuration overrides
|
54
|
+
#
|
55
|
+
# @return [SpawnServer] instance corresponding to the spawned server
|
56
|
+
def self.spawn(supplied_opts = {})
|
57
|
+
self.new(supplied_opts)
|
58
|
+
end
|
59
|
+
|
60
|
+
attr_reader :opts, :supplied_opts, :server_opts, :pid
|
61
|
+
|
62
|
+
def initialize(supplied_opts = {})
|
63
|
+
default_opts = {
|
64
|
+
generated_config_file: "/tmp/redis-spawned.#{Process.pid}.config",
|
65
|
+
cleanup_files: [:socket, :log, :config],
|
66
|
+
server_opts: {},
|
67
|
+
start: true
|
68
|
+
}
|
69
|
+
@supplied_opts = supplied_opts
|
70
|
+
@opts = default_opts.merge(supplied_opts)
|
71
|
+
self.server_opts = opts[:server_opts]
|
72
|
+
|
73
|
+
@pid = opts[:start] ? self.start : 0
|
74
|
+
|
75
|
+
# Return the instance
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
# Prepare a redis configuration file then start the server
|
80
|
+
#
|
81
|
+
# @return pid of the server
|
82
|
+
def start
|
83
|
+
# If config_file is passed in opts use it as the name of the config file.
|
84
|
+
# Otherwise, generate our own
|
85
|
+
@config_file = if opts.has_key?(:config_file)
|
86
|
+
# Don't attempt to cleanup files when supplied with pre-existing
|
87
|
+
# config file unless specifically asked
|
88
|
+
opts[:cleanup_files] = [] unless supplied_opts.has_key?(:cleanup_files)
|
89
|
+
opts[:config_file]
|
90
|
+
else
|
91
|
+
self.write_config
|
92
|
+
end
|
93
|
+
|
94
|
+
# Ensure the data directory exists
|
95
|
+
Dir.exists?(self.server_opts[:dir]) || Dir.mkdir(self.server_opts[:dir])
|
96
|
+
|
97
|
+
# Spawn the redis server for this instance
|
98
|
+
self.spawn
|
99
|
+
end
|
100
|
+
|
101
|
+
# Spawn a redis server. Only call this function once a config file exists
|
102
|
+
# and is specified
|
103
|
+
#
|
104
|
+
# @return the pid of the spawned server
|
105
|
+
def spawn
|
106
|
+
# Abort if there's no config file
|
107
|
+
unless @config_file && File.exist?(@config_file)
|
108
|
+
raise "Config file #{@config_file.inspect} not found"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Make sure we clean up after our children and avoid a zombie invasion
|
112
|
+
trap("CLD") do
|
113
|
+
pid = Process.wait
|
114
|
+
end
|
115
|
+
|
116
|
+
# Start the server
|
117
|
+
pid = fork { exec("redis-server #{@config_file}") }
|
118
|
+
#logger.info("Spawned redis server with PID #{pid}")
|
119
|
+
|
120
|
+
at_exit do
|
121
|
+
begin
|
122
|
+
Process.kill("TERM", pid) # Maybe make this configurable to allow the server to continue after exit
|
123
|
+
rescue Errno::ESRCH
|
124
|
+
# Already dead - do nothing
|
125
|
+
end
|
126
|
+
self.cleanup_files
|
127
|
+
end
|
128
|
+
|
129
|
+
pid
|
130
|
+
end
|
131
|
+
|
132
|
+
# Attribute write for server opts: merges supplied opts with defaults
|
133
|
+
# to create fully populated server opts
|
134
|
+
#
|
135
|
+
# @param [Hash] opts: partially populated server options hash
|
136
|
+
def server_opts=(opts)
|
137
|
+
@server_opts = self.class.default_server_opts.merge(opts)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Write the Redis configuration file to disk.
|
141
|
+
#
|
142
|
+
# @return the name of the written file
|
143
|
+
def write_config
|
144
|
+
File.open(self.opts[:generated_config_file], "w") do |file|
|
145
|
+
## @todo Migrate class based build_config to instance based build_config
|
146
|
+
file.write(self.build_config)
|
147
|
+
end
|
148
|
+
self.opts[:generated_config_file]
|
149
|
+
end
|
150
|
+
|
151
|
+
# Build configuration file data
|
152
|
+
#
|
153
|
+
# @return Redis server compatible configuration file data
|
154
|
+
def build_config
|
155
|
+
config_data = ""
|
156
|
+
self.server_opts.each do |key, value|
|
157
|
+
if value.kind_of?(Array)
|
158
|
+
value.each { |subvalue| config_data << self.class.build_config_line(key, subvalue) << "\n" }
|
159
|
+
else
|
160
|
+
config_data << self.class.build_config_line(key, value) << "\n"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
config_data
|
164
|
+
end
|
165
|
+
|
166
|
+
# Clean up server files associated with this instance. Expects
|
167
|
+
# #opts[:cleanup_files] to already be set up
|
168
|
+
def cleanup_files
|
169
|
+
files_from_symbols(opts[:cleanup_files]) do |file|
|
170
|
+
File.exist?(file) && File.delete(file)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Iterates over the supplied symbols and yields corresponding filenames
|
175
|
+
#
|
176
|
+
# @param [Array] file_syms: array of symbols to iterate over
|
177
|
+
def files_from_symbols(file_syms)
|
178
|
+
file_syms.each do |file_sym|
|
179
|
+
yield case file_sym
|
180
|
+
when :socket
|
181
|
+
server_opts[:unixsocket]
|
182
|
+
when :log
|
183
|
+
server_opts[:logfile]
|
184
|
+
when :config
|
185
|
+
@config_file || opts[:generated_config_file]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Monkeypatch Process to allow for consistent value of Process.pid in tests
|
2
|
+
module Process
|
3
|
+
class << self
|
4
|
+
alias :redis_spawn_original_pid :pid
|
5
|
+
|
6
|
+
def pid
|
7
|
+
if caller[0] =~ /redis\/spawn/
|
8
|
+
return 0
|
9
|
+
else
|
10
|
+
return redis_spawn_original_pid
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require File.expand_path("./build_config_helper", File.dirname(__FILE__))
|
2
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
3
|
+
|
4
|
+
test "build_config_line" do
|
5
|
+
assert "key value" == Redis::SpawnServer.build_config_line("key", "value")
|
6
|
+
assert "symbolkey value" == Redis::SpawnServer.build_config_line(:symbolkey, "value")
|
7
|
+
assert "symbol-with-underscores value" == Redis::SpawnServer.build_config_line(:symbol_with_underscores, "value")
|
8
|
+
assert "key 0" == Redis::SpawnServer.build_config_line("key", 0)
|
9
|
+
end
|
10
|
+
|
11
|
+
setup do
|
12
|
+
@test_config_defaults = <<TEST_CONF_END
|
13
|
+
port 0
|
14
|
+
bind 127.0.0.1
|
15
|
+
unixsocket /tmp/redis-spawned.0.sock
|
16
|
+
loglevel notice
|
17
|
+
logfile /tmp/redis-spawned.0.log
|
18
|
+
databases 16
|
19
|
+
save 900 1
|
20
|
+
save 300 10
|
21
|
+
save 60 10000
|
22
|
+
rdbcompression yes
|
23
|
+
dbfilename dump.rdb
|
24
|
+
dir /tmp/redis-spawned.0.data
|
25
|
+
appendonly no
|
26
|
+
appendfsync everysec
|
27
|
+
vm-enabled no
|
28
|
+
hash-max-zipmap-entries 512
|
29
|
+
hash-max-zipmap-value 64
|
30
|
+
list-max-ziplist-entries 512
|
31
|
+
list-max-ziplist-value 64
|
32
|
+
set-max-intset-entries 512
|
33
|
+
activerehashing yes
|
34
|
+
TEST_CONF_END
|
35
|
+
end
|
36
|
+
|
37
|
+
test "build_config with defaults" do
|
38
|
+
assert @test_config_defaults == Redis::SpawnServer.new(start: false).build_config
|
39
|
+
end
|
40
|
+
|
41
|
+
setup do
|
42
|
+
@test_config_defaults = <<TEST_CONF_END
|
43
|
+
port 0
|
44
|
+
bind 127.0.0.1
|
45
|
+
unixsocket /tmp/redis-spawned.override.sock
|
46
|
+
loglevel notice
|
47
|
+
logfile /tmp/redis-spawned.override.log
|
48
|
+
databases 8
|
49
|
+
save 900 1
|
50
|
+
save 300 10
|
51
|
+
save 100 1000
|
52
|
+
save 60 10000
|
53
|
+
rdbcompression no
|
54
|
+
dbfilename dump.rdb
|
55
|
+
dir /tmp/redis-spawned.override.data
|
56
|
+
appendonly no
|
57
|
+
appendfsync everysec
|
58
|
+
vm-enabled no
|
59
|
+
hash-max-zipmap-entries 512
|
60
|
+
hash-max-zipmap-value 64
|
61
|
+
list-max-ziplist-entries 512
|
62
|
+
list-max-ziplist-value 64
|
63
|
+
set-max-intset-entries 512
|
64
|
+
activerehashing yes
|
65
|
+
TEST_CONF_END
|
66
|
+
end
|
67
|
+
|
68
|
+
test "build_config with overrides" do
|
69
|
+
overrides = {
|
70
|
+
unixsocket: "/tmp/redis-spawned.override.sock",
|
71
|
+
logfile: "/tmp/redis-spawned.override.log",
|
72
|
+
databases: 8,
|
73
|
+
save: ["900 1", "300 10", "100 1000", "60 10000"],
|
74
|
+
rdbcompression: "no",
|
75
|
+
dir: "/tmp/redis-spawned.override.data"
|
76
|
+
}
|
77
|
+
assert @test_config_defaults == Redis::SpawnServer.new(start: false, server_opts: overrides).build_config
|
78
|
+
end
|
79
|
+
|
80
|
+
|
data/test/helper.rb
ADDED
data/test/spawn_test.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
2
|
+
|
3
|
+
test "spawn with defaults" do
|
4
|
+
spawn_instance = Redis::SpawnServer.new
|
5
|
+
assert_equal `ps -o cmd= -p #{spawn_instance.pid}`, "redis-server /tmp/redis-spawned.#{Process.pid}.config\n"
|
6
|
+
Process.kill("TERM", spawn_instance.pid)
|
7
|
+
end
|
8
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path("./helper", File.dirname(__FILE__))
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
setup do
|
5
|
+
testdir = "/tmp/redis-spawn-write-config-test.dir"
|
6
|
+
filename = "/tmp/redis-spawn-write-config-test.config"
|
7
|
+
spawn_instance = Redis::SpawnServer.new(start: false,
|
8
|
+
generated_config_file: filename,
|
9
|
+
server_opts: {dir: testdir})
|
10
|
+
Dir.exists?(testdir) && Dir.rmdir(testdir)
|
11
|
+
{
|
12
|
+
filename: filename,
|
13
|
+
spawn_instance: spawn_instance,
|
14
|
+
config_sha: Digest::SHA1.hexdigest(spawn_instance.build_config)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
test "write_config" do |params|
|
19
|
+
begin
|
20
|
+
filename = params[:spawn_instance].write_config
|
21
|
+
assert_equal filename, params[:filename]
|
22
|
+
assert_equal Digest::SHA1.hexdigest(File.read(filename)), params[:config_sha]
|
23
|
+
# assert Dir.exist?(params[:spawn_instance].server_opts[:dir])
|
24
|
+
ensure
|
25
|
+
File.delete(params[:filename])
|
26
|
+
end
|
27
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis-spawn
|
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
|
+
- Phil Stewart
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-10-26 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: redis-rb
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 0
|
31
|
+
version: "2.0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: cutest
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
segments:
|
43
|
+
- 0
|
44
|
+
- 1
|
45
|
+
version: "0.1"
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
48
|
+
description: Insert something more verbose than the summary here.
|
49
|
+
email:
|
50
|
+
- phil.stewart@lichp.co.uk
|
51
|
+
executables: []
|
52
|
+
|
53
|
+
extensions: []
|
54
|
+
|
55
|
+
extra_rdoc_files: []
|
56
|
+
|
57
|
+
files:
|
58
|
+
- lib/redis/spawn/version.rb
|
59
|
+
- lib/redis/spawn.rb
|
60
|
+
- README
|
61
|
+
- LICENSE
|
62
|
+
- Rakefile
|
63
|
+
- test/write_config_test.rb
|
64
|
+
- test/helper.rb
|
65
|
+
- test/spawn_test.rb
|
66
|
+
- test/build_config_helper.rb
|
67
|
+
- test/build_config_test.rb
|
68
|
+
has_rdoc: true
|
69
|
+
homepage: http://github.com/lichp/redis-spawn
|
70
|
+
licenses: []
|
71
|
+
|
72
|
+
post_install_message:
|
73
|
+
rdoc_options: []
|
74
|
+
|
75
|
+
require_paths:
|
76
|
+
- lib
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
segments:
|
83
|
+
- 0
|
84
|
+
version: "0"
|
85
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
+
none: false
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
requirements: []
|
94
|
+
|
95
|
+
rubyforge_project:
|
96
|
+
rubygems_version: 1.3.7
|
97
|
+
signing_key:
|
98
|
+
specification_version: 3
|
99
|
+
summary: An extension to redis-rb to facilitate spawning a redis-server specifically for your app.
|
100
|
+
test_files: []
|
101
|
+
|