resque-durable 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,24 @@
1
+ module Resque
2
+ module Durable
3
+ module GUID
4
+
5
+ def self.generate
6
+ [ hostname,
7
+ Process.pid,
8
+ Time.now.to_i,
9
+ increment_counter
10
+ ].join('/')
11
+ end
12
+
13
+ def self.hostname
14
+ @hostname ||= `hostname`.chomp
15
+ end
16
+
17
+ def self.increment_counter
18
+ @counter ||= 0
19
+ @counter += 1
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ module Resque
2
+ module Durable
3
+ module Monitor
4
+
5
+ attr_accessor :auditor, :expiration, :wait_duration
6
+
7
+ def initialize(auditor)
8
+ @auditor = auditor
9
+ end
10
+
11
+ def watch
12
+ auditor.recover
13
+ auditor.cleanup(expiration.ago)
14
+ end
15
+
16
+ def run
17
+ install_signal_handlers
18
+
19
+ loop do
20
+ watch
21
+ wait
22
+ break if @stopped
23
+ end
24
+ end
25
+
26
+ def wait
27
+ sleep(wait_duration)
28
+ end
29
+
30
+ def wait_duration
31
+ @wait_duration ||= 1
32
+ end
33
+
34
+ def install_signal_handlers
35
+ trap('TERM') { stop }
36
+ trap('INT') { stop }
37
+ end
38
+
39
+ def stop
40
+ puts 'Stopping...'
41
+ @stopped = true
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,117 @@
1
+ require 'active_record'
2
+ require 'active_support/core_ext/class'
3
+
4
+ module Resque
5
+ module Durable
6
+ class QueueAudit < ActiveRecord::Base
7
+ set_table_name :durable_queue_audits
8
+ # id
9
+ # enqueued_id
10
+ # queue_name
11
+ # payload
12
+ # enqueue_count
13
+ # enqueued_at
14
+ # completed_at
15
+ # timeout_at
16
+ # updated_at
17
+ # created_at
18
+ DEFAULT_DURATION = 10.minutes
19
+
20
+ validates_length_of :payload_before_type_cast, :in => 1..5000
21
+
22
+ validates_inclusion_of :duration, :in => 1.minute..3.hours
23
+
24
+ named_scope :older_than, lambda { |date|
25
+ { :conditions => [ 'created_at < ?', date ] }
26
+ }
27
+
28
+ named_scope :failed, lambda {
29
+ { :conditions => [ 'completed_at is null AND timeout_at < ?', Time.now.utc ], :order => 'timeout_at asc' }
30
+ }
31
+
32
+ named_scope :complete, lambda {
33
+ { :conditions => 'completed_at is not null' }
34
+ }
35
+
36
+ module Recovery
37
+
38
+ def recover
39
+ failed.all(:limit => 500).each { |audit| audit.enqueue if audit.retryable? }
40
+ end
41
+
42
+ def cleanup(date)
43
+ older_than(date).destroy_all
44
+ end
45
+
46
+ end
47
+ extend Recovery
48
+
49
+
50
+ def self.initialize_by_klass_and_args(job_klass, args)
51
+ new(:job_klass => job_klass, :payload => args, :enqueued_id => GUID.generate)
52
+ end
53
+
54
+ def job_klass
55
+ read_attribute(:job_klass).constantize
56
+ end
57
+
58
+ def job_klass=(klass)
59
+ write_attribute(:job_klass, klass.to_s)
60
+ end
61
+
62
+ def payload
63
+ ActiveSupport::JSON.decode(super)
64
+ end
65
+
66
+ def payload=(value)
67
+ super value.to_json
68
+ end
69
+
70
+ def queue
71
+ Resque.queue_from_class(job_klass)
72
+ end
73
+
74
+ def enqueue
75
+ job_klass.enqueue(*(payload.push(self)))
76
+ end
77
+
78
+ def duration
79
+ job_klass.job_timeout
80
+ end
81
+
82
+ def heartbeat!
83
+ update_attribute(:timeout_at, Time.now.utc + duration)
84
+ end
85
+
86
+ def fail!
87
+ update_attribute(:timeout_at, Time.now.utc)
88
+ end
89
+
90
+ def enqueued!
91
+ self.enqueued_at = Time.now.utc
92
+ self.timeout_at = enqueued_at + duration
93
+ self.enqueue_count += 1
94
+ save!
95
+ end
96
+
97
+ def complete!
98
+ self.completed_at = Time.now.utc
99
+ save!
100
+ end
101
+
102
+ def complete?
103
+ completed_at.present?
104
+ end
105
+
106
+ def retryable?
107
+ Time.now.utc > (timeout_at + delay)
108
+ end
109
+
110
+ # 1, 8, 27, 64, 125, 216, etc. minutes.
111
+ def delay
112
+ (enqueue_count ** 3).minutes
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,77 @@
1
+ module Resque
2
+ module Durable
3
+ autoload :GUID, 'resque/durable/guid'
4
+ autoload :Monitor, 'resque/durable/monitor'
5
+ autoload :QueueAudit, 'resque/durable/queue_audit'
6
+
7
+ def self.extended(base)
8
+ base.cattr_accessor :job_timeout
9
+ base.job_timeout = 10.minutes
10
+
11
+ base.cattr_accessor :auditor
12
+ base.auditor = QueueAudit
13
+ end
14
+
15
+ def enqueue(*args)
16
+ if args.last.is_a?(auditor)
17
+ # the audit-is-re-enqueing case
18
+ audit = args.pop
19
+ else
20
+ audit = build_audit(args)
21
+ end
22
+
23
+ args << audit.enqueued_id
24
+ begin
25
+ audit.enqueued!
26
+ rescue Exception => e
27
+ audit_failed(e)
28
+ end
29
+
30
+ Resque.enqueue(self, *args)
31
+ rescue Exception => e
32
+ enqueue_failed(e)
33
+ end
34
+
35
+ def audit(args)
36
+ audit = auditor.find_by_enqueued_id(args.last)
37
+ audit_failed(ArgumentError.new("Could not find audit: #{args.last}")) if audit.nil?
38
+ audit
39
+ end
40
+
41
+ def heartbeat(args)
42
+ if a = audit(args)
43
+ a.heartbeat!
44
+ end
45
+ end
46
+
47
+ def around_perform_manage_audit(*args)
48
+ if a = audit(args)
49
+ a.heartbeat!
50
+ return if a.complete?
51
+ yield
52
+ a.complete!
53
+ else
54
+ yield
55
+ end
56
+ end
57
+
58
+ def on_failure_set_timeout(exception, *args)
59
+ if a = audit(args)
60
+ a.fail!
61
+ end
62
+ end
63
+
64
+ def build_audit(args)
65
+ auditor.initialize_by_klass_and_args(self, args)
66
+ end
67
+
68
+ def audit_failed(e)
69
+ raise e
70
+ end
71
+
72
+ def enqueue_failed(e)
73
+ raise e
74
+ end
75
+
76
+ end
77
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-durable
3
3
  version: !ruby/object:Gem::Version
4
- hash: 23
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Eric Chapweske
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2012-04-13 00:00:00 Z
19
+ date: 2012-04-16 00:00:00 Z
20
20
  dependencies: []
21
21
 
22
22
  description:
@@ -27,8 +27,11 @@ extensions: []
27
27
 
28
28
  extra_rdoc_files: []
29
29
 
30
- files: []
31
-
30
+ files:
31
+ - lib/resque/durable.rb
32
+ - lib/resque/durable/guid.rb
33
+ - lib/resque/durable/monitor.rb
34
+ - lib/resque/durable/queue_audit.rb
32
35
  homepage:
33
36
  licenses: []
34
37