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 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
@@ -0,0 +1,8 @@
1
+ # redis-spawn
2
+
3
+ An extension to redis-rb to facilitate spawning a redis-server specifically
4
+ for your app.
5
+
6
+ Phil Stewart, 201110
7
+
8
+ License: MIT
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
@@ -0,0 +1,5 @@
1
+ class Redis
2
+ class SpawnServer
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ $:.unshift File.expand_path('../lib', File.dirname(__FILE__))
2
+
3
+ require 'redis/spawn'
4
+
@@ -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
+