resque-bus 0.2.10 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.mdown +69 -0
- data/lib/resque-bus.rb +18 -1
- data/lib/resque_bus/driver.rb +1 -1
- data/lib/resque_bus/heartbeat.rb +96 -0
- data/lib/resque_bus/version.rb +1 -1
- data/spec/driver_spec.rb +1 -1
- data/spec/heartbeat_spec.rb +46 -0
- metadata +5 -2
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
|
data/lib/resque_bus/driver.rb
CHANGED
@@ -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
|
data/lib/resque_bus/version.rb
CHANGED
data/spec/driver_spec.rb
CHANGED
@@ -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.
|
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
|
+
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
|