resque-bus 0.2.10 → 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/README.mdown CHANGED
@@ -101,6 +101,75 @@ If you want retry to work for subscribing apps, you should run resque-scheduler
101
101
 
102
102
  $ rake resquebus:driver resque:scheduler
103
103
 
104
+ ### Heartbeat
105
+
106
+ We've found it useful to have the bus act like cron. Triggering timed jobs throughout the system. Resque Bus calls this a heartbeat.
107
+ It uses resque-scheduler to trigger the events. You can enable it in your Rakefile.
108
+
109
+ # resque.rake
110
+ namespace :resque do
111
+ task :setup => [:environment] do
112
+ ResqueBus.heartbeat!
113
+ end
114
+ end
115
+
116
+ Or add it to your `schedule.yml` directly
117
+
118
+ resquebus_heartbeat:
119
+ cron: "* * * * *"
120
+ class: "::ResqueBus::Heartbeat"
121
+ queue: resquebus_incoming
122
+ description: "I publish a heartbeat_minutes event every minute"
123
+
124
+ It is the equivalent of doing this every minute
125
+
126
+ seconds = minutes * (60)
127
+ hours = minutes / (60)
128
+ days = minutes / (60*24)
129
+
130
+ now = Time.at(seconds)
131
+
132
+ attributes = {}
133
+
134
+
135
+ now = Time.now
136
+ seconds = now.to_i
137
+ ResqueBus.publish("hearbeat_minutes", {
138
+ "epoch_seconds" => seconds,
139
+ "epoch_minutes" => seconds / 1.minute,
140
+ "epoch_hours" => seconds / 1.hour,
141
+ "epoch_days" => seconds / 1.day,
142
+ "minute" => now.min
143
+ "hour" => now.hour
144
+ "day" => now.day
145
+ "month" => now.month
146
+ "year" => now.year
147
+ "yday" => now.yday
148
+ "wday" => now.wday
149
+ })
150
+
151
+ This allows you do something like this:
152
+
153
+ ResqueBus.dispatch("app_c") do
154
+ # runs at 10:20, 11:20, etc
155
+ subscribe "once_an_hour", 'bus_event_type' => 'heartbeat_minutes', 'minute' => 20 do |attributes|
156
+ Sitemap.generate!
157
+ end
158
+
159
+ # runs every five minutes
160
+ subscribe "every_five_minutes", 'bus_event_type' => 'heartbeat_minutes' do |attributes|
161
+ next unless attributes["epoch_minutes"] % 5 == 0
162
+ HealthCheck.run!
163
+ end
164
+
165
+ # runs at 8am on the first of every month
166
+ subscribe "new_month_morning", 'bus_event_type' => 'heartbeat_minutes', 'day' => 1, hour' => 8, 'minute' => 0, do |attributes|
167
+ next unless attributes["epoch_minutes"] % 5 == 0
168
+ Token.old.expire!
169
+ end
170
+ end
171
+
172
+
104
173
  ### Compatibility
105
174
 
106
175
  ResqueBus can live along side another instance of Resque that points at a different Redis server.
data/lib/resque-bus.rb CHANGED
@@ -3,12 +3,12 @@ require "resque_bus/version"
3
3
  require 'redis/namespace'
4
4
  require 'resque'
5
5
 
6
-
7
6
  module ResqueBus
8
7
 
9
8
  autoload :Application, 'resque_bus/application'
10
9
  autoload :Dispatch, 'resque_bus/dispatch'
11
10
  autoload :Driver, 'resque_bus/driver'
11
+ autoload :Heartbeat, 'resque_bus/heartbeat'
12
12
  autoload :Local, 'resque_bus/local'
13
13
  autoload :Matcher, 'resque_bus/matcher'
14
14
  autoload :Publisher, 'resque_bus/publisher'
@@ -71,6 +71,23 @@ module ResqueBus
71
71
  def local_mode
72
72
  @local_mode
73
73
  end
74
+
75
+ def heartbeat!
76
+ # turn on the heartbeat
77
+ # should be down after loading scheduler yml if you do that
78
+ # otherwise, anytime
79
+ require 'resque/scheduler'
80
+ name = 'resquebus_hearbeat'
81
+ schedule = { 'class' => '::ResqueBus::Heartbeat',
82
+ 'cron' => '* * * * *', # every minute
83
+ 'queue' => incoming_queue,
84
+ 'description' => 'I publish a heartbeat_minutes event every minute'
85
+ }
86
+ if Resque::Scheduler.dynamic
87
+ Resque.set_schedule(name, schedule)
88
+ end
89
+ Resque.schedule[name] = schedule
90
+ end
74
91
 
75
92
  # Accepts:
76
93
  # 1. A 'hostname:port' String
@@ -13,7 +13,7 @@ module ResqueBus
13
13
  end
14
14
 
15
15
  def perform(attributes={})
16
- raise "No attribiutes passed" if attributes.empty?
16
+ raise "No attributes passed" if attributes.empty?
17
17
 
18
18
  ResqueBus.log_worker("Driver running: #{attributes.inspect}")
19
19
 
