resque-bus 0.3.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|