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.
Files changed (2) hide show
  1. data/lib/rescheduler.rb +72 -26
  2. 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:* TMARGS:* TMRUNNING:*}.each do |p|
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
- qnids = redis.keys(rk_args('*'))
27
- stats = {}
28
- qnids.each do |k|
29
- qnid = k.split('TMARGS:')[1]
30
- queue = qnid_to_queue(qnid)
31
- stats[queue] ||= 0
32
- stats[queue] += 1
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.set(rk_args(qnid), options.to_json)
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.exists(rk_args(qnid))
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.del(rk_args(qnid))
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
- redis.watch(rk) do # Transaction to ensure read/delete is atomic
187
- optstr = redis.get(rk)
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.del(rk)
195
- redis.set(rkr, optstr)
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.del(rkr)
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(tube)
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(qnid); "#{prefix}TMARGS:#{qnid}"; end
280
- def rk_running(qnid); "#{prefix}TMRUNNING:#{qnid}"; end
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); qnid[0...qnid.index(':')]; end
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.2.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: ! " Rescheduler is a library that uses Redis to maintain a task queue
47
- of immediate and delayed jobs without any polling.\n\n Goals:\n 1. Light weight.
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: []