resque-bus 0.3.7 → 0.5.7
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/.gitignore +1 -0
- data/Gemfile +2 -2
- data/README.mdown +42 -64
- data/Rakefile +0 -1
- data/lib/resque-bus.rb +13 -305
- data/lib/resque_bus/adapter.rb +65 -0
- data/lib/resque_bus/compatibility/deprecated.rb +38 -0
- data/lib/resque_bus/compatibility/driver.rb +10 -0
- data/lib/resque_bus/compatibility/heartbeat.rb +10 -0
- data/lib/resque_bus/compatibility/publisher.rb +13 -0
- data/lib/resque_bus/compatibility/rider.rb +32 -0
- data/lib/resque_bus/compatibility/subscriber.rb +8 -0
- data/lib/resque_bus/compatibility/task_manager.rb +8 -0
- data/lib/resque_bus/server/views/bus.erb +2 -2
- data/lib/resque_bus/server.rb +5 -4
- data/lib/resque_bus/tasks.rb +46 -46
- data/lib/resque_bus/version.rb +2 -4
- data/resque-bus.gemspec +5 -10
- data/spec/adapter/compatibility_spec.rb +97 -0
- data/spec/adapter/integration_spec.rb +111 -0
- data/spec/adapter/publish_at_spec.rb +50 -0
- data/spec/adapter/retry_spec.rb +47 -0
- data/spec/adapter/support.rb +23 -0
- data/spec/adapter_spec.rb +14 -0
- data/spec/application_spec.rb +62 -62
- data/spec/config_spec.rb +83 -0
- data/spec/dispatch_spec.rb +6 -6
- data/spec/driver_spec.rb +62 -44
- data/spec/heartbeat_spec.rb +4 -4
- data/spec/integration_spec.rb +2 -2
- data/spec/matcher_spec.rb +29 -29
- data/spec/publish_spec.rb +46 -43
- data/spec/publisher_spec.rb +7 -0
- data/spec/rider_spec.rb +14 -66
- data/spec/spec_helper.rb +25 -25
- data/spec/subscriber_spec.rb +194 -176
- data/spec/subscription_list_spec.rb +1 -1
- data/spec/subscription_spec.rb +1 -1
- data/spec/worker_spec.rb +32 -0
- metadata +47 -58
- data/lib/resque_bus/application.rb +0 -115
- data/lib/resque_bus/compatibility.rb +0 -24
- data/lib/resque_bus/dispatch.rb +0 -61
- data/lib/resque_bus/driver.rb +0 -30
- data/lib/resque_bus/heartbeat.rb +0 -106
- data/lib/resque_bus/local.rb +0 -38
- data/lib/resque_bus/matcher.rb +0 -81
- data/lib/resque_bus/publisher.rb +0 -12
- data/lib/resque_bus/rider.rb +0 -54
- data/lib/resque_bus/subscriber.rb +0 -63
- data/lib/resque_bus/subscription.rb +0 -55
- data/lib/resque_bus/subscription_list.rb +0 -53
- data/lib/resque_bus/task_manager.rb +0 -52
- data/lib/resque_bus/util.rb +0 -42
- data/lib/tasks/resquebus.rake +0 -2
- data/spec/compatibility_spec.rb +0 -93
- data/spec/publish_at_spec.rb +0 -74
- data/spec/redis_spec.rb +0 -13
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.mdown
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
## Resque Bus
|
2
2
|
|
3
|
-
This gem
|
3
|
+
This gem provides an adapter for Resque for use in the [queue-bus](https://github.com/queue-bus/queue-bus) system.
|
4
|
+
It uses Redis and the Resque that you are already using to allow simple asynchronous communication between apps.
|
4
5
|
|
5
6
|
### Install
|
6
7
|
|
@@ -10,35 +11,36 @@ To install, include the 'resque-bus' gem and add the following to your Rakefile:
|
|
10
11
|
require "resque_bus/tasks"
|
11
12
|
```
|
12
13
|
|
14
|
+
|
13
15
|
### Example
|
14
16
|
|
15
17
|
Application A can publish an event
|
16
18
|
|
17
19
|
```ruby
|
18
|
-
#
|
19
|
-
|
20
|
+
# pick an adapter
|
21
|
+
require 'resque-bus' # (or other adapter)
|
20
22
|
|
21
23
|
# business logic
|
22
|
-
|
24
|
+
QueueBus.publish("user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
|
23
25
|
|
24
26
|
# or do it later
|
25
|
-
|
27
|
+
QueueBus.publish_at(1.hour.from_now, "user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
|
26
28
|
```
|
27
29
|
|
28
30
|
Application B is subscribed to events
|
29
31
|
|
30
32
|
```ruby
|
31
|
-
#
|
32
|
-
|
33
|
+
# pick an adapter
|
34
|
+
require 'resque-bus' # (or other adapter)
|
33
35
|
|
34
36
|
# initializer
|
35
|
-
|
37
|
+
QueueBus.dispatch("app_b") do
|
36
38
|
# processes event on app_b_default queue
|
37
39
|
# subscribe is short-hand to subscribe to your 'default' queue and this block with process events with the name "user_created"
|
38
40
|
subscribe "user_created" do |attributes|
|
39
41
|
NameCount.find_or_create_by_name(attributes["last_name"]).increment!
|
40
42
|
end
|
41
|
-
|
43
|
+
|
42
44
|
# processes event on app_b_critical queue
|
43
45
|
# critical is short-hand to subscribe to your 'critical' queue and this block with process events with the name "user_paid"
|
44
46
|
critical "user_paid" do |attributes|
|
@@ -46,13 +48,13 @@ ResqueBus.dispatch("app_b") do
|
|
46
48
|
end
|
47
49
|
|
48
50
|
# you can pass any queue name you would like to process from as well IE: `banana "peeled" do |attributes|`
|
49
|
-
|
51
|
+
|
50
52
|
# and regexes work as well. note that with the above configuration along with this regex,
|
51
53
|
# the following as well as the corresponding block above would both be executed
|
52
54
|
subscribe /^user_/ do |attributes|
|
53
55
|
Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
|
54
56
|
end
|
55
|
-
|
57
|
+
|
56
58
|
# the above all filter on just the event_type, but you can filter on anything
|
57
59
|
# this would be _any_ event that has a user_id and the page value of homepage regardless of bus_event_type
|
58
60
|
subscribe "my_key", { "user_id" => :present, "page" => "homepage"} do
|
@@ -61,11 +63,11 @@ ResqueBus.dispatch("app_b") do
|
|
61
63
|
end
|
62
64
|
```
|
63
65
|
|
64
|
-
Applications can also subscribe within classes using the provided `Subscriber` module.
|
66
|
+
Applications can also subscribe within classes using the provided `Subscriber` module.
|
65
67
|
|
66
68
|
```ruby
|
67
69
|
class SimpleSubscriber
|
68
|
-
include
|
70
|
+
include QueueBus::Subscriber
|
69
71
|
subscribe :my_method
|
70
72
|
|
71
73
|
def my_method(attributes)
|
@@ -78,7 +80,7 @@ The following is equivalent to the original initializer and shows more options:
|
|
78
80
|
|
79
81
|
```ruby
|
80
82
|
class OtherSubscriber
|
81
|
-
include
|
83
|
+
include QueueBus::Subscriber
|
82
84
|
application :app_b
|
83
85
|
|
84
86
|
subscribe :user_created
|
@@ -110,20 +112,21 @@ Note: This subscribes when this class is loaded, so it needs to be in your load
|
|
110
112
|
|
111
113
|
Each app needs to tell Redis about its subscriptions:
|
112
114
|
|
113
|
-
$ rake
|
115
|
+
$ rake queuebus:subscribe
|
114
116
|
|
115
117
|
The subscription block is run inside a Resque worker which needs to be started for each app.
|
116
118
|
|
117
|
-
$ rake
|
118
|
-
|
119
|
+
$ rake queuebus:setup resque:work
|
120
|
+
|
119
121
|
The incoming queue also needs to be processed on a dedicated or all the app servers.
|
120
122
|
|
121
|
-
$ rake
|
122
|
-
|
123
|
+
$ rake queuebus:driver resque:work
|
124
|
+
|
123
125
|
If you want retry to work for subscribing apps, you should run resque-scheduler
|
124
|
-
|
126
|
+
|
125
127
|
$ rake resque:scheduler
|
126
128
|
|
129
|
+
|
127
130
|
### Heartbeat
|
128
131
|
|
129
132
|
We've found it useful to have the bus act like `cron`, triggering timed jobs throughout the system. Resque Bus calls this a heartbeat.
|
@@ -133,7 +136,7 @@ It uses resque-scheduler to trigger the events. You can enable it in your Rakefi
|
|
133
136
|
# resque.rake
|
134
137
|
namespace :resque do
|
135
138
|
task :setup => [:environment] do
|
136
|
-
|
139
|
+
QueueBus.heartbeat!
|
137
140
|
end
|
138
141
|
end
|
139
142
|
```
|
@@ -143,8 +146,8 @@ Or add it to your `schedule.yml` directly
|
|
143
146
|
```yaml
|
144
147
|
resquebus_heartbeat:
|
145
148
|
cron: "* * * * *"
|
146
|
-
class: "::
|
147
|
-
queue:
|
149
|
+
class: "::QueueBus::Heartbeat"
|
150
|
+
queue: bus_incoming
|
148
151
|
description: "I publish a heartbeat_minutes event every minute"
|
149
152
|
```
|
150
153
|
|
@@ -161,7 +164,7 @@ attributes = {}
|
|
161
164
|
|
162
165
|
now = Time.now
|
163
166
|
seconds = now.to_i
|
164
|
-
|
167
|
+
QueueBus.publish("hearbeat_minutes", {
|
165
168
|
"epoch_seconds" => seconds,
|
166
169
|
"epoch_minutes" => seconds / 1.minute,
|
167
170
|
"epoch_hours" => seconds / 1.hour,
|
@@ -179,18 +182,18 @@ ResqueBus.publish("hearbeat_minutes", {
|
|
179
182
|
This allows you do something like this:
|
180
183
|
|
181
184
|
```ruby
|
182
|
-
|
185
|
+
QueueBus.dispatch("app_c") do
|
183
186
|
# runs at 10:20, 11:20, etc
|
184
187
|
subscribe "once_an_hour", 'bus_event_type' => 'heartbeat_minutes', 'minute' => 20 do |attributes|
|
185
188
|
Sitemap.generate!
|
186
189
|
end
|
187
|
-
|
190
|
+
|
188
191
|
# runs every five minutes
|
189
192
|
subscribe "every_five_minutes", 'bus_event_type' => 'heartbeat_minutes' do |attributes|
|
190
193
|
next unless attributes["epoch_minutes"] % 5 == 0
|
191
194
|
HealthCheck.run!
|
192
195
|
end
|
193
|
-
|
196
|
+
|
194
197
|
# runs at 8am on the first of every month
|
195
198
|
subscribe "new_month_morning", 'bus_event_type' => 'heartbeat_minutes', 'day' => 1, hour' => 8, 'minute' => 0, do |attributes|
|
196
199
|
next unless attributes["epoch_minutes"] % 5 == 0
|
@@ -199,57 +202,32 @@ ResqueBus.dispatch("app_c") do
|
|
199
202
|
end
|
200
203
|
```
|
201
204
|
|
202
|
-
### Compatibility
|
203
|
-
|
204
|
-
ResqueBus can live along side another instance of Resque that points at a different Redis server.
|
205
|
-
|
206
|
-
```ruby
|
207
|
-
# config
|
208
|
-
Resque.redis = "192.168.1.0:6379"
|
209
|
-
ResqueBus.redis = "192.168.1.1:6379"
|
210
|
-
```
|
211
|
-
|
212
|
-
If no Redis instance is given specifically, ResqueBus will use the Resque one.
|
213
|
-
|
214
|
-
```ruby
|
215
|
-
# config
|
216
|
-
Resque.redis = "192.168.1.0:6379"
|
217
|
-
```
|
218
|
-
|
219
|
-
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.
|
220
|
-
|
221
|
-
```ruby
|
222
|
-
# config
|
223
|
-
Resque.redis = "192.168.1.0:6379"
|
224
|
-
ResqueBus.redis.namespace = :get_on_the_bus
|
225
|
-
```
|
226
|
-
|
227
205
|
### Local Mode
|
228
206
|
|
229
|
-
For development, a local mode is
|
207
|
+
For development, a local mode is provided and is specified in the configuration.
|
230
208
|
|
231
209
|
```ruby
|
232
210
|
# config
|
233
|
-
|
234
|
-
or
|
235
|
-
|
211
|
+
QueueBus.local_mode = :standalone
|
212
|
+
or
|
213
|
+
QueueBus.local_mode = :inline
|
236
214
|
```
|
237
215
|
|
238
|
-
Standalone mode does not require a separate
|
239
|
-
incoming queue. Simply publishing to the bus will distribute the incoming events
|
240
|
-
to the appropriate application specific queue. A separate
|
216
|
+
Standalone mode does not require a separate queuebus:driver task to be running to process the
|
217
|
+
incoming queue. Simply publishing to the bus will distribute the incoming events
|
218
|
+
to the appropriate application specific queue. A separate queuebus:work task does
|
241
219
|
still need to be run to process these events
|
242
220
|
|
243
221
|
Inline mode skips queue processing entirely and directly dispatches the
|
244
|
-
event to the appropriate code block.
|
222
|
+
event to the appropriate code block.
|
245
223
|
|
224
|
+
You can also say `QueueBus.local_mode = :suppress` to turn off publishing altogether.
|
225
|
+
This can be helpful inside some sort of migration, for example.
|
246
226
|
|
247
227
|
### TODO
|
248
228
|
|
249
|
-
*
|
229
|
+
* Replace local modes with adapters
|
250
230
|
* Make this not freak out in development without Redis or when Redis is down
|
251
231
|
* We might not actually need to publish in tests
|
252
232
|
* Add some rspec helpers for the apps to use: should_ post an event_publish or something along those lines
|
253
|
-
* Allow calling
|
254
|
-
|
255
|
-
Copyright (c) 2011 Brian Leonard, released under the MIT license
|
233
|
+
* Allow calling queuebus:setup and queuebus:driver together (append to ENV['QUEUES'], don't replace it)
|
data/Rakefile
CHANGED
data/lib/resque-bus.rb
CHANGED
@@ -1,311 +1,19 @@
|
|
1
|
+
require "queue-bus"
|
2
|
+
require "resque_bus/adapter"
|
1
3
|
require "resque_bus/version"
|
2
4
|
|
3
|
-
require 'redis/namespace'
|
4
|
-
require 'resque'
|
5
|
-
|
6
|
-
module QueueBus
|
7
|
-
autoload :Worker, 'resque_bus/compatibility'
|
8
|
-
end
|
9
|
-
|
10
5
|
module ResqueBus
|
11
|
-
|
12
|
-
autoload :Application, 'resque_bus/application'
|
13
|
-
autoload :Dispatch, 'resque_bus/dispatch'
|
14
|
-
autoload :Driver, 'resque_bus/driver'
|
15
|
-
autoload :Heartbeat, 'resque_bus/heartbeat'
|
16
|
-
autoload :Local, 'resque_bus/local'
|
17
|
-
autoload :Matcher, 'resque_bus/matcher'
|
18
|
-
autoload :Publisher, 'resque_bus/publisher'
|
19
|
-
autoload :Rider, 'resque_bus/rider'
|
20
|
-
autoload :Subscriber, 'resque_bus/subscriber'
|
21
|
-
autoload :Subscription, 'resque_bus/subscription'
|
22
|
-
autoload :SubscriptionList, 'resque_bus/subscription_list'
|
23
|
-
autoload :TaskManager, 'resque_bus/task_manager'
|
24
|
-
autoload :Util, 'resque_bus/util'
|
25
|
-
|
26
|
-
class << self
|
27
|
-
|
28
|
-
def default_app_key=val
|
29
|
-
@default_app_key = Application.normalize(val)
|
30
|
-
end
|
31
|
-
|
32
|
-
def default_app_key
|
33
|
-
@default_app_key
|
34
|
-
end
|
35
|
-
|
36
|
-
def default_queue=val
|
37
|
-
@default_queue = val
|
38
|
-
end
|
39
|
-
|
40
|
-
def default_queue
|
41
|
-
@default_queue
|
42
|
-
end
|
43
|
-
|
44
|
-
def hostname
|
45
|
-
@hostname ||= `hostname 2>&1`.strip.sub(/.local/,'')
|
46
|
-
end
|
47
|
-
|
48
|
-
def dispatch(app_key=nil, &block)
|
49
|
-
dispatcher = dispatcher_by_key(app_key)
|
50
|
-
dispatcher.instance_eval(&block)
|
51
|
-
dispatcher
|
52
|
-
end
|
53
|
-
|
54
|
-
def dispatchers
|
55
|
-
@dispatchers ||= {}
|
56
|
-
@dispatchers.values
|
57
|
-
end
|
58
|
-
|
59
|
-
def dispatcher_by_key(app_key)
|
60
|
-
app_key = Application.normalize(app_key || default_app_key)
|
61
|
-
@dispatchers ||= {}
|
62
|
-
@dispatchers[app_key] ||= Dispatch.new(app_key)
|
63
|
-
end
|
64
|
-
|
65
|
-
def dispatcher_execute(app_key, key, attributes)
|
66
|
-
@dispatchers ||= {}
|
67
|
-
dispatcher = @dispatchers[app_key]
|
68
|
-
dispatcher.execute(key, attributes) if dispatcher
|
69
|
-
end
|
70
|
-
|
71
|
-
def local_mode=value
|
72
|
-
@local_mode = value
|
73
|
-
end
|
74
|
-
|
75
|
-
def local_mode
|
76
|
-
@local_mode
|
77
|
-
end
|
78
|
-
|
79
|
-
def heartbeat!
|
80
|
-
# turn on the heartbeat
|
81
|
-
# should be down after loading scheduler yml if you do that
|
82
|
-
# otherwise, anytime
|
83
|
-
require 'resque/scheduler'
|
84
|
-
name = 'resquebus_hearbeat'
|
85
|
-
schedule = { 'class' => '::ResqueBus::Heartbeat',
|
86
|
-
'cron' => '* * * * *', # every minute
|
87
|
-
'queue' => incoming_queue,
|
88
|
-
'description' => 'I publish a heartbeat_minutes event every minute'
|
89
|
-
}
|
90
|
-
if Resque::Scheduler.dynamic
|
91
|
-
Resque.set_schedule(name, schedule)
|
92
|
-
end
|
93
|
-
Resque.schedule[name] = schedule
|
94
|
-
end
|
6
|
+
# TODO: all of this will be removed
|
95
7
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
def redis=(server)
|
104
|
-
case server
|
105
|
-
when String
|
106
|
-
if server =~ /redis\:\/\//
|
107
|
-
redis = Redis.connect(:url => server, :thread_safe => true)
|
108
|
-
else
|
109
|
-
server, namespace = server.split('/', 2)
|
110
|
-
host, port, db = server.split(':')
|
111
|
-
redis = Redis.new(:host => host, :port => port,
|
112
|
-
:thread_safe => true, :db => db)
|
113
|
-
end
|
114
|
-
namespace ||= default_namespace
|
8
|
+
autoload :Deprecated, 'resque_bus/compatibility/deprecated'
|
9
|
+
autoload :Subscriber, 'resque_bus/compatibility/subscriber'
|
10
|
+
autoload :TaskManager, 'resque_bus/compatibility/task_manager'
|
11
|
+
autoload :Driver, 'resque_bus/compatibility/driver'
|
12
|
+
autoload :Rider, 'resque_bus/compatibility/rider'
|
13
|
+
autoload :Publisher, 'resque_bus/compatibility/publisher'
|
14
|
+
autoload :Heartbeat, 'resque_bus/compatibility/heartbeat'
|
115
15
|
|
116
|
-
|
117
|
-
|
118
|
-
@redis = server
|
119
|
-
else
|
120
|
-
@redis = Redis::Namespace.new(default_namespace, :redis => server)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Returns the current Redis connection. If none has been created, will
|
125
|
-
# create a new one from the Reqsue one (with a different namespace)
|
126
|
-
def redis
|
127
|
-
return @redis if @redis
|
128
|
-
copy = Resque.redis.clone
|
129
|
-
copy.namespace = default_namespace
|
130
|
-
self.redis = copy
|
131
|
-
self.redis
|
132
|
-
end
|
133
|
-
|
134
|
-
def original_redis=(server)
|
135
|
-
@original_redis = server
|
136
|
-
end
|
137
|
-
def original_redis
|
138
|
-
@original_redis
|
139
|
-
end
|
140
|
-
|
141
|
-
def with_global_attributes(attributes)
|
142
|
-
original_timezone = false
|
143
|
-
original_locale = false
|
144
|
-
|
145
|
-
if attributes["bus_locale"] && defined?(I18n) && I18n.respond_to?(:locale=)
|
146
|
-
original_locale = I18n.locale if I18n.respond_to?(:locale)
|
147
|
-
I18n.locale = attributes["bus_locale"]
|
148
|
-
end
|
149
|
-
|
150
|
-
if attributes["bus_timezone"] && defined?(Time) && Time.respond_to?(:zone=)
|
151
|
-
original_timezone = Time.zone if Time.respond_to?(:zone)
|
152
|
-
Time.zone = attributes["bus_timezone"]
|
153
|
-
end
|
154
|
-
|
155
|
-
yield
|
156
|
-
ensure
|
157
|
-
I18n.locale = original_locale unless original_locale == false
|
158
|
-
Time.zone = original_timezone unless original_timezone == false
|
159
|
-
end
|
160
|
-
|
161
|
-
def before_publish=(proc)
|
162
|
-
@before_publish_callback = proc
|
163
|
-
end
|
164
|
-
|
165
|
-
def before_publish_callback(attributes)
|
166
|
-
if @before_publish_callback
|
167
|
-
@before_publish_callback.call(attributes)
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
|
172
|
-
def publish_metadata(event_type, attributes={})
|
173
|
-
# TODO: "bus_app_key" => application.app_key ?
|
174
|
-
bus_attr = {"bus_published_at" => Time.now.to_i, "bus_event_type" => event_type}
|
175
|
-
bus_attr["bus_id"] = "#{Time.now.to_i}-#{generate_uuid}"
|
176
|
-
bus_attr["bus_app_hostname"] = hostname
|
177
|
-
bus_attr["bus_locale"] = I18n.locale.to_s if defined?(I18n) && I18n.respond_to?(:locale) && I18n.locale
|
178
|
-
bus_attr["bus_timezone"] = Time.zone.name if defined?(Time) && Time.respond_to?(:zone) && Time.zone
|
179
|
-
out = bus_attr.merge(attributes || {})
|
180
|
-
ResqueBus.before_publish_callback(out)
|
181
|
-
out
|
182
|
-
end
|
183
|
-
|
184
|
-
def generate_uuid
|
185
|
-
require 'securerandom' unless defined?(SecureRandom)
|
186
|
-
return SecureRandom.uuid
|
187
|
-
|
188
|
-
rescue Exception => e
|
189
|
-
# secure random not there
|
190
|
-
# big random number a few times
|
191
|
-
n_bytes = [42].pack('i').size
|
192
|
-
n_bits = n_bytes * 8
|
193
|
-
max = 2 ** (n_bits - 2) - 1
|
194
|
-
return "#{rand(max)}-#{rand(max)}-#{rand(max)}"
|
195
|
-
end
|
196
|
-
|
197
|
-
def publish(event_type, attributes = {})
|
198
|
-
to_publish = publish_metadata(event_type, attributes)
|
199
|
-
ResqueBus.log_application("Event published: #{event_type} #{to_publish.inspect}")
|
200
|
-
if local_mode
|
201
|
-
ResqueBus::Local.perform(to_publish)
|
202
|
-
else
|
203
|
-
enqueue_to(incoming_queue, Driver, to_publish)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
def publish_at(timestamp_or_epoch, event_type, attributes = {})
|
208
|
-
to_publish = publish_metadata(event_type, attributes)
|
209
|
-
to_publish["bus_delayed_until"] ||= timestamp_or_epoch.to_i
|
210
|
-
to_publish.delete("bus_published_at") unless attributes["bus_published_at"] # will be put on when it actually does it
|
211
|
-
|
212
|
-
ResqueBus.log_application("Event published:#{event_type} #{to_publish.inspect} publish_at: #{timestamp_or_epoch.to_i}")
|
213
|
-
item = delayed_job_to_hash_with_queue(incoming_queue, Publisher, [event_type, to_publish])
|
214
|
-
delayed_push(timestamp_or_epoch, item)
|
215
|
-
end
|
216
|
-
|
217
|
-
def enqueue_to(queue, klass, *args)
|
218
|
-
push(queue, :class => klass.to_s, :args => args)
|
219
|
-
end
|
220
|
-
|
221
|
-
def logger
|
222
|
-
@logger
|
223
|
-
end
|
224
|
-
|
225
|
-
def logger=val
|
226
|
-
@logger = val
|
227
|
-
end
|
228
|
-
|
229
|
-
def log_application(message)
|
230
|
-
if logger
|
231
|
-
time = Time.now.strftime('%H:%M:%S %Y-%m-%d')
|
232
|
-
logger.info("** [#{time}] #$$: ResqueBus #{message}")
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def log_worker(message)
|
237
|
-
if ENV['LOGGING'] || ENV['VERBOSE'] || ENV['VVERBOSE']
|
238
|
-
time = Time.now.strftime('%H:%M:%S %Y-%m-%d')
|
239
|
-
puts "** [#{time}] #$$: #{message}"
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
protected
|
244
|
-
|
245
|
-
def reset
|
246
|
-
# used by tests
|
247
|
-
@redis = nil # clear instance of redis
|
248
|
-
@dispatcher = nil
|
249
|
-
@default_app_key = nil
|
250
|
-
@default_queue = nil
|
251
|
-
@before_publish_callback = nil
|
252
|
-
end
|
253
|
-
|
254
|
-
def incoming_queue
|
255
|
-
"resquebus_incoming"
|
256
|
-
end
|
257
|
-
|
258
|
-
def default_namespace
|
259
|
-
# It might play better on the same server, but overall life is more complicated
|
260
|
-
:resque
|
261
|
-
end
|
262
|
-
|
263
|
-
## From Resque, but using a (possibly) different instance of Redis
|
264
|
-
|
265
|
-
# Pushes a job onto a queue. Queue name should be a string and the
|
266
|
-
# item should be any JSON-able Ruby object.
|
267
|
-
#
|
268
|
-
# Resque works generally expect the `item` to be a hash with the following
|
269
|
-
# keys:
|
270
|
-
#
|
271
|
-
# class - The String name of the job to run.
|
272
|
-
# args - An Array of arguments to pass the job. Usually passed
|
273
|
-
# via `class.to_class.perform(*args)`.
|
274
|
-
#
|
275
|
-
# Example
|
276
|
-
#
|
277
|
-
# Resque.push('archive', :class => 'Archive', :args => [ 35, 'tar' ])
|
278
|
-
#
|
279
|
-
# Returns nothing
|
280
|
-
def push(queue, item)
|
281
|
-
watch_queue(queue)
|
282
|
-
redis.rpush "queue:#{queue}", Resque.encode(item)
|
283
|
-
end
|
284
|
-
|
285
|
-
# Used internally to keep track of which queues we've created.
|
286
|
-
# Don't call this directly.
|
287
|
-
def watch_queue(queue)
|
288
|
-
redis.sadd(:queues, queue.to_s)
|
289
|
-
end
|
290
|
-
|
291
|
-
### From Resque Scheduler
|
292
|
-
# Used internally to stuff the item into the schedule sorted list.
|
293
|
-
# +timestamp+ can be either in seconds or a datetime object
|
294
|
-
# Insertion if O(log(n)).
|
295
|
-
# Returns true if it's the first job to be scheduled at that time, else false
|
296
|
-
def delayed_push(timestamp, item)
|
297
|
-
# First add this item to the list for this timestamp
|
298
|
-
redis.rpush("delayed:#{timestamp.to_i}", Resque.encode(item))
|
16
|
+
extend ::ResqueBus::Deprecated
|
17
|
+
end
|
299
18
|
|
300
|
-
|
301
|
-
# the same since we'll be querying by timestamp, and we don't have
|
302
|
-
# anything else to store.
|
303
|
-
redis.zadd :delayed_queue_schedule, timestamp.to_i, timestamp.to_i
|
304
|
-
end
|
305
|
-
|
306
|
-
def delayed_job_to_hash_with_queue(queue, klass, args)
|
307
|
-
{:class => klass.to_s, :args => args, :queue => queue}
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
end
|
19
|
+
QueueBus.adapter = QueueBus::Adapters::Resque.new
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module QueueBus
|
2
|
+
module Adapters
|
3
|
+
class Resque < QueueBus::Adapters::Base
|
4
|
+
def enabled!
|
5
|
+
# know we are using it
|
6
|
+
require 'resque'
|
7
|
+
require 'resque/scheduler'
|
8
|
+
require 'resque-retry'
|
9
|
+
|
10
|
+
QueueBus::Worker.extend(::Resque::Plugins::ExponentialBackoff)
|
11
|
+
QueueBus::Worker.extend(::QueueBus::Adapters::Resque::RetryHandlers)
|
12
|
+
end
|
13
|
+
|
14
|
+
def redis(&block)
|
15
|
+
block.call(::Resque.redis)
|
16
|
+
end
|
17
|
+
|
18
|
+
def enqueue(queue_name, klass, json)
|
19
|
+
::Resque.enqueue_to(queue_name, klass, json)
|
20
|
+
end
|
21
|
+
|
22
|
+
def enqueue_at(epoch_seconds, queue_name, klass, json)
|
23
|
+
::Resque.enqueue_at_with_queue(queue_name, epoch_seconds, klass, json)
|
24
|
+
end
|
25
|
+
|
26
|
+
def setup_heartbeat!(queue_name)
|
27
|
+
# turn on the heartbeat
|
28
|
+
# should be down after loading scheduler yml if you do that
|
29
|
+
# otherwise, anytime
|
30
|
+
name = 'resquebus_heartbeat'
|
31
|
+
schedule = { 'class' => '::QueueBus::Heartbeat',
|
32
|
+
'cron' => '* * * * *', # every minute
|
33
|
+
'queue' => queue_name,
|
34
|
+
'description' => 'I publish a heartbeat_minutes event every minute'
|
35
|
+
}
|
36
|
+
if ::Resque::Scheduler.dynamic
|
37
|
+
::Resque.set_schedule(name, schedule)
|
38
|
+
end
|
39
|
+
::Resque.schedule[name] = schedule
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
module RetryHandlers
|
45
|
+
# @failure_hooks_already_ran on https://github.com/defunkt/resque/tree/1-x-stable
|
46
|
+
# to prevent running twice
|
47
|
+
def queue
|
48
|
+
@my_queue
|
49
|
+
end
|
50
|
+
|
51
|
+
def on_failure_aaa(exception, *args)
|
52
|
+
# note: sorted alphabetically
|
53
|
+
# queue needs to be set for rety to work (know what queue in Requeue.class_to_queue)
|
54
|
+
hash = ::QueueBus::Util.decode(args[0])
|
55
|
+
@my_queue = hash["bus_rider_queue"]
|
56
|
+
end
|
57
|
+
|
58
|
+
def on_failure_zzz(exception, *args)
|
59
|
+
# note: sorted alphabetically
|
60
|
+
@my_queue = nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module ResqueBus
|
2
|
+
module Deprecated
|
3
|
+
def show_deprecations=val
|
4
|
+
@show_deprecations = val
|
5
|
+
end
|
6
|
+
|
7
|
+
def show_deprecations?
|
8
|
+
return @show_deprecations if defined?(@show_deprecations)
|
9
|
+
return true if !ENV['QUEUES'] && !ENV['QUEUE'] # not in background, probably test
|
10
|
+
return true if ENV['VVERBOSE'] || ENV['LOGGING'] || ENV['VERBOSE']
|
11
|
+
false
|
12
|
+
end
|
13
|
+
|
14
|
+
def note_deprecation(message)
|
15
|
+
@noted_deprecations ||= {}
|
16
|
+
if @noted_deprecations[message]
|
17
|
+
@noted_deprecations[message] += 1
|
18
|
+
else
|
19
|
+
warn(message) if show_deprecations?
|
20
|
+
@noted_deprecations[message] = 1
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def redis
|
25
|
+
ResqueBus.note_deprecation "[DEPRECATION] ResqueBus direct usage is deprecated. Use `QueueBus.redis` instead. Note that it also requires block usage now."
|
26
|
+
::Resque.redis
|
27
|
+
end
|
28
|
+
|
29
|
+
def redis=val
|
30
|
+
ResqueBus.note_deprecation "[DEPRECATION] ResqueBus can no longer set redis directly. It will use Resque's instance of redis."
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_missing(method_name, *args, &block)
|
34
|
+
ResqueBus.note_deprecation "[DEPRECATION] ResqueBus direct usage is deprecated. Use `QueueBus.#{method_name}` instead."
|
35
|
+
::QueueBus.send(method_name, *args, &block)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|