fiveruns-starling 0.9.7.5
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 +27 -0
- data/LICENSE +20 -0
- data/README.rdoc +93 -0
- data/Rakefile +21 -0
- data/bin/starling +5 -0
- data/bin/starling_client +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/client.rb +151 -0
- data/lib/starling/client_runner.rb +272 -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/lib/starling/thread_pool.rb +62 -0
- data/lib/starling/worker.rb +239 -0
- data/test/test_starling_client.rb +100 -0
- data/test/test_starling_server.rb +205 -0
- data/test/test_starling_worker.rb +237 -0
- metadata +115 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# http://en.wikipedia.org/wiki/Thread_pool_pattern
|
|
2
|
+
class ThreadPool
|
|
3
|
+
|
|
4
|
+
# 1. Initialize with arbitrary number of threads
|
|
5
|
+
def initialize( threads_number = 1, &exception_handler )
|
|
6
|
+
@work_queue = SizedQueue.new(threads_number)
|
|
7
|
+
@worker_threads = ThreadGroup.new
|
|
8
|
+
|
|
9
|
+
# Create all threads in advance: that's the thread pool after all
|
|
10
|
+
threads_number.times do
|
|
11
|
+
|
|
12
|
+
worker_thread = Thread.new do
|
|
13
|
+
# Body of the Worker Thread
|
|
14
|
+
begin
|
|
15
|
+
work_to_do = @work_queue.pop() # Blocking
|
|
16
|
+
break if work_to_do == :END_OF_WORK
|
|
17
|
+
begin
|
|
18
|
+
work_to_do.block.call( *work_to_do.args )
|
|
19
|
+
rescue Exception => e # Since they are swallowed by default
|
|
20
|
+
if !exception_handler.nil?
|
|
21
|
+
yield e
|
|
22
|
+
else
|
|
23
|
+
# Standard exception handling
|
|
24
|
+
dump = "Worker Thread thrown an exception:\n"
|
|
25
|
+
dump += "#{e.message}\n(#{e.class.name})\n"
|
|
26
|
+
dump += e.backtrace().join( "\n" ) + "\n"
|
|
27
|
+
print dump
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end until false
|
|
31
|
+
# Worker thread ends up gracefully <img src='http://ruby-rails.pl/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
@worker_threads.add worker_thread
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# 2. Add job to the queue, example:
|
|
39
|
+
def add_work( *args, &block )
|
|
40
|
+
@work_queue.push( Runnable.new( args, &block ) )
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# 3. Allow working threads to end up after finishing all queued work
|
|
44
|
+
def no_more_work
|
|
45
|
+
threads_n = @worker_threads.list.length
|
|
46
|
+
threads_n.times { @work_queue.push :END_OF_WORK }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# 4. Wait for all work to finish
|
|
50
|
+
def join
|
|
51
|
+
@worker_threads.list.each { |t| t.join }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class Runnable
|
|
57
|
+
attr_accessor :args, :block
|
|
58
|
+
def initialize( args, &block )
|
|
59
|
+
@args = args
|
|
60
|
+
@block = block
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'logger'
|
|
3
|
+
require 'thread'
|
|
4
|
+
require 'timeout'
|
|
5
|
+
require 'starling/thread_pool'
|
|
6
|
+
|
|
7
|
+
module StarlingWorker
|
|
8
|
+
attr_reader :logger
|
|
9
|
+
|
|
10
|
+
VERSION = "0.9.7.5"
|
|
11
|
+
|
|
12
|
+
class Base
|
|
13
|
+
DEFAULT_TIMEOUT = 10
|
|
14
|
+
DEFAULT_CONTINUES_PROCESSING = true
|
|
15
|
+
|
|
16
|
+
# you just need to reimplement this
|
|
17
|
+
def process(message=nil, &block)
|
|
18
|
+
if block
|
|
19
|
+
@block = block
|
|
20
|
+
else
|
|
21
|
+
raise "you need to implement process"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def run
|
|
26
|
+
process_worker(&@block)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def initialize(opts = {})
|
|
30
|
+
@opts = {
|
|
31
|
+
:timeout => DEFAULT_TIMEOUT,
|
|
32
|
+
:incoming_remote_queue_name => nil,
|
|
33
|
+
:outgoing_remote_queue_name => nil,
|
|
34
|
+
:continues_processing => DEFAULT_CONTINUES_PROCESSING,
|
|
35
|
+
:threads => 10,
|
|
36
|
+
:log_level => Logger::INFO
|
|
37
|
+
}.merge(opts)
|
|
38
|
+
|
|
39
|
+
@@logger = case @opts[:logger]
|
|
40
|
+
when IO, String; Logger.new(@opts[:logger])
|
|
41
|
+
when Logger; @opts[:logger]
|
|
42
|
+
else; Logger.new(STDERR)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
@@logger = SyslogLogger.new(@opts[:syslog_channel]) if @opts[:syslog_channel]
|
|
46
|
+
|
|
47
|
+
@@logger.level = @opts[:log_level] || Logger::ERROR
|
|
48
|
+
|
|
49
|
+
@@logger.info "StarlingWorker#{self.class.to_s} STARTUP"
|
|
50
|
+
|
|
51
|
+
unless @opts[:host] && @opts[:port]
|
|
52
|
+
raise "you need to pass starling host en port"
|
|
53
|
+
else
|
|
54
|
+
@@logger.info "StarlingWorker#{self.class.to_s} connecting to starling"
|
|
55
|
+
@starling = Starling.new("#{@opts[:host]}:#{@opts[:port]}", :multithread => true)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
@@logger.info "StarlingWorker#{self.class.to_s} ThreadPool #{@opts[:threads]}"
|
|
59
|
+
@threadpool = ThreadPool.new(@opts[:threads])
|
|
60
|
+
|
|
61
|
+
@@logger.info "StarlingWorker#{self.class.to_s} Created local queue"
|
|
62
|
+
@messages = Queue.new #local queue
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def process_worker(&block)
|
|
66
|
+
enq_thread = Thread.new do
|
|
67
|
+
@@logger.info "StarlingWorker#{self.class.to_s} starting process_message_from_remote_queue"
|
|
68
|
+
if @opts[:continues_processing]
|
|
69
|
+
while @opts[:continues_processing]
|
|
70
|
+
process_message_from_incoming_remote_queue(&block)
|
|
71
|
+
end
|
|
72
|
+
else
|
|
73
|
+
process_message_from_incoming_remote_queue(&block)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
enq_thread.join unless @opts[:continues_processing]
|
|
78
|
+
|
|
79
|
+
deq_thread = Thread.new do
|
|
80
|
+
@@logger.info "StarlingWorker#{self.class.to_s} starting process_message_from_local_queue_to_outgoing_remote_queue"
|
|
81
|
+
if @opts[:continues_processing]
|
|
82
|
+
while @opts[:continues_processing]
|
|
83
|
+
process_message_from_local_queue_to_outgoing_remote_queue
|
|
84
|
+
end
|
|
85
|
+
else
|
|
86
|
+
process_message_from_local_queue_to_outgoing_remote_queue
|
|
87
|
+
end
|
|
88
|
+
end if @opts[:outgoing_remote_queue_name]
|
|
89
|
+
|
|
90
|
+
deq_thread.join unless @opts[:continues_processing] || @opts[:outgoing_remote_queue_name] == nil
|
|
91
|
+
|
|
92
|
+
if @opts[:continues_processing]
|
|
93
|
+
enq_thread.join
|
|
94
|
+
deq_thread.join unless @opts[:outgoing_remote_queue_name] == nil
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def process_message_from_incoming_remote_queue(&block)
|
|
99
|
+
begin
|
|
100
|
+
if @opts[:incoming_remote_queue_name]
|
|
101
|
+
starling = Starling.new("#{@opts[:host]}:#{@opts[:port]}", :multithread => true) unless starling
|
|
102
|
+
message = from_remote_queue_to_process(starling)
|
|
103
|
+
while message == nil
|
|
104
|
+
sleep 0.25
|
|
105
|
+
message = from_remote_queue_to_process(starling)
|
|
106
|
+
end
|
|
107
|
+
@@logger.info "StarlingWorker#{self.class.to_s} processing message (#{message})"
|
|
108
|
+
process_as_thread(message, &block)
|
|
109
|
+
else
|
|
110
|
+
@@logger.info "StarlingWorker#{self.class.to_s} processing without message (#{message})"
|
|
111
|
+
process_as_thread(&block) # just process without message
|
|
112
|
+
end
|
|
113
|
+
rescue Exception => e
|
|
114
|
+
puts "--- process_worker ---"
|
|
115
|
+
puts e
|
|
116
|
+
puts e.backtrace.join("\n")
|
|
117
|
+
sleep 1
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def process_message_from_local_queue_to_outgoing_remote_queue
|
|
122
|
+
begin
|
|
123
|
+
starling = Starling.new("#{@opts[:host]}:#{@opts[:port]}", :multithread => true) unless starling
|
|
124
|
+
|
|
125
|
+
from_local_queue_to_remote_queue(starling)
|
|
126
|
+
rescue Exception => e
|
|
127
|
+
puts "--- process_worker ---"
|
|
128
|
+
puts e
|
|
129
|
+
puts e.backtrace.join("\n")
|
|
130
|
+
sleep 1
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def process_as_thread(message=nil, &block)
|
|
135
|
+
@threadpool.add_work do
|
|
136
|
+
timeout(@opts[:timeout]) do
|
|
137
|
+
if block
|
|
138
|
+
if message
|
|
139
|
+
@@logger.info "StarlingWorker#{self.class.to_s} processing block message (#{message})"
|
|
140
|
+
from_process_to_local_queue block.call(message)
|
|
141
|
+
else
|
|
142
|
+
@@logger.info "StarlingWorker#{self.class.to_s} processing block without message (#{message})"
|
|
143
|
+
from_process_to_local_queue block.call
|
|
144
|
+
end
|
|
145
|
+
else
|
|
146
|
+
if message
|
|
147
|
+
@@logger.info "StarlingWorker#{self.class.to_s} processing method message (#{message})"
|
|
148
|
+
from_process_to_local_queue process(message)
|
|
149
|
+
else
|
|
150
|
+
@@logger.info "StarlingWorker#{self.class.to_s} processing method without message (#{message})"
|
|
151
|
+
from_process_to_local_queue process
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def from_remote_queue_to_process(starling=@starling)
|
|
159
|
+
if @opts[:incoming_remote_queue_name]
|
|
160
|
+
message = get_message_from_incoming_remote_queue(starling)
|
|
161
|
+
|
|
162
|
+
while message == nil
|
|
163
|
+
sleep 0.25
|
|
164
|
+
message = get_message_from_incoming_remote_queue(starling)
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
@@logger.info "StarlingWorker#{self.class.to_s} return remote message to process (#{message})"
|
|
168
|
+
|
|
169
|
+
message
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def from_process_to_local_queue(message)
|
|
174
|
+
@@logger.info "StarlingWorker#{self.class.to_s} add message to local queue (#{message})" if @opts[:outgoing_remote_queue_name]
|
|
175
|
+
add_message_to_local_queue(message) if @opts[:outgoing_remote_queue_name]
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def from_local_queue_to_remote_queue(starling=@starling)
|
|
179
|
+
if @opts[:outgoing_remote_queue_name]
|
|
180
|
+
message = get_message_from_local_queue
|
|
181
|
+
|
|
182
|
+
@@logger.info "StarlingWorker::#{self.class.to_s} set message on remote queue (#{message})"
|
|
183
|
+
|
|
184
|
+
starling.set(@opts[:outgoing_remote_queue_name], message)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def threadpool
|
|
189
|
+
@threadpool
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def incoming_remote_queue_name
|
|
193
|
+
@opts[:incoming_remote_queue_name]
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def outgoing_remote_queue_name
|
|
197
|
+
@opts[:outgoing_remote_queue_name]
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def add_message_to_local_queue(message)
|
|
201
|
+
@messages.enq message
|
|
202
|
+
true
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def get_message_from_local_queue
|
|
206
|
+
@messages.deq
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def local_queue
|
|
210
|
+
@messages
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def add_message_to_incoming_remote_queue(message, starling=@starling)
|
|
214
|
+
starling.set(incoming_remote_queue_name, message)
|
|
215
|
+
true
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def get_message_from_incoming_remote_queue(starling=@starling)
|
|
219
|
+
starling.get(incoming_remote_queue_name)
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def add_message_to_outgoing_remote_queue(message, starling=@starling)
|
|
223
|
+
starling.set(outgoing_remote_queue_name, message)
|
|
224
|
+
true
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def get_message_from_outgoing_remote_queue(starling=@starling)
|
|
228
|
+
starling.get(outgoing_remote_queue_name)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def flush_remote_queue
|
|
232
|
+
@starling.flush
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
def self.logger
|
|
236
|
+
@@logger
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'spec'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'memcache'
|
|
7
|
+
require 'digest/md5'
|
|
8
|
+
|
|
9
|
+
require 'starling/server'
|
|
10
|
+
require 'starling/client'
|
|
11
|
+
|
|
12
|
+
class StarlingServer::PersistentQueue
|
|
13
|
+
remove_const :SOFT_LOG_MAX_SIZE
|
|
14
|
+
SOFT_LOG_MAX_SIZE = 16 * 1024 # 16 KB
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def safely_fork(&block)
|
|
18
|
+
# anti-race juice:
|
|
19
|
+
blocking = true
|
|
20
|
+
Signal.trap("USR1") { blocking = false }
|
|
21
|
+
|
|
22
|
+
pid = Process.fork(&block)
|
|
23
|
+
|
|
24
|
+
while blocking
|
|
25
|
+
sleep 0.1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
pid
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "StarlingServer" do
|
|
32
|
+
before do
|
|
33
|
+
@tmp_path = File.join(File.dirname(__FILE__), "tmp")
|
|
34
|
+
@templates_path = File.join(File.dirname(__FILE__), "templates")
|
|
35
|
+
@workers_path = File.join(File.dirname(__FILE__), "workers")
|
|
36
|
+
|
|
37
|
+
begin
|
|
38
|
+
Dir::mkdir(@tmp_path)
|
|
39
|
+
rescue Errno::EEXIST
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
@server_pid = safely_fork do
|
|
43
|
+
server = StarlingServer::Base.new(:host => '127.0.0.1',
|
|
44
|
+
:port => 22133,
|
|
45
|
+
:path => @tmp_path,
|
|
46
|
+
:logger => Logger.new(STDERR),
|
|
47
|
+
:log_level => Logger::FATAL)
|
|
48
|
+
Signal.trap("INT") { server.stop }
|
|
49
|
+
Process.kill("USR1", Process.ppid)
|
|
50
|
+
server.run
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
@client = StarlingClient::Base.new(:host => '127.0.0.1',
|
|
54
|
+
:port => 22133,
|
|
55
|
+
:templates_path => @templates_path,
|
|
56
|
+
:workers_path => @workers_path)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
it "should test if tmp_path exists and is writeable" do
|
|
60
|
+
File.exist?(@tmp_path).should be_true
|
|
61
|
+
File.directory?(@tmp_path).should be_true
|
|
62
|
+
File.writable?(@tmp_path).should be_true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "should test if templates_path exists and is writeable" do
|
|
66
|
+
File.exist?(@templates_path).should be_true
|
|
67
|
+
File.directory?(@templates_path).should be_true
|
|
68
|
+
File.writable?(@templates_path).should be_true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "should test if workers_path exists and is writeable" do
|
|
72
|
+
File.exist?(@workers_path).should be_true
|
|
73
|
+
File.directory?(@workers_path).should be_true
|
|
74
|
+
File.writable?(@workers_path).should be_true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it "should load templates" do
|
|
78
|
+
@client.load_templates.should eql(["ActiveRecord", "Basic"])
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
it "should load workers" do
|
|
82
|
+
@client.load_workers.should eql(["GetDataFromSomeApi", "PushDataToSomeApi"])
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it "should return Starling" do
|
|
86
|
+
@client.starling.should_not == nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "should run" do
|
|
90
|
+
@client.starling.set("get_data_from_some_api_out", "test")
|
|
91
|
+
@client.starling.set("gettingdata_out", "test")
|
|
92
|
+
@client.run
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
after do
|
|
96
|
+
Process.kill("INT", @server_pid)
|
|
97
|
+
Process.wait(@server_pid)
|
|
98
|
+
FileUtils.rm(Dir.glob(File.join(@tmp_path, '*')))
|
|
99
|
+
end
|
|
100
|
+
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
|