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