qs 0.6.1 → 0.7.0
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.
- 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
|