rescheduler 0.2.0 → 0.3.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.
- data/lib/rescheduler.rb +72 -26
- metadata +3 -6
data/lib/rescheduler.rb
CHANGED
@@ -7,6 +7,7 @@ module Rescheduler
|
|
7
7
|
# Setup configuration
|
8
8
|
attr_accessor :config
|
9
9
|
self.config = {}
|
10
|
+
|
10
11
|
#==========================
|
11
12
|
# Management routines
|
12
13
|
def prefix
|
@@ -14,8 +15,8 @@ module Rescheduler
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def reinitialize # Very slow reinitialize
|
17
|
-
keys = %w{TMCOUNTER TMMAINT TMDEFERRED}
|
18
|
-
%w{TMTUBE:*
|
18
|
+
keys = %w{TMCOUNTER TMMAINT TMDEFERRED TMARGS TMRUNNING}.map {|p| prefix + p }
|
19
|
+
%w{TMTUBE:*}.each do |p|
|
19
20
|
keys += redis.keys(prefix + p)
|
20
21
|
end
|
21
22
|
redis.del(keys)
|
@@ -23,15 +24,56 @@ module Rescheduler
|
|
23
24
|
|
24
25
|
# Return a hash of statistics
|
25
26
|
def stats
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
loop do
|
28
|
+
redis.watch(rk_args) do
|
29
|
+
stats = {}
|
30
|
+
qnids = redis.hkeys(rk_args)
|
31
|
+
# Get all the "pending jobs"
|
32
|
+
qnids.each do |qnid|
|
33
|
+
queue = qnid_to_queue(qnid)
|
34
|
+
stats[queue] ||= {}
|
35
|
+
stats[queue][:pending] ||= 0
|
36
|
+
stats[queue][:pending] += 1
|
37
|
+
end
|
38
|
+
|
39
|
+
# Get all running
|
40
|
+
qnids = redis.hkeys(rk_running)
|
41
|
+
# Get all the "pending jobs"
|
42
|
+
qnids.each do |qnid|
|
43
|
+
queue = qnid_to_queue(qnid)
|
44
|
+
stats[queue] ||= {}
|
45
|
+
stats[queue][:running] ||= 0
|
46
|
+
stats[queue][:running] += 1
|
47
|
+
end
|
48
|
+
|
49
|
+
# Get all the deferred
|
50
|
+
deferred = redis.zrange(rk_deferred, 0, -1, :with_scores=>true)
|
51
|
+
deferred.each do |qnid, ts|
|
52
|
+
queue = qnid_to_queue(qnid)
|
53
|
+
stats[queue] ||= {}
|
54
|
+
stats[queue][:deferred] ||= 0
|
55
|
+
stats[queue][:deferred] += 1
|
56
|
+
stats[queue][:first] ||= ts # First is first
|
57
|
+
end
|
58
|
+
|
59
|
+
# Get all the immediate
|
60
|
+
qus = stats.keys
|
61
|
+
quls = redis.multi do
|
62
|
+
qus.each { |queue| redis.llen(rk_queue(queue)) }
|
63
|
+
end
|
64
|
+
|
65
|
+
unless quls # Retry
|
66
|
+
log_debug('Contention during stats')
|
67
|
+
next
|
68
|
+
end
|
69
|
+
|
70
|
+
qus.each_with_index do |k, idx|
|
71
|
+
stats[k][:immediate] = quls[idx]
|
72
|
+
end
|
73
|
+
|
74
|
+
return {:jobs=>stats}
|
75
|
+
end
|
33
76
|
end
|
34
|
-
return {:jobs=>stats}
|
35
77
|
end
|
36
78
|
|
37
79
|
#==========================
|
@@ -67,7 +109,7 @@ module Rescheduler
|
|
67
109
|
end
|
68
110
|
|
69
111
|
# Save options
|
70
|
-
redis.
|
112
|
+
redis.hset(rk_args, qnid, options.to_json)
|
71
113
|
|
72
114
|
# Determine the due time
|
73
115
|
if ts > now.to_i # Future job
|
@@ -91,7 +133,7 @@ module Rescheduler
|
|
91
133
|
def exists?(options)
|
92
134
|
raise ArgumentError, 'Can not test existence without :id' unless options.include?(:id)
|
93
135
|
qnid = get_qnid(options)
|
94
|
-
return redis.
|
136
|
+
return redis.hexists(rk_args, qnid)
|
95
137
|
end
|
96
138
|
|
97
139
|
def enqueue_unless_exists(options)
|
@@ -102,7 +144,7 @@ module Rescheduler
|
|
102
144
|
qnid = get_qnid(options)
|
103
145
|
|
104
146
|
redis.multi do
|
105
|
-
redis.
|
147
|
+
redis.hdel(rk_args, qnid)
|
106
148
|
redis.zrem(rk_deferred, qnid)
|
107
149
|
redis.lrem(rk_queue(options[:queue]), 0, qnid)
|
108
150
|
end
|
@@ -178,21 +220,20 @@ module Rescheduler
|
|
178
220
|
# Runner routines
|
179
221
|
def run_job(qnid)
|
180
222
|
# First load job parameters for running
|
181
|
-
rk = rk_args(qnid)
|
182
|
-
rkr = rk_running(qnid)
|
183
223
|
optstr = nil
|
184
224
|
begin
|
185
225
|
res = nil
|
186
|
-
|
187
|
-
|
226
|
+
# Note: We use a single key to watch, can be improved by having a per-job key,
|
227
|
+
redis.watch(rk_args) do # Transaction to ensure read/delete is atomic
|
228
|
+
optstr = redis.hget(rk_args, qnid)
|
188
229
|
if optstr.nil?
|
189
230
|
redis.unwatch
|
190
231
|
log_debug("Job is deleted mysteriously")
|
191
232
|
return # Job is deleted somewhere
|
192
233
|
end
|
193
234
|
res = redis.multi do
|
194
|
-
redis.
|
195
|
-
redis.
|
235
|
+
redis.hdel(rk_args, qnid)
|
236
|
+
redis.hset(rk_running, qnid, optstr)
|
196
237
|
end
|
197
238
|
if !res
|
198
239
|
# Contention, try read again
|
@@ -218,7 +259,7 @@ module Rescheduler
|
|
218
259
|
end
|
219
260
|
|
220
261
|
# 3. Remove job from running list (Done)
|
221
|
-
redis.
|
262
|
+
redis.hdel(rk_running, qnid)
|
222
263
|
end
|
223
264
|
|
224
265
|
# Helper routines
|
@@ -270,21 +311,26 @@ module Rescheduler
|
|
270
311
|
return nt
|
271
312
|
end
|
272
313
|
|
273
|
-
def rk_queue(
|
274
|
-
return "#{prefix}TMTUBE:#{tube}"
|
275
|
-
end
|
314
|
+
def rk_queue(queue); "#{prefix}TMTUBE:#{queue}"; end
|
276
315
|
|
277
316
|
def rk_deferred; prefix + 'TMDEFERRED'; end
|
278
317
|
def rk_maintenace; prefix + 'TMMAINT'; end
|
279
|
-
def rk_args
|
280
|
-
def rk_running
|
318
|
+
def rk_args; prefix + "TMARGS"; end
|
319
|
+
def rk_running; prefix + "TMRUNNING"; end
|
281
320
|
def rk_counter; prefix + 'TMCOUNTER'; end
|
282
321
|
|
283
322
|
def get_qnid(options)
|
284
323
|
return "#{options[:queue]}:#{options[:id]}"
|
285
324
|
end
|
286
325
|
|
287
|
-
def qnid_to_queue(qnid)
|
326
|
+
def qnid_to_queue(qnid)
|
327
|
+
idx = qnid.index(':')
|
328
|
+
unless idx
|
329
|
+
log_debug("Invalid qnid: #{qnid}")
|
330
|
+
return nil
|
331
|
+
end
|
332
|
+
qnid[0...idx]
|
333
|
+
end
|
288
334
|
|
289
335
|
def redis
|
290
336
|
@redis ||= Redis.new(@config[:redis_connection] || {})
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rescheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -43,11 +43,8 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
-
description:
|
47
|
-
|
48
|
-
Leave as much control to user as possible.\n 2. Fast response: no polling, pipe
|
49
|
-
architecture for immediate job response time.\n 3. No Setup: Can not require extra
|
50
|
-
\"maintenance thread\" in background. Auto maintained by each and every worker thread.\n\n"
|
46
|
+
description: Rescheduler is a library that uses Redis to maintain a task queue of
|
47
|
+
immediate and delayed jobs without polling.
|
51
48
|
email: liaody@gmail.com
|
52
49
|
executables: []
|
53
50
|
extensions: []
|