resque-bus 0.3.0 → 0.3.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.
- data/README.mdown +158 -132
- data/lib/resque_bus/heartbeat.rb +10 -0
- data/lib/resque_bus/version.rb +1 -1
- data/spec/heartbeat_spec.rb +0 -2
- metadata +2 -2
data/README.mdown
CHANGED
@@ -6,88 +6,103 @@ This gem uses Redis and Resque to allow simple asynchronous communication betwee
|
|
6
6
|
|
7
7
|
Application A can publish an event
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
```ruby
|
10
|
+
# config
|
11
|
+
ResqueBus.redis = "192.168.1.1:6379"
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
# business logic
|
14
|
+
ResqueBus.publish("user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
|
15
|
+
|
16
|
+
# or do it later
|
17
|
+
ResqueBus.publish_at(1.hour.from_now, "user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
|
18
|
+
```
|
17
19
|
|
18
20
|
Application B is subscribed to events
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
22
|
+
```ruby
|
23
|
+
# config
|
24
|
+
ResqueBus.redis = "192.168.1.1:6379"
|
25
|
+
|
26
|
+
# initializer
|
27
|
+
ResqueBus.dispatch("app_b") do
|
28
|
+
# processes event on app_b_default queue
|
29
|
+
# subscribe is short-hand to subscribe to your 'default' queue and this block with process events with the name "user_created"
|
30
|
+
subscribe "user_created" do |attributes|
|
31
|
+
NameCount.find_or_create_by_name(attributes["last_name"]).increment!
|
32
|
+
end
|
33
|
+
|
34
|
+
# processes event on app_b_critical queue
|
35
|
+
# critical is short-hand to subscribe to your 'critical' queue and this block with process events with the name "user_paid"
|
36
|
+
critical "user_paid" do |attributes|
|
37
|
+
CreditCard.charge!(attributes)
|
38
|
+
end
|
39
|
+
|
40
|
+
# you can pass any queue name you would like to process from as well IE: `banana "peeled" do |attributes|`
|
41
|
+
|
42
|
+
# and regexes work as well. note that with the above configuration along with this regex,
|
43
|
+
# the following as well as the corresponding block above would both be executed
|
44
|
+
subscribe /^user_/ do |attributes|
|
45
|
+
Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
|
46
|
+
end
|
47
|
+
|
48
|
+
# the above all filter on just the event_type, but you can filter on anything
|
49
|
+
# this would be _any_ event that has a user_id and the page value of homepage regardless of bus_event_type
|
50
|
+
subscribe "my_key", { "user_id" => :present, "page" => "homepage"} do
|
51
|
+
Mixpanel.homepage_action!(attributes["action"])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
```
|
51
55
|
|
52
56
|
Applications can also subscribe within classes using the provided `Subscriber` module.
|
53
57
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
```ruby
|
59
|
+
class SimpleSubscriber
|
60
|
+
include ResqueBus::Subscriber
|
61
|
+
subscribe :my_method
|
62
|
+
|
63
|
+
def my_method(attributes)
|
64
|
+
# heavy lifting
|
65
|
+
end
|
66
|
+
end
|
67
|
+
```
|
62
68
|
|
63
69
|
The following is equivalent to the original initializer and shows more options:
|
64
70
|
|
65
|
-
|
66
|
-
|
67
|
-
|
71
|
+
```ruby
|
72
|
+
class OtherSubscriber
|
73
|
+
include ResqueBus::Subscriber
|
74
|
+
application :app_b
|
75
|
+
|
76
|
+
subscribe :user_created
|
77
|
+
subscribe_queue :app_b_critical, :user_paid
|
78
|
+
subscribe_queue :app_b_default, :user_action, :bus_event_type => /^user_/
|
79
|
+
subscribe :homepage_method, :user_id => :present, :page => "homepage"
|
68
80
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
subscribe :homepage_method, :user_id => :present, :page => "homepage"
|
81
|
+
def user_created(attributes)
|
82
|
+
NameCount.find_or_create_by_name(attributes["last_name"]).increment!
|
83
|
+
end
|
73
84
|
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
def user_paid(attributes)
|
86
|
+
CreditCard.charge!(attributes)
|
87
|
+
end
|
77
88
|
|
78
|
-
|
79
|
-
|
80
|
-
|
89
|
+
def user_action(attributes)
|
90
|
+
Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
|
91
|
+
end
|
81
92
|
|
82
|
-
|
83
|
-
|
84
|
-
|
93
|
+
def homepage_method
|
94
|
+
Mixpanel.homepage_action!(attributes["action"])
|
95
|
+
end
|
96
|
+
end
|
97
|
+
```
|
85
98
|
|
86
|
-
|
87
|
-
Mixpanel.homepage_action!(attributes["action"])
|
88
|
-
end
|
89
|
-
end
|
99
|
+
Note: This subscribes when this class is loaded, so it needs to be in your load or otherwise referenced/required during app initialization to work properly.
|
90
100
|
|
101
|
+
### Commands
|
102
|
+
|
103
|
+
Each app needs to tell Redis about its subscriptions:
|
104
|
+
|
105
|
+
$ rake resquebus:subscribe
|
91
106
|
|
92
107
|
The subscription block is run inside a Resque worker which needs to be started for each app.
|
93
108
|
|
@@ -99,30 +114,35 @@ The incoming queue also needs to be processed on a dedicated or all the app serv
|
|
99
114
|
|
100
115
|
If you want retry to work for subscribing apps, you should run resque-scheduler
|
101
116
|
|
102
|
-
$ rake
|
117
|
+
$ rake resque:scheduler
|
103
118
|
|
104
119
|
### Heartbeat
|
105
120
|
|
106
|
-
We've found it useful to have the bus act like cron
|
121
|
+
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
122
|
It uses resque-scheduler to trigger the events. You can enable it in your Rakefile.
|
108
123
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
124
|
+
```ruby
|
125
|
+
# resque.rake
|
126
|
+
namespace :resque do
|
127
|
+
task :setup => [:environment] do
|
128
|
+
ResqueBus.heartbeat!
|
129
|
+
end
|
130
|
+
end
|
131
|
+
```
|
115
132
|
|
116
133
|
Or add it to your `schedule.yml` directly
|
117
134
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
135
|
+
```yaml
|
136
|
+
resquebus_heartbeat:
|
137
|
+
cron: "* * * * *"
|
138
|
+
class: "::ResqueBus::Heartbeat"
|
139
|
+
queue: resquebus_incoming
|
140
|
+
description: "I publish a heartbeat_minutes event every minute"
|
141
|
+
```
|
123
142
|
|
124
143
|
It is the equivalent of doing this every minute
|
125
144
|
|
145
|
+
```ruby
|
126
146
|
seconds = minutes * (60)
|
127
147
|
hours = minutes / (60)
|
128
148
|
days = minutes / (60*24)
|
@@ -131,74 +151,81 @@ now = Time.at(seconds)
|
|
131
151
|
|
132
152
|
attributes = {}
|
133
153
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
154
|
+
now = Time.now
|
155
|
+
seconds = now.to_i
|
156
|
+
ResqueBus.publish("hearbeat_minutes", {
|
157
|
+
"epoch_seconds" => seconds,
|
158
|
+
"epoch_minutes" => seconds / 1.minute,
|
159
|
+
"epoch_hours" => seconds / 1.hour,
|
160
|
+
"epoch_days" => seconds / 1.day,
|
161
|
+
"minute" => now.min
|
162
|
+
"hour" => now.hour
|
163
|
+
"day" => now.day
|
164
|
+
"month" => now.month
|
165
|
+
"year" => now.year
|
166
|
+
"yday" => now.yday
|
167
|
+
"wday" => now.wday
|
168
|
+
})
|
169
|
+
```
|
150
170
|
|
151
171
|
This allows you do something like this:
|
152
172
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
173
|
+
```ruby
|
174
|
+
ResqueBus.dispatch("app_c") do
|
175
|
+
# runs at 10:20, 11:20, etc
|
176
|
+
subscribe "once_an_hour", 'bus_event_type' => 'heartbeat_minutes', 'minute' => 20 do |attributes|
|
177
|
+
Sitemap.generate!
|
178
|
+
end
|
179
|
+
|
180
|
+
# runs every five minutes
|
181
|
+
subscribe "every_five_minutes", 'bus_event_type' => 'heartbeat_minutes' do |attributes|
|
182
|
+
next unless attributes["epoch_minutes"] % 5 == 0
|
183
|
+
HealthCheck.run!
|
184
|
+
end
|
185
|
+
|
186
|
+
# runs at 8am on the first of every month
|
187
|
+
subscribe "new_month_morning", 'bus_event_type' => 'heartbeat_minutes', 'day' => 1, hour' => 8, 'minute' => 0, do |attributes|
|
188
|
+
next unless attributes["epoch_minutes"] % 5 == 0
|
189
|
+
Token.old.expire!
|
190
|
+
end
|
191
|
+
end
|
192
|
+
```
|
172
193
|
|
173
194
|
### Compatibility
|
174
195
|
|
175
196
|
ResqueBus can live along side another instance of Resque that points at a different Redis server.
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
# config
|
200
|
+
Resque.redis = "192.168.1.0:6379"
|
201
|
+
ResqueBus.redis = "192.168.1.1:6379"
|
202
|
+
```
|
180
203
|
|
181
204
|
If no Redis instance is given specifically, ResqueBus will use the Resque one.
|
182
205
|
|
183
|
-
|
184
|
-
|
206
|
+
```ruby
|
207
|
+
# config
|
208
|
+
Resque.redis = "192.168.1.0:6379"
|
209
|
+
```
|
185
210
|
|
186
211
|
That will use the default (resque) namespace which can be helpful for using the tooling. Conflict with queue names are unlikely. You can change the namespace if you like though.
|
187
212
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
213
|
+
```ruby
|
214
|
+
# config
|
215
|
+
Resque.redis = "192.168.1.0:6379"
|
216
|
+
ResqusBus.redis.namespace = :get_on_the_bus
|
217
|
+
```
|
192
218
|
|
193
219
|
### Local Mode
|
194
220
|
|
195
|
-
For development, a local mode is also provided and is specified in the
|
196
|
-
configuration.
|
221
|
+
For development, a local mode is also provided and is specified in the configuration.
|
197
222
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
223
|
+
```ruby
|
224
|
+
# config
|
225
|
+
ResqueBus.local_mode = :standalone
|
226
|
+
or
|
227
|
+
ResqueBus.local_mode = :inline
|
228
|
+
```
|
202
229
|
|
203
230
|
Standalone mode does not require a separate resquebus:driver task to be running to process the
|
204
231
|
incoming queue. Simply publishing to the bus will distribute the incoming events
|
@@ -215,7 +242,6 @@ event to the appropriate code block.
|
|
215
242
|
* Make this not freak out in development without Redis or when Redis is down
|
216
243
|
* We might not actually need to publish in tests
|
217
244
|
* Add some rspec helpers for the apps to use: should_ post an event_publish or something along those lines
|
218
|
-
*
|
219
|
-
* Should this use resque-retry or should they jsut go into the failure queue?
|
245
|
+
* Allow calling resquebus:setup and resquebus:driver together (append to ENV['QUEUES'], don't replace it)
|
220
246
|
|
221
247
|
Copyright (c) 2011 Brian Leonard, released under the MIT license
|
data/lib/resque_bus/heartbeat.rb
CHANGED
@@ -41,9 +41,19 @@ module ResqueBus
|
|
41
41
|
"resquebus:heartbeat:timestamp"
|
42
42
|
end
|
43
43
|
|
44
|
+
def environment_name
|
45
|
+
ENV["RAILS_ENV"] || ENV["RACK_ENV"] || ENV["RESQUEBUS_ENV"]
|
46
|
+
end
|
47
|
+
|
44
48
|
def get_saved_minute!
|
45
49
|
key = ResqueBus.redis.get(redis_key)
|
46
50
|
return nil if key.nil?
|
51
|
+
case environment_name
|
52
|
+
when 'development', 'test'
|
53
|
+
# only 3 minutes in development; otherwise, TONS of events if not run in a while
|
54
|
+
three_ago = Time.now.to_i - 3*60*60
|
55
|
+
key = three_ago if key.to_i < three_ago
|
56
|
+
end
|
47
57
|
return key.to_i
|
48
58
|
end
|
49
59
|
|
data/lib/resque_bus/version.rb
CHANGED
data/spec/heartbeat_spec.rb
CHANGED
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.3.
|
4
|
+
version: 0.3.1
|
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-
|
12
|
+
date: 2013-10-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: resque
|