mmmurf-starling 0.9.7.10
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/CHANGELOG +37 -0
- data/LICENSE +20 -0
- data/README.rdoc +106 -0
- data/Rakefile +21 -0
- data/bin/starling +6 -0
- data/bin/starling_top +57 -0
- data/etc/starling.redhat +63 -0
- data/etc/starling.ubuntu +71 -0
- data/lib/starling.rb +104 -0
- data/lib/starling/handler.rb +222 -0
- data/lib/starling/persistent_queue.rb +151 -0
- data/lib/starling/queue_collection.rb +141 -0
- data/lib/starling/server.rb +113 -0
- data/lib/starling/server_runner.rb +292 -0
- data/test/test_starling_server.rb +205 -0
- metadata +108 -0
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'logger'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'eventmachine'
|
5
|
+
require 'analyzer_tools/syslog_logger'
|
6
|
+
|
7
|
+
here = File.dirname(__FILE__)
|
8
|
+
|
9
|
+
require File.join(here, 'queue_collection')
|
10
|
+
require File.join(here, 'handler')
|
11
|
+
|
12
|
+
module StarlingServer
|
13
|
+
|
14
|
+
VERSION = "0.9.7.9"
|
15
|
+
|
16
|
+
class Base
|
17
|
+
attr_reader :logger
|
18
|
+
|
19
|
+
DEFAULT_HOST = '127.0.0.1'
|
20
|
+
DEFAULT_PORT = 22122
|
21
|
+
DEFAULT_PATH = "/tmp/starling/"
|
22
|
+
DEFAULT_TIMEOUT = 60
|
23
|
+
|
24
|
+
##
|
25
|
+
# Initialize a new Starling server and immediately start processing
|
26
|
+
# requests.
|
27
|
+
#
|
28
|
+
# +opts+ is an optional hash, whose valid options are:
|
29
|
+
#
|
30
|
+
# [:host] Host on which to listen (default is 127.0.0.1).
|
31
|
+
# [:port] Port on which to listen (default is 22122).
|
32
|
+
# [:path] Path to Starling queue logs. Default is /tmp/starling/
|
33
|
+
# [:timeout] Time in seconds to wait before closing connections.
|
34
|
+
# [:logger] A Logger object, an IO handle, or a path to the log.
|
35
|
+
# [:loglevel] Logger verbosity. Default is Logger::ERROR.
|
36
|
+
#
|
37
|
+
# Other options are ignored.
|
38
|
+
|
39
|
+
def self.start(opts = {})
|
40
|
+
server = self.new(opts)
|
41
|
+
server.run
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# Initialize a new Starling server, but do not accept connections or
|
46
|
+
# process requests.
|
47
|
+
#
|
48
|
+
# +opts+ is as for +start+
|
49
|
+
|
50
|
+
def initialize(opts = {})
|
51
|
+
@opts = {
|
52
|
+
:host => DEFAULT_HOST,
|
53
|
+
:port => DEFAULT_PORT,
|
54
|
+
:path => DEFAULT_PATH,
|
55
|
+
:timeout => DEFAULT_TIMEOUT,
|
56
|
+
:server => self
|
57
|
+
}.merge(opts)
|
58
|
+
|
59
|
+
@stats = Hash.new(0)
|
60
|
+
|
61
|
+
FileUtils.mkdir_p(@opts[:path])
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
##
|
66
|
+
# Start listening and processing requests.
|
67
|
+
|
68
|
+
def run
|
69
|
+
@stats[:start_time] = Time.now
|
70
|
+
|
71
|
+
@@logger = case @opts[:logger]
|
72
|
+
when IO, String; Logger.new(@opts[:logger])
|
73
|
+
when Logger; @opts[:logger]
|
74
|
+
else; Logger.new(STDERR)
|
75
|
+
end
|
76
|
+
@@logger = SyslogLogger.new(@opts[:syslog_channel]) if @opts[:syslog_channel]
|
77
|
+
|
78
|
+
@opts[:queue] = QueueCollection.new(@opts[:path])
|
79
|
+
@@logger.level = @opts[:log_level] || Logger::ERROR
|
80
|
+
|
81
|
+
@@logger.info "Starling STARTUP on #{@opts[:host]}:#{@opts[:port]}"
|
82
|
+
|
83
|
+
EventMachine.epoll
|
84
|
+
EventMachine.set_descriptor_table_size(4096)
|
85
|
+
EventMachine.run do
|
86
|
+
EventMachine.start_server(@opts[:host], @opts[:port], Handler, @opts)
|
87
|
+
end
|
88
|
+
|
89
|
+
# code here will get executed on shutdown:
|
90
|
+
@opts[:queue].close
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.logger
|
94
|
+
@@logger
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
##
|
99
|
+
# Stop accepting new connections and shutdown gracefully.
|
100
|
+
|
101
|
+
def stop
|
102
|
+
EventMachine.stop_event_loop
|
103
|
+
end
|
104
|
+
|
105
|
+
def stats(stat = nil) #:nodoc:
|
106
|
+
case stat
|
107
|
+
when nil; @stats
|
108
|
+
when :connections; 1
|
109
|
+
else; @stats[stat]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,292 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'server')
|
2
|
+
require 'optparse'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
module StarlingServer
|
6
|
+
class Runner
|
7
|
+
|
8
|
+
attr_accessor :options
|
9
|
+
private :options, :options=
|
10
|
+
|
11
|
+
def self.run
|
12
|
+
new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.shutdown
|
16
|
+
@@instance.shutdown
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@@instance = self
|
21
|
+
parse_options
|
22
|
+
|
23
|
+
@process = ProcessHelper.new(options[:logger], options[:pid_file], options[:user], options[:group])
|
24
|
+
|
25
|
+
pid = @process.running?
|
26
|
+
if pid
|
27
|
+
STDERR.puts "There is already a Starling process running (pid #{pid}), exiting."
|
28
|
+
exit(1)
|
29
|
+
elsif pid.nil?
|
30
|
+
STDERR.puts "Cleaning up stale pidfile at #{options[:pid_file]}."
|
31
|
+
end
|
32
|
+
|
33
|
+
start
|
34
|
+
end
|
35
|
+
|
36
|
+
def load_config_file(filename)
|
37
|
+
YAML.load(File.open(filename))['starling'].each do |key, value|
|
38
|
+
# alias some keys
|
39
|
+
case key
|
40
|
+
when "queue_path" then key = "path"
|
41
|
+
when "log_file" then key = "logger"
|
42
|
+
end
|
43
|
+
options[key.to_sym] = value
|
44
|
+
|
45
|
+
if options[:log_level].instance_of?(String)
|
46
|
+
options[:log_level] = Logger.const_get(options[:log_level])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_options
|
52
|
+
self.options = { :host => '127.0.0.1',
|
53
|
+
:port => 22122,
|
54
|
+
:path => File.join(%w( / tmp starling spool )),
|
55
|
+
:log_level => Logger::INFO,
|
56
|
+
:daemonize => false,
|
57
|
+
:timeout => 0,
|
58
|
+
:pid_file => File.join(%w( / tmp starling starling.pid )) }
|
59
|
+
|
60
|
+
OptionParser.new do |opts|
|
61
|
+
opts.summary_width = 25
|
62
|
+
|
63
|
+
opts.banner = "Starling (#{StarlingServer::VERSION})\n\n",
|
64
|
+
"usage: starling [options...]\n",
|
65
|
+
" starling --help\n",
|
66
|
+
" starling --version\n"
|
67
|
+
|
68
|
+
opts.separator ""
|
69
|
+
opts.separator "Configuration:"
|
70
|
+
|
71
|
+
opts.on("-f", "--config FILENAME",
|
72
|
+
"Config file (yaml) to load") do |filename|
|
73
|
+
load_config_file(filename)
|
74
|
+
end
|
75
|
+
|
76
|
+
opts.on("-q", "--queue_path PATH",
|
77
|
+
:REQUIRED,
|
78
|
+
"Path to store Starling queue logs", "(default: #{options[:path]})") do |queue_path|
|
79
|
+
options[:path] = queue_path
|
80
|
+
end
|
81
|
+
|
82
|
+
opts.separator ""; opts.separator "Network:"
|
83
|
+
|
84
|
+
opts.on("-hHOST", "--host HOST", "Interface on which to listen (default: #{options[:host]})") do |host|
|
85
|
+
options[:host] = host
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on("-pHOST", "--port PORT", Integer, "TCP port on which to listen (default: #{options[:port]})") do |port|
|
89
|
+
options[:port] = port
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.separator ""; opts.separator "Process:"
|
93
|
+
|
94
|
+
opts.on("-d", "Run as a daemon.") do
|
95
|
+
options[:daemonize] = true
|
96
|
+
end
|
97
|
+
|
98
|
+
opts.on("-PFILE", "--pid FILENAME", "save PID in FILENAME when using -d option.", "(default: #{options[:pid_file]})") do |pid_file|
|
99
|
+
options[:pid_file] = pid_file
|
100
|
+
end
|
101
|
+
|
102
|
+
opts.on("-u", "--user USER", "User to run as") do |user|
|
103
|
+
options[:user] = user.to_i == 0 ? Etc.getpwnam(user).uid : user.to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
opts.on("-gGROUP", "--group GROUP", "Group to run as") do |group|
|
107
|
+
options[:group] = group.to_i == 0 ? Etc.getgrnam(group).gid : group.to_i
|
108
|
+
end
|
109
|
+
|
110
|
+
opts.separator ""; opts.separator "Logging:"
|
111
|
+
|
112
|
+
opts.on("-L", "--log [FILE]", "Path to print debugging information.") do |log_path|
|
113
|
+
options[:logger] = log_path
|
114
|
+
end
|
115
|
+
|
116
|
+
opts.on("-l", "--syslog CHANNEL", "Write logs to the syslog instead of a log file.") do |channel|
|
117
|
+
options[:syslog_channel] = channel
|
118
|
+
end
|
119
|
+
|
120
|
+
opts.on("-v", "Increase logging verbosity (may be used multiple times).") do
|
121
|
+
options[:log_level] -= 1
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.on("-t", "--timeout [SECONDS]", Integer,
|
125
|
+
"Time in seconds before disconnecting inactive clients (0 to disable).",
|
126
|
+
"(default: #{options[:timeout]})") do |timeout|
|
127
|
+
options[:timeout] = timeout
|
128
|
+
end
|
129
|
+
|
130
|
+
opts.separator ""; opts.separator "Miscellaneous:"
|
131
|
+
|
132
|
+
opts.on_tail("-?", "--help", "Display this usage information.") do
|
133
|
+
puts "#{opts}\n"
|
134
|
+
exit
|
135
|
+
end
|
136
|
+
|
137
|
+
opts.on_tail("-V", "--version", "Print version number and exit.") do
|
138
|
+
puts "Starling #{StarlingServer::VERSION}\n\n"
|
139
|
+
exit
|
140
|
+
end
|
141
|
+
end.parse!
|
142
|
+
end
|
143
|
+
|
144
|
+
def start
|
145
|
+
drop_privileges
|
146
|
+
|
147
|
+
@process.daemonize if options[:daemonize]
|
148
|
+
|
149
|
+
setup_signal_traps
|
150
|
+
@process.write_pid_file
|
151
|
+
|
152
|
+
STDOUT.puts "Starting at #{options[:host]}:#{options[:port]}."
|
153
|
+
@server = StarlingServer::Base.new(options)
|
154
|
+
@server.run
|
155
|
+
|
156
|
+
@process.remove_pid_file
|
157
|
+
end
|
158
|
+
|
159
|
+
def drop_privileges
|
160
|
+
Process.egid = options[:group] if options[:group]
|
161
|
+
Process.euid = options[:user] if options[:user]
|
162
|
+
end
|
163
|
+
|
164
|
+
def shutdown
|
165
|
+
begin
|
166
|
+
STDOUT.puts "Shutting down."
|
167
|
+
StarlingServer::Base.logger.info "Shutting down."
|
168
|
+
@server.stop
|
169
|
+
rescue Object => e
|
170
|
+
STDERR.puts "There was an error shutting down: #{e}"
|
171
|
+
exit(70)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def setup_signal_traps
|
176
|
+
Signal.trap("INT") { shutdown }
|
177
|
+
Signal.trap("TERM") { shutdown }
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
class ProcessHelper
|
182
|
+
|
183
|
+
def initialize(log_file = nil, pid_file = nil, user = nil, group = nil)
|
184
|
+
@log_file = log_file
|
185
|
+
@pid_file = pid_file
|
186
|
+
@user = user
|
187
|
+
@group = group
|
188
|
+
end
|
189
|
+
|
190
|
+
def safefork
|
191
|
+
begin
|
192
|
+
if pid = fork
|
193
|
+
return pid
|
194
|
+
end
|
195
|
+
rescue Errno::EWOULDBLOCK
|
196
|
+
sleep 5
|
197
|
+
retry
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def daemonize
|
202
|
+
sess_id = detach_from_terminal
|
203
|
+
exit if pid = safefork
|
204
|
+
|
205
|
+
Dir.chdir("/")
|
206
|
+
File.umask 0000
|
207
|
+
|
208
|
+
close_io_handles
|
209
|
+
redirect_io
|
210
|
+
|
211
|
+
return sess_id
|
212
|
+
end
|
213
|
+
|
214
|
+
def detach_from_terminal
|
215
|
+
srand
|
216
|
+
safefork and exit
|
217
|
+
|
218
|
+
unless sess_id = Process.setsid
|
219
|
+
raise "Couldn't detache from controlling terminal."
|
220
|
+
end
|
221
|
+
|
222
|
+
trap 'SIGHUP', 'IGNORE'
|
223
|
+
|
224
|
+
sess_id
|
225
|
+
end
|
226
|
+
|
227
|
+
def close_io_handles
|
228
|
+
ObjectSpace.each_object(IO) do |io|
|
229
|
+
unless [STDIN, STDOUT, STDERR].include?(io)
|
230
|
+
begin
|
231
|
+
io.close unless io.closed?
|
232
|
+
rescue Exception
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def redirect_io
|
239
|
+
begin; STDIN.reopen('/dev/null'); rescue Exception; end
|
240
|
+
|
241
|
+
if @log_file
|
242
|
+
begin
|
243
|
+
STDOUT.reopen(@log_file, "a")
|
244
|
+
STDOUT.sync = true
|
245
|
+
rescue Exception
|
246
|
+
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
247
|
+
end
|
248
|
+
else
|
249
|
+
begin; STDOUT.reopen('/dev/null'); rescue Exception; end
|
250
|
+
end
|
251
|
+
|
252
|
+
begin; STDERR.reopen(STDOUT); rescue Exception; end
|
253
|
+
STDERR.sync = true
|
254
|
+
end
|
255
|
+
|
256
|
+
def rescue_exception
|
257
|
+
begin
|
258
|
+
yield
|
259
|
+
rescue Exception
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
def write_pid_file
|
264
|
+
return unless @pid_file
|
265
|
+
FileUtils.mkdir_p(File.dirname(@pid_file))
|
266
|
+
File.open(@pid_file, "w") { |f| f.write(Process.pid) }
|
267
|
+
File.chmod(0644, @pid_file)
|
268
|
+
end
|
269
|
+
|
270
|
+
def remove_pid_file
|
271
|
+
return unless @pid_file
|
272
|
+
File.unlink(@pid_file) if File.exists?(@pid_file)
|
273
|
+
end
|
274
|
+
|
275
|
+
def running?
|
276
|
+
return false unless @pid_file
|
277
|
+
|
278
|
+
pid = File.read(@pid_file).chomp.to_i rescue nil
|
279
|
+
pid = nil if pid == 0
|
280
|
+
return false unless pid
|
281
|
+
|
282
|
+
begin
|
283
|
+
Process.kill(0, pid)
|
284
|
+
return pid
|
285
|
+
rescue Errno::ESRCH
|
286
|
+
return nil
|
287
|
+
rescue Errno::EPERM
|
288
|
+
return pid
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
@@ -0,0 +1,205 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'fileutils'
|
5
|
+
require 'memcache'
|
6
|
+
require 'digest/md5'
|
7
|
+
|
8
|
+
require 'starling/server'
|
9
|
+
|
10
|
+
class StarlingServer::PersistentQueue
|
11
|
+
remove_const :SOFT_LOG_MAX_SIZE
|
12
|
+
SOFT_LOG_MAX_SIZE = 16 * 1024 # 16 KB
|
13
|
+
end
|
14
|
+
|
15
|
+
def safely_fork(&block)
|
16
|
+
# anti-race juice:
|
17
|
+
blocking = true
|
18
|
+
Signal.trap("USR1") { blocking = false }
|
19
|
+
|
20
|
+
pid = Process.fork(&block)
|
21
|
+
|
22
|
+
while blocking
|
23
|
+
sleep 0.1
|
24
|
+
end
|
25
|
+
|
26
|
+
pid
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "StarlingServer" do
|
30
|
+
before do
|
31
|
+
@tmp_path = File.join(File.dirname(__FILE__), "tmp")
|
32
|
+
|
33
|
+
begin
|
34
|
+
Dir::mkdir(@tmp_path)
|
35
|
+
rescue Errno::EEXIST
|
36
|
+
end
|
37
|
+
|
38
|
+
@server_pid = safely_fork do
|
39
|
+
server = StarlingServer::Base.new(:host => '127.0.0.1',
|
40
|
+
:port => 22133,
|
41
|
+
:path => @tmp_path,
|
42
|
+
:logger => Logger.new(STDERR),
|
43
|
+
:log_level => Logger::FATAL)
|
44
|
+
Signal.trap("INT") { server.stop }
|
45
|
+
Process.kill("USR1", Process.ppid)
|
46
|
+
server.run
|
47
|
+
end
|
48
|
+
|
49
|
+
@client = MemCache.new('127.0.0.1:22133')
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should test if temp_path exists and is writeable" do
|
53
|
+
File.exist?(@tmp_path).should be_true
|
54
|
+
File.directory?(@tmp_path).should be_true
|
55
|
+
File.writable?(@tmp_path).should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should set and get" do
|
59
|
+
v = rand((2**32)-1)
|
60
|
+
@client.get('test_set_and_get_one_entry').should be_nil
|
61
|
+
@client.set('test_set_and_get_one_entry', v)
|
62
|
+
@client.get('test_set_and_get_one_entry').should eql(v)
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
it "should expire entries" do
|
67
|
+
v = rand((2**32)-1)
|
68
|
+
@client.get('test_set_with_expiry').should be_nil
|
69
|
+
now = Time.now.to_i
|
70
|
+
@client.set('test_set_with_expiry', v + 2, now)
|
71
|
+
@client.set('test_set_with_expiry', v)
|
72
|
+
sleep(1.0)
|
73
|
+
@client.get('test_set_with_expiry').should eql(v)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should have age stat" do
|
77
|
+
now = Time.now.to_i
|
78
|
+
@client.set('test_age', 'nibbler')
|
79
|
+
sleep(1.0)
|
80
|
+
@client.get('test_age').should eql('nibbler')
|
81
|
+
|
82
|
+
stats = @client.stats['127.0.0.1:22133']
|
83
|
+
stats.has_key?('queue_test_age_age').should be_true
|
84
|
+
(stats['queue_test_age_age'] >= 1000).should be_true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should rotate log" do
|
88
|
+
log_rotation_path = File.join(@tmp_path, 'test_log_rotation')
|
89
|
+
|
90
|
+
Dir.glob("#{log_rotation_path}*").each do |file|
|
91
|
+
File.unlink(file) rescue nil
|
92
|
+
end
|
93
|
+
@client.get('test_log_rotation').should be_nil
|
94
|
+
|
95
|
+
v = 'x' * 8192
|
96
|
+
|
97
|
+
@client.set('test_log_rotation', v)
|
98
|
+
File.size(log_rotation_path).should eql(8207)
|
99
|
+
@client.get('test_log_rotation')
|
100
|
+
|
101
|
+
@client.get('test_log_rotation').should be_nil
|
102
|
+
|
103
|
+
@client.set('test_log_rotation', v)
|
104
|
+
@client.get('test_log_rotation').should eql(v)
|
105
|
+
|
106
|
+
File.size(log_rotation_path).should eql(1)
|
107
|
+
# rotated log should be erased after a successful roll.
|
108
|
+
Dir.glob("#{log_rotation_path}*").size.should eql(1)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should output statistics per server" do
|
112
|
+
stats = @client.stats
|
113
|
+
assert_kind_of Hash, stats
|
114
|
+
assert stats.has_key?('127.0.0.1:22133')
|
115
|
+
|
116
|
+
server_stats = stats['127.0.0.1:22133']
|
117
|
+
|
118
|
+
basic_stats = %w( bytes pid time limit_maxbytes cmd_get version
|
119
|
+
bytes_written cmd_set get_misses total_connections
|
120
|
+
curr_connections curr_items uptime get_hits total_items
|
121
|
+
rusage_system rusage_user bytes_read )
|
122
|
+
|
123
|
+
basic_stats.each do |stat|
|
124
|
+
server_stats.has_key?(stat).should be_true
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should return valid response with unkown command" do
|
129
|
+
response = @client.add('blah', 1)
|
130
|
+
response.should eql("CLIENT_ERROR bad command line format\r\n")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should disconnect and reconnect again" do
|
134
|
+
v = rand(2**32-1)
|
135
|
+
@client.set('test_that_disconnecting_and_reconnecting_works', v)
|
136
|
+
@client.reset
|
137
|
+
@client.get('test_that_disconnecting_and_reconnecting_works').should eql(v)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should use epoll on linux" do
|
141
|
+
# this may take a few seconds.
|
142
|
+
# the point is to make sure that we're using epoll on Linux, so we can
|
143
|
+
# handle more than 1024 connections.
|
144
|
+
|
145
|
+
unless IO::popen("uname").read.chomp == "Linux"
|
146
|
+
raise "(Skipping epoll test: not on Linux)"
|
147
|
+
skip = true
|
148
|
+
end
|
149
|
+
fd_limit = IO::popen("bash -c 'ulimit -n'").read.chomp.to_i
|
150
|
+
unless fd_limit > 1024
|
151
|
+
raise "(Skipping epoll test: 'ulimit -n' = #{fd_limit}, need > 1024)"
|
152
|
+
skip = true
|
153
|
+
end
|
154
|
+
|
155
|
+
unless skip
|
156
|
+
v = rand(2**32 - 1)
|
157
|
+
@client.set('test_epoll', v)
|
158
|
+
|
159
|
+
# we can't open 1024 connections to memcache from within this process,
|
160
|
+
# because we will hit ruby's 1024 fd limit ourselves!
|
161
|
+
pid1 = safely_fork do
|
162
|
+
unused_sockets = []
|
163
|
+
600.times do
|
164
|
+
unused_sockets << TCPSocket.new("127.0.0.1", 22133)
|
165
|
+
end
|
166
|
+
Process.kill("USR1", Process.ppid)
|
167
|
+
sleep 90
|
168
|
+
end
|
169
|
+
pid2 = safely_fork do
|
170
|
+
unused_sockets = []
|
171
|
+
600.times do
|
172
|
+
unused_sockets << TCPSocket.new("127.0.0.1", 22133)
|
173
|
+
end
|
174
|
+
Process.kill("USR1", Process.ppid)
|
175
|
+
sleep 90
|
176
|
+
end
|
177
|
+
|
178
|
+
begin
|
179
|
+
client = MemCache.new('127.0.0.1:22133')
|
180
|
+
client.get('test_epoll').should eql(v)
|
181
|
+
ensure
|
182
|
+
Process.kill("TERM", pid1)
|
183
|
+
Process.kill("TERM", pid2)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
it "should raise error if queue collection is an invalid path" do
|
189
|
+
invalid_path = nil
|
190
|
+
while invalid_path.nil? || File.exist?(invalid_path)
|
191
|
+
invalid_path = File.join('/', Digest::MD5.hexdigest(rand(2**32-1).to_s)[0,8])
|
192
|
+
end
|
193
|
+
|
194
|
+
lambda {
|
195
|
+
StarlingServer::QueueCollection.new(invalid_path)
|
196
|
+
}.should raise_error(StarlingServer::InaccessibleQueuePath)
|
197
|
+
end
|
198
|
+
|
199
|
+
after do
|
200
|
+
Process.kill("INT", @server_pid)
|
201
|
+
Process.wait(@server_pid)
|
202
|
+
@client.reset
|
203
|
+
FileUtils.rm(Dir.glob(File.join(@tmp_path, '*')))
|
204
|
+
end
|
205
|
+
end
|