qs 0.6.1 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bench/config.qs +1 -0
- data/bench/dispatcher.qs +1 -0
- data/bench/report.rb +2 -0
- data/bench/setup.rb +1 -1
- data/lib/qs.rb +95 -41
- data/lib/qs/client.rb +5 -5
- data/lib/qs/config_file.rb +1 -1
- data/lib/qs/daemon.rb +133 -99
- data/lib/qs/daemon_data.rb +22 -21
- data/lib/qs/message_handler.rb +1 -0
- data/lib/qs/process.rb +20 -10
- data/lib/qs/qs_runner.rb +13 -21
- data/lib/qs/runner.rb +4 -0
- data/lib/qs/test_runner.rb +12 -2
- data/lib/qs/version.rb +1 -1
- data/qs.gemspec +6 -7
- data/test/helper.rb +11 -0
- data/test/support/app_queue.rb +104 -0
- data/test/support/client_spy.rb +2 -2
- data/test/support/config.qs +9 -2
- data/test/support/factory.rb +1 -1
- data/test/system/daemon_tests.rb +117 -36
- data/test/system/queue_tests.rb +1 -1
- data/test/unit/cli_tests.rb +2 -2
- data/test/unit/client_tests.rb +11 -11
- data/test/unit/config_file_tests.rb +2 -2
- data/test/unit/daemon_data_tests.rb +53 -48
- data/test/unit/daemon_tests.rb +221 -214
- data/test/unit/message_handler_tests.rb +14 -1
- data/test/unit/process_tests.rb +50 -25
- data/test/unit/qs_runner_tests.rb +120 -34
- data/test/unit/qs_tests.rb +180 -75
- data/test/unit/queue_tests.rb +5 -5
- data/test/unit/runner_tests.rb +9 -1
- data/test/unit/test_runner_tests.rb +12 -1
- metadata +13 -23
- data/test/support/app_daemon.rb +0 -143
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
data.tar.gz:
|
4
|
-
metadata.gz:
|
3
|
+
data.tar.gz: 110c7cbf5c19120501d4dfae9f332e428363aff3
|
4
|
+
metadata.gz: 053fda8d651bb4509a99537ef8b8edb906a80acd
|
5
5
|
SHA512:
|
6
|
-
data.tar.gz:
|
7
|
-
metadata.gz:
|
6
|
+
data.tar.gz: 6c145c40e72aecad5c87b33709a3a3aa097a07a4bd08a17819d23965efd36fdbe46312a01fd638f346e4e34a2c7cc4c2ab85af7a9951083e97aae68f93527624
|
7
|
+
metadata.gz: 56bff595f01f50dba796e2b436b842376188d3c031ab6cb88395bb7cc41e04250217c04ef5eedc78ba221586795045ba8e6b739816cbe65d89cb01dacd9607ab
|
data/bench/config.qs
CHANGED
data/bench/dispatcher.qs
CHANGED
data/bench/report.rb
CHANGED
data/bench/setup.rb
CHANGED
data/lib/qs.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'ns-options'
|
2
1
|
require 'qs/version'
|
3
2
|
require 'qs/client'
|
4
3
|
require 'qs/daemon'
|
@@ -15,28 +14,24 @@ module Qs
|
|
15
14
|
end
|
16
15
|
|
17
16
|
def self.init
|
18
|
-
self.config.
|
19
|
-
self.config.redis.ip,
|
20
|
-
self.config.redis.port,
|
21
|
-
self.config.redis.db
|
22
|
-
)
|
17
|
+
self.config.validate!
|
23
18
|
|
24
19
|
@dispatcher_queue ||= DispatcherQueue.new({
|
25
20
|
:queue_class => self.config.dispatcher_queue_class,
|
26
|
-
:queue_name => self.config.
|
27
|
-
:job_name => self.config.
|
28
|
-
:job_handler_class_name => self.config.
|
21
|
+
:queue_name => self.config.dispatcher_queue_name,
|
22
|
+
:job_name => self.config.dispatcher_job_name,
|
23
|
+
:job_handler_class_name => self.config.dispatcher_job_handler_class_name
|
29
24
|
})
|
30
25
|
|
31
26
|
@encoder ||= self.config.encoder
|
32
27
|
@decoder ||= self.config.decoder
|
33
|
-
@client ||= Client.new(self.
|
28
|
+
@client ||= Client.new(self.redis_connect_hash)
|
34
29
|
@redis ||= @client.redis
|
35
30
|
true
|
36
31
|
end
|
37
32
|
|
38
33
|
def self.reset!
|
39
|
-
|
34
|
+
@config = nil
|
40
35
|
@dispatcher_queue = nil
|
41
36
|
@encoder = nil
|
42
37
|
@decoder = nil
|
@@ -46,19 +41,19 @@ module Qs
|
|
46
41
|
end
|
47
42
|
|
48
43
|
def self.enqueue(queue, job_name, params = nil)
|
49
|
-
|
44
|
+
self.client.enqueue(queue, job_name, params)
|
50
45
|
end
|
51
46
|
|
52
47
|
def self.publish(channel, name, params = nil)
|
53
|
-
|
48
|
+
self.client.publish(channel, name, params)
|
54
49
|
end
|
55
50
|
|
56
51
|
def self.publish_as(publisher, channel, name, params = nil)
|
57
|
-
|
52
|
+
self.client.publish_as(publisher, channel, name, params)
|
58
53
|
end
|
59
54
|
|
60
55
|
def self.push(queue_name, payload)
|
61
|
-
|
56
|
+
self.client.push(queue_name, payload)
|
62
57
|
end
|
63
58
|
|
64
59
|
def self.encode(payload)
|
@@ -82,15 +77,15 @@ module Qs
|
|
82
77
|
end
|
83
78
|
|
84
79
|
def self.client
|
85
|
-
@client
|
80
|
+
@client || NullClient.new
|
86
81
|
end
|
87
82
|
|
88
83
|
def self.redis
|
89
84
|
@redis
|
90
85
|
end
|
91
86
|
|
92
|
-
def self.
|
93
|
-
self.config.
|
87
|
+
def self.redis_connect_hash
|
88
|
+
self.config.redis_connect_hash
|
94
89
|
end
|
95
90
|
|
96
91
|
def self.dispatcher_queue
|
@@ -98,7 +93,7 @@ module Qs
|
|
98
93
|
end
|
99
94
|
|
100
95
|
def self.dispatcher_job_name
|
101
|
-
self.config.
|
96
|
+
self.config.dispatcher_job_name
|
102
97
|
end
|
103
98
|
|
104
99
|
def self.event_publisher
|
@@ -110,46 +105,105 @@ module Qs
|
|
110
105
|
end
|
111
106
|
|
112
107
|
class Config
|
113
|
-
include NsOptions::Proxy
|
114
108
|
|
115
|
-
|
116
|
-
|
109
|
+
DEFAULT_ENCODER = proc{ |payload| ::JSON.dump(payload) }
|
110
|
+
DEFAULT_DECODER = proc{ |encoded_payload| ::JSON.load(encoded_payload) }
|
117
111
|
|
118
|
-
|
112
|
+
DEFAULT_DISPATCHER_QUEUE_CLASS = Queue
|
113
|
+
DEFAULT_DISPATCHER_QUEUE_NAME = 'dispatcher'.freeze
|
114
|
+
DEFAULT_DISPATCHER_JOB_NAME = 'run_dispatch_job'.freeze
|
115
|
+
DEFAULT_DISPATCHER_JOB_HANDLER_CLASS_NAME = DispatcherQueue::RunDispatchJob.to_s.freeze
|
119
116
|
|
120
|
-
|
117
|
+
DEFAULT_REDIS_IP = '127.0.0.1'.freeze
|
118
|
+
DEFAULT_REDIS_PORT = 6379.freeze
|
119
|
+
DEFAULT_REDIS_DB = 0.freeze
|
120
|
+
DEFAULT_REDIS_NS = 'qs'.freeze
|
121
|
+
DEFAULT_REDIS_DRIVER = 'ruby'.freeze
|
122
|
+
DEFAULT_REDIS_TIMEOUT = 1.freeze
|
123
|
+
DEFAULT_REDIS_SIZE = 4.freeze
|
121
124
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
125
|
+
attr_accessor :encoder, :decoder, :timeout, :event_publisher
|
126
|
+
attr_accessor :dispatcher_queue_class, :dispatcher_queue_name
|
127
|
+
attr_accessor :dispatcher_job_name, :dispatcher_job_handler_class_name
|
128
|
+
attr_accessor :redis_ip, :redis_port, :redis_db, :redis_ns
|
129
|
+
attr_accessor :redis_driver, :redis_timeout, :redis_size, :redis_url
|
127
130
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
131
|
+
def initialize
|
132
|
+
@encoder = DEFAULT_ENCODER
|
133
|
+
@decoder = DEFAULT_DECODER
|
134
|
+
@timeout = nil
|
135
|
+
@event_publisher = nil
|
136
|
+
|
137
|
+
@dispatcher_queue_class = DEFAULT_DISPATCHER_QUEUE_CLASS
|
138
|
+
@dispatcher_queue_name = DEFAULT_DISPATCHER_QUEUE_NAME
|
139
|
+
@dispatcher_job_name = DEFAULT_DISPATCHER_JOB_NAME
|
140
|
+
@dispatcher_job_handler_class_name = DEFAULT_DISPATCHER_JOB_HANDLER_CLASS_NAME
|
141
|
+
|
142
|
+
@redis_ip = DEFAULT_REDIS_IP
|
143
|
+
@redis_port = DEFAULT_REDIS_PORT
|
144
|
+
@redis_db = DEFAULT_REDIS_DB
|
145
|
+
@redis_ns = DEFAULT_REDIS_NS
|
146
|
+
@redis_driver = DEFAULT_REDIS_DRIVER
|
147
|
+
@redis_timeout = DEFAULT_REDIS_TIMEOUT
|
148
|
+
@redis_size = DEFAULT_REDIS_SIZE
|
149
|
+
@redis_url = nil
|
150
|
+
|
151
|
+
@valid = nil
|
152
|
+
end
|
132
153
|
|
133
|
-
|
154
|
+
# the keys here should be compatible with HellaRedis connection configs
|
155
|
+
# https://github.com/redding/hella-redis#connection
|
156
|
+
def redis_connect_hash
|
157
|
+
{ :ip => self.redis_ip,
|
158
|
+
:port => self.redis_port,
|
159
|
+
:db => self.redis_db,
|
160
|
+
:redis_ns => self.redis_ns,
|
161
|
+
:driver => self.redis_driver,
|
162
|
+
:timeout => self.redis_timeout,
|
163
|
+
:size => self.redis_size,
|
164
|
+
:url => self.redis_url
|
165
|
+
}
|
166
|
+
end
|
134
167
|
|
135
|
-
|
136
|
-
|
137
|
-
option :timeout, Integer, :default => 1
|
138
|
-
option :size, Integer, :default => 4
|
168
|
+
def valid?
|
169
|
+
!!@valid
|
139
170
|
end
|
140
171
|
|
141
|
-
|
172
|
+
def validate!
|
173
|
+
return @valid if !@valid.nil? # only need to run this once per config
|
142
174
|
|
143
|
-
|
144
|
-
self.
|
175
|
+
# set the `redis_url`
|
176
|
+
self.redis_url ||= RedisUrl.new(self.redis_ip, self.redis_port, self.redis_db)
|
177
|
+
|
178
|
+
@valid = true
|
145
179
|
end
|
180
|
+
|
146
181
|
end
|
147
182
|
|
148
183
|
module RedisUrl
|
184
|
+
|
149
185
|
def self.new(ip, port, db)
|
150
186
|
return if ip.to_s.empty? || port.to_s.empty? || db.to_s.empty?
|
151
187
|
"redis://#{ip}:#{port}/#{db}"
|
152
188
|
end
|
189
|
+
|
153
190
|
end
|
154
191
|
|
192
|
+
class NullClient
|
193
|
+
def initialize
|
194
|
+
@error = UninitializedError.new("Qs hasn't been initialized")
|
195
|
+
end
|
196
|
+
|
197
|
+
def enqueue(*args); raise @error; end
|
198
|
+
def publish(*args); raise @error; end
|
199
|
+
def publish_as(*args); raise @error; end
|
200
|
+
def push(*args); raise @error; end
|
201
|
+
def sync_subscriptions(*args); raise @error; end
|
202
|
+
def clear_subscriptions(*args); raise @error; end
|
203
|
+
def event_subscribers(*args); raise @error; end
|
204
|
+
end
|
205
|
+
|
206
|
+
UninitializedError = Class.new(RuntimeError)
|
207
|
+
TimeoutError = Class.new(RuntimeError)
|
208
|
+
|
155
209
|
end
|
data/lib/qs/client.rb
CHANGED
@@ -25,10 +25,10 @@ module Qs
|
|
25
25
|
|
26
26
|
module InstanceMethods
|
27
27
|
|
28
|
-
attr_reader :
|
28
|
+
attr_reader :redis_connect_hash, :redis
|
29
29
|
|
30
|
-
def initialize(
|
31
|
-
@
|
30
|
+
def initialize(redis_connect_hash)
|
31
|
+
@redis_connect_hash = redis_connect_hash
|
32
32
|
end
|
33
33
|
|
34
34
|
def enqueue(queue, job_name, job_params = nil)
|
@@ -119,7 +119,7 @@ module Qs
|
|
119
119
|
|
120
120
|
def initialize(*args)
|
121
121
|
super
|
122
|
-
@redis = HellaRedis::Connection.new(self.
|
122
|
+
@redis = HellaRedis::Connection.new(self.redis_connect_hash)
|
123
123
|
end
|
124
124
|
|
125
125
|
def push(queue_name, payload_hash)
|
@@ -145,7 +145,7 @@ module Qs
|
|
145
145
|
def initialize(*args)
|
146
146
|
super
|
147
147
|
require 'hella-redis/connection_spy'
|
148
|
-
@redis = HellaRedis::ConnectionSpy.new(self.
|
148
|
+
@redis = HellaRedis::ConnectionSpy.new(self.redis_connect_hash)
|
149
149
|
@pushed_items = []
|
150
150
|
end
|
151
151
|
|
data/lib/qs/config_file.rb
CHANGED
@@ -35,7 +35,7 @@ module Qs
|
|
35
35
|
full_path_with_sanford
|
36
36
|
end
|
37
37
|
|
38
|
-
# This evaluates the file and creates a proc using
|
38
|
+
# This evaluates the file and creates a proc using its contents. This is
|
39
39
|
# a trick borrowed from Rack. It is essentially converting a file into a
|
40
40
|
# proc and then instance eval'ing it. This has a couple benefits:
|
41
41
|
# * The obvious benefit is the file is evaluated in the context of this
|
data/lib/qs/daemon.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
require 'dat-worker-pool'
|
2
2
|
require 'much-plugin'
|
3
|
-
require 'ns-options'
|
4
|
-
require 'pathname'
|
5
|
-
require 'system_timer'
|
6
3
|
require 'thread'
|
7
4
|
require 'qs'
|
8
5
|
require 'qs/client'
|
@@ -16,9 +13,8 @@ module Qs
|
|
16
13
|
module Daemon
|
17
14
|
include MuchPlugin
|
18
15
|
|
19
|
-
|
20
|
-
|
21
|
-
SIGNAL = '.'.freeze
|
16
|
+
SIGNAL = '.'.freeze
|
17
|
+
FETCH_ERR_SLEEP_TIME = 1.0.freeze
|
22
18
|
|
23
19
|
plugin_included do
|
24
20
|
extend ClassMethods
|
@@ -27,28 +23,46 @@ module Qs
|
|
27
23
|
|
28
24
|
module InstanceMethods
|
29
25
|
|
30
|
-
attr_reader :daemon_data, :
|
31
|
-
attr_reader :signals_redis_key, :queue_redis_keys
|
26
|
+
attr_reader :daemon_data, :signals_redis_key
|
32
27
|
|
33
|
-
# set the size of the client to the num workers + 1, this ensures we have
|
34
|
-
# 1 connection for fetching work from redis and at least 1 connection for
|
35
|
-
# each worker to requeue its message when hard-shutdown
|
36
28
|
def initialize
|
37
|
-
self.class.
|
29
|
+
config = self.class.config
|
30
|
+
begin
|
31
|
+
config.validate!
|
32
|
+
rescue InvalidError => exception
|
33
|
+
exception.set_backtrace(caller)
|
34
|
+
raise exception
|
35
|
+
end
|
38
36
|
Qs.init
|
39
|
-
@daemon_data = DaemonData.new(self.class.configuration.to_hash)
|
40
|
-
@logger = @daemon_data.logger
|
41
37
|
|
42
|
-
@
|
43
|
-
:
|
44
|
-
:
|
45
|
-
|
46
|
-
|
38
|
+
@daemon_data = DaemonData.new({
|
39
|
+
:name => config.name,
|
40
|
+
:pid_file => config.pid_file,
|
41
|
+
:shutdown_timeout => config.shutdown_timeout,
|
42
|
+
:worker_class => config.worker_class,
|
43
|
+
:worker_params => config.worker_params,
|
44
|
+
:num_workers => config.num_workers,
|
45
|
+
:error_procs => config.error_procs,
|
46
|
+
:logger => config.logger,
|
47
|
+
:queues => config.queues,
|
48
|
+
:verbose_logging => config.verbose_logging,
|
49
|
+
:routes => config.routes
|
50
|
+
})
|
47
51
|
|
48
|
-
@signals_redis_key = "signals:#{
|
52
|
+
@signals_redis_key = "signals:#{self.daemon_data.name}-" \
|
49
53
|
"#{Socket.gethostname}-#{::Process.pid}"
|
50
54
|
|
55
|
+
@thread = nil
|
51
56
|
@worker_available = WorkerAvailable.new
|
57
|
+
@state = State.new(:stop)
|
58
|
+
|
59
|
+
# set the size of the client to the num workers + 1, this ensures we
|
60
|
+
# have 1 connection for fetching work from redis and at least 1
|
61
|
+
# connection for each worker to requeue its message when hard-shutdown
|
62
|
+
@client = QsClient.new(Qs.redis_connect_hash.merge({
|
63
|
+
:timeout => 1,
|
64
|
+
:size => self.daemon_data.num_workers + 1
|
65
|
+
}))
|
52
66
|
|
53
67
|
@worker_pool = DatWorkerPool.new(self.daemon_data.worker_class, {
|
54
68
|
:num_workers => self.daemon_data.num_workers,
|
@@ -57,15 +71,9 @@ module Qs
|
|
57
71
|
:qs_daemon_data => self.daemon_data,
|
58
72
|
:qs_client => @client,
|
59
73
|
:qs_worker_available => @worker_available,
|
60
|
-
:qs_logger =>
|
74
|
+
:qs_logger => self.logger
|
61
75
|
})
|
62
76
|
})
|
63
|
-
|
64
|
-
@thread = nil
|
65
|
-
@state = State.new(:stop)
|
66
|
-
rescue InvalidError => exception
|
67
|
-
exception.set_backtrace(caller)
|
68
|
-
raise exception
|
69
77
|
end
|
70
78
|
|
71
79
|
def name
|
@@ -80,14 +88,22 @@ module Qs
|
|
80
88
|
@daemon_data.pid_file
|
81
89
|
end
|
82
90
|
|
91
|
+
def logger
|
92
|
+
@daemon_data.logger
|
93
|
+
end
|
94
|
+
|
95
|
+
def queue_redis_keys
|
96
|
+
@daemon_data.queue_redis_keys
|
97
|
+
end
|
98
|
+
|
83
99
|
def running?
|
84
100
|
!!(@thread && @thread.alive?)
|
85
101
|
end
|
86
102
|
|
87
|
-
# ping to check that it can communicate with redis before running, this is
|
88
|
-
# friendlier than starting and continously erroring because it can't
|
89
|
-
# dequeue
|
90
103
|
def start
|
104
|
+
# ping to check that it can communicate with redis before running,
|
105
|
+
# this is friendlier than starting and continously erroring because
|
106
|
+
# it can't dequeue
|
91
107
|
@client.ping
|
92
108
|
@state.set :run
|
93
109
|
@thread ||= Thread.new{ work_loop }
|
@@ -121,25 +137,25 @@ module Qs
|
|
121
137
|
teardown
|
122
138
|
end
|
123
139
|
|
124
|
-
# clear any signals that are already on the signals list in redis
|
125
140
|
def setup
|
126
|
-
|
141
|
+
# clear any signals that are already on the signals list in redis
|
142
|
+
@client.clear(self.signals_redis_key)
|
127
143
|
@worker_pool.start
|
128
144
|
end
|
129
145
|
|
130
|
-
# shuffle the queue redis keys to avoid queue starvation, redis will pull
|
131
|
-
# messages off queues in the order they are passed to the command, by
|
132
|
-
# shuffling we ensure they are randomly ordered so every queue should get
|
133
|
-
# a chance; use 0 for the brpop timeout which means block indefinitely;
|
134
|
-
# rescue runtime errors so the daemon thread doesn't fail if redis is
|
135
|
-
# temporarily down, sleep for a second to keep the thread from thrashing
|
136
|
-
# by repeatedly erroring if redis is down
|
137
146
|
def fetch_messages
|
138
147
|
if !@worker_pool.worker_available? && @state.run?
|
139
148
|
@worker_available.wait
|
140
149
|
end
|
141
150
|
return unless @worker_pool.worker_available? && @state.run?
|
142
151
|
|
152
|
+
# shuffle the queue redis keys to avoid queue starvation, redis will
|
153
|
+
# pull messages off queues in the order they are passed to the command,
|
154
|
+
# by shuffling we ensure they are randomly ordered so every queue
|
155
|
+
# should get a chance; use 0 for the brpop timeout which means block
|
156
|
+
# indefinitely; rescue runtime errors so the daemon thread doesn't fail
|
157
|
+
# if redis is temporarily down, sleep for a second to keep the thread
|
158
|
+
# from thrashing by repeatedly erroring if redis is down
|
143
159
|
begin
|
144
160
|
args = [self.signals_redis_key, self.queue_redis_keys.shuffle, 0].flatten
|
145
161
|
redis_key, encoded_payload = @client.block_dequeue(*args)
|
@@ -150,7 +166,7 @@ module Qs
|
|
150
166
|
log "Error occurred while dequeueing", :error
|
151
167
|
log "#{exception.class}: #{exception.message}", :error
|
152
168
|
(exception.backtrace || []).each{ |l| log(l, :error) }
|
153
|
-
sleep
|
169
|
+
sleep FETCH_ERR_SLEEP_TIME
|
154
170
|
end
|
155
171
|
end
|
156
172
|
|
@@ -183,86 +199,102 @@ module Qs
|
|
183
199
|
|
184
200
|
module ClassMethods
|
185
201
|
|
186
|
-
def
|
187
|
-
@
|
202
|
+
def config
|
203
|
+
@config ||= Config.new
|
204
|
+
end
|
205
|
+
|
206
|
+
def name(value = nil)
|
207
|
+
self.config.name = value if !value.nil?
|
208
|
+
self.config.name
|
188
209
|
end
|
189
210
|
|
190
|
-
def
|
191
|
-
self.
|
211
|
+
def pid_file(value = nil)
|
212
|
+
self.config.pid_file = value if !value.nil?
|
213
|
+
self.config.pid_file
|
192
214
|
end
|
193
215
|
|
194
|
-
def
|
195
|
-
self.
|
216
|
+
def shutdown_timeout(value = nil)
|
217
|
+
self.config.shutdown_timeout = value if !value.nil?
|
218
|
+
self.config.shutdown_timeout
|
196
219
|
end
|
197
220
|
|
198
|
-
def worker_class(
|
199
|
-
self.
|
200
|
-
self.
|
221
|
+
def worker_class(value = nil)
|
222
|
+
self.config.worker_class = value if !value.nil?
|
223
|
+
self.config.worker_class
|
201
224
|
end
|
202
225
|
|
203
|
-
def worker_params(
|
204
|
-
self.
|
205
|
-
self.
|
226
|
+
def worker_params(value = nil)
|
227
|
+
self.config.worker_params = value if !value.nil?
|
228
|
+
self.config.worker_params
|
206
229
|
end
|
207
230
|
|
208
|
-
def num_workers(
|
209
|
-
self.
|
231
|
+
def num_workers(new_num_workers = nil)
|
232
|
+
self.config.num_workers = new_num_workers if new_num_workers
|
233
|
+
self.config.num_workers
|
210
234
|
end
|
211
235
|
alias :workers :num_workers
|
212
236
|
|
213
|
-
def
|
214
|
-
self.
|
237
|
+
def init(&block)
|
238
|
+
self.config.init_procs << block
|
215
239
|
end
|
216
240
|
|
217
|
-
def
|
218
|
-
self.
|
241
|
+
def init_procs
|
242
|
+
self.config.init_procs
|
219
243
|
end
|
220
244
|
|
221
|
-
def
|
222
|
-
self.
|
245
|
+
def error(&block)
|
246
|
+
self.config.error_procs << block
|
223
247
|
end
|
224
248
|
|
225
|
-
def
|
226
|
-
self.
|
249
|
+
def error_procs
|
250
|
+
self.config.error_procs
|
227
251
|
end
|
228
252
|
|
229
|
-
def
|
230
|
-
self.
|
253
|
+
def logger(value = nil)
|
254
|
+
self.config.logger = value if !value.nil?
|
255
|
+
self.config.logger
|
231
256
|
end
|
232
257
|
|
233
|
-
def queue(
|
234
|
-
self.
|
258
|
+
def queue(value)
|
259
|
+
self.config.queues << value
|
235
260
|
end
|
236
261
|
|
237
262
|
def queues
|
238
|
-
self.
|
263
|
+
self.config.queues
|
239
264
|
end
|
240
265
|
|
241
|
-
|
266
|
+
# flags
|
242
267
|
|
243
|
-
|
244
|
-
|
268
|
+
def verbose_logging(value = nil)
|
269
|
+
self.config.verbose_logging = value if !value.nil?
|
270
|
+
self.config.verbose_logging
|
271
|
+
end
|
245
272
|
|
246
|
-
|
247
|
-
option :pid_file, Pathname
|
273
|
+
end
|
248
274
|
|
249
|
-
|
275
|
+
class Config
|
250
276
|
|
251
|
-
|
252
|
-
option :logger, :default => proc{ Qs::NullLogger.new }
|
277
|
+
DEFAULT_NUM_WORKERS = 4.freeze
|
253
278
|
|
254
|
-
|
279
|
+
attr_accessor :name, :pid_file, :shutdown_timeout
|
280
|
+
attr_accessor :worker_class, :worker_params, :num_workers
|
281
|
+
attr_accessor :init_procs, :error_procs, :logger, :queues
|
282
|
+
attr_accessor :verbose_logging
|
255
283
|
|
256
|
-
|
257
|
-
|
258
|
-
|
284
|
+
def initialize
|
285
|
+
@name = nil
|
286
|
+
@pid_file = nil
|
287
|
+
@shutdown_timeout = nil
|
288
|
+
@worker_class = DefaultWorker
|
289
|
+
@worker_params = nil
|
290
|
+
@num_workers = DEFAULT_NUM_WORKERS
|
291
|
+
@init_procs = []
|
292
|
+
@error_procs = []
|
293
|
+
@logger = Qs::NullLogger.new
|
294
|
+
@queues = []
|
295
|
+
|
296
|
+
@verbose_logging = true
|
259
297
|
|
260
|
-
def initialize(values = nil)
|
261
|
-
super(values)
|
262
|
-
@init_procs, @error_procs = [], []
|
263
|
-
@worker_class = DefaultWorker
|
264
|
-
@worker_params = nil
|
265
|
-
@queues = []
|
266
298
|
@valid = nil
|
267
299
|
end
|
268
300
|
|
@@ -270,35 +302,37 @@ module Qs
|
|
270
302
|
@queues.map(&:routes).flatten
|
271
303
|
end
|
272
304
|
|
273
|
-
def to_hash
|
274
|
-
super.merge({
|
275
|
-
:error_procs => self.error_procs,
|
276
|
-
:worker_class => self.worker_class,
|
277
|
-
:worker_params => self.worker_params,
|
278
|
-
:routes => self.routes,
|
279
|
-
:queue_redis_keys => self.queues.map(&:redis_key)
|
280
|
-
})
|
281
|
-
end
|
282
|
-
|
283
305
|
def valid?
|
284
306
|
!!@valid
|
285
307
|
end
|
286
308
|
|
309
|
+
# for the config to be considered "valid", a few things need to happen.
|
310
|
+
# The key here is that this only needs to be done _once_ for each config.
|
311
|
+
|
287
312
|
def validate!
|
288
|
-
return @valid if !@valid.nil?
|
313
|
+
return @valid if !@valid.nil? # only need to run this once per config
|
314
|
+
|
315
|
+
# ensure all user and plugin configs/settings are applied
|
289
316
|
self.init_procs.each(&:call)
|
290
|
-
if self.queues.empty? ||
|
291
|
-
raise InvalidError, "a name and queue must be configured"
|
317
|
+
if self.queues.empty? || self.name.nil?
|
318
|
+
raise InvalidError, "a name and at least 1 queue must be configured"
|
292
319
|
end
|
320
|
+
|
321
|
+
# validate the worker class
|
293
322
|
if !self.worker_class.kind_of?(Class) || !self.worker_class.include?(Qs::Worker)
|
294
323
|
raise InvalidError, "worker class must include `#{Qs::Worker}`"
|
295
324
|
end
|
325
|
+
|
326
|
+
# validate the routes
|
296
327
|
self.routes.each(&:validate!)
|
297
|
-
|
328
|
+
|
329
|
+
@valid = true # if it made it this far, it's valid!
|
298
330
|
end
|
331
|
+
|
299
332
|
end
|
300
333
|
|
301
334
|
DefaultWorker = Class.new{ include Qs::Worker }
|
335
|
+
InvalidError = Class.new(ArgumentError)
|
302
336
|
|
303
337
|
class WorkerAvailable
|
304
338
|
def initialize
|