@@ -0,0 +1,96 @@
1
+ module ResqueBus
2
+ # publishes event about the current time
3
+ class Heartbeat
4
+
5
+ class << self
6
+
7
+ def lock_key
8
+ "resquebus:heartbeat:lock"
9
+ end
10
+
11
+ def lock_seconds
12
+ 60
13
+ end
14
+
15
+ def lock!
16
+ now = Time.now.to_i
17
+ timeout = now + lock_seconds + 2
18
+
19
+ # return true if we successfully acquired the lock
20
+ return timeout if Resque.redis.setnx(lock_key, timeout)
21
+
22
+ # see if the existing timeout is still valid and return false if it is
23
+ # (we cannot acquire the lock during the timeout period)
24
+ return 0 if now <= Resque.redis.get(lock_key).to_i
25
+
26
+ # otherwise set the timeout and ensure that no other worker has
27
+ # acquired the lock
28
+ if now > Resque.redis.getset(lock_key, timeout).to_i
29
+ return timeout
30
+ else
31
+ return 0
32
+ end
33
+ end
34
+
35
+ def unlock!
36
+ Resque.redis.del(lock_key)
37
+ end
38
+
39
+
40
+ def redis_key
41
+ "resquebus:heartbeat:timestamp"
42
+ end
43
+
44
+ def get_saved_minute!
45
+ key = ResqueBus.redis.get(redis_key)
46
+ return nil if key.nil?
47
+ return key.to_i
48
+ end
49
+
50
+ def set_saved_minute!(epoch_minute)
51
+ ResqueBus.redis.set(redis_key, epoch_minute)
52
+ end
53
+
54
+ def perform
55
+ real_now = Time.now.to_i
56
+ run_until = lock! - 2
57
+ return if run_until < real_now
58
+
59
+ while((real_now = Time.now.to_i) < run_until)
60
+ minutes = real_now.to_i / 60
61
+ last = get_saved_minute!
62
+ if last
63
+ break if minutes <= last
64
+ minutes = last + 1
65
+ end
66
+
67
+ seconds = minutes * (60)
68
+ hours = minutes / (60)
69
+ days = minutes / (60*24)
70
+
71
+ now = Time.at(seconds)
72
+
73
+ attributes = {}
74
+ attributes["epoch_seconds"] = seconds
75
+ attributes["epoch_minutes"] = minutes
76
+ attributes["epoch_hours"] = hours
77
+ attributes["epoch_days"] = days
78
+
79
+ attributes["minute"] = now.min
80
+ attributes["hour"] = now.hour
81
+ attributes["day"] = now.day
82
+ attributes["month"] = now.month
83
+ attributes["year"] = now.year
84
+ attributes["yday"] = now.yday
85
+ attributes["wday"] = now.wday
86
+
87
+ ResqueBus.publish("heartbeat_minutes", attributes)
88
+ set_saved_minute!(minutes)
89
+ end
90
+
91
+ unlock!
92
+ end
93
+ end
94
+
95
+ end
96
+ end
@@ -1,5 +1,5 @@
1
1
  module Resque
2
2
  module Bus
3
- VERSION = "0.2.10"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
data/spec/driver_spec.rb CHANGED
@@ -34,7 +34,7 @@ module ResqueBus
34
34
  end
35
35
  end
36
36
 
37
- describe ".peform" do
37
+ describe ".perform" do
38
38
  let(:attributes) { {"x" => "y"} }
39
39
 
40
40
  before(:each) do
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ module ResqueBus
4
+ describe Heartbeat do
5
+ def now_attributes
6
+ {
7
+ "epoch_seconds" => (Time.now.to_i / 60) * 60, # rounded
8
+ "epoch_minutes" => Time.now.to_i / 60,
9
+ "epoch_hours" => Time.now.to_i / (60*60),
10
+ "epoch_days" => Time.now.to_i / (60*60*24),
11
+ "minute" => Time.now.min,
12
+ "hour" => Time.now.hour,
13
+ "day" => Time.now.day,
14
+ "month" => Time.now.month,
15
+ "year" => Time.now.year,
16
+ "yday" => Time.now.yday,
17
+ "wday" => Time.now.wday
18
+ }
19
+ end
20
+
21
+ it "should publish the current time once" do
22
+ Timecop.freeze "12/12/2013 12:01:19" do
23
+ ResqueBus.should_receive(:publish).with("heartbeat_minutes", now_attributes)
24
+ Heartbeat.perform
25
+ end
26
+
27
+ Timecop.freeze "12/12/2013 12:01:40" do
28
+ Heartbeat.perform
29
+ end
30
+ end
31
+
32
+ it "should publish a minute later" do
33
+ Timecop.freeze "12/12/2013 12:01:19" do
34
+ ResqueBus.should_receive(:publish).with("heartbeat_minutes", now_attributes)
35
+ Heartbeat.perform
36
+ end
37
+
38
+ debugger
39
+
40
+ Timecop.freeze "12/12/2013 12:02:01" do
41
+ ResqueBus.should_receive(:publish).with("heartbeat_minutes", now_attributes)
42
+ Heartbeat.perform
43
+ end
44
+ end
45
+ end
46
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque-bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.10
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-09-12 00:00:00.000000000 Z
12
+ date: 2013-09-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: resque
@@ -164,6 +164,7 @@ files:
164
164
  - lib/resque_bus/application.rb
165
165
  - lib/resque_bus/dispatch.rb
166
166
  - lib/resque_bus/driver.rb
167
+ - lib/resque_bus/heartbeat.rb
167
168
  - lib/resque_bus/local.rb
168
169
  - lib/resque_bus/matcher.rb
169
170
  - lib/resque_bus/publisher.rb
@@ -182,6 +183,7 @@ files:
182
183
  - spec/application_spec.rb
183
184
  - spec/dispatch_spec.rb
184
185
  - spec/driver_spec.rb
186
+ - spec/heartbeat_spec.rb
185
187
  - spec/integration_spec.rb
186
188
  - spec/matcher_spec.rb
187
189
  - spec/publish_at_spec.rb
@@ -220,6 +222,7 @@ test_files:
220
222
  - spec/application_spec.rb
221
223
  - spec/dispatch_spec.rb
222
224
  - spec/driver_spec.rb
225
+ - spec/heartbeat_spec.rb
223
226
  - spec/integration_spec.rb
224
227
  - spec/matcher_spec.rb
225
228
  - spec/publish_at_spec.rb