resque-durable 0.2.0 → 0.2.1

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.
@@ -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