sidekiq-bus 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8c073296475ba07b08f7f92b019d091b38b4038e
4
+ data.tar.gz: cb95f95a5ec099cc471dcd8695cbf2ec478a9b6a
5
+ SHA512:
6
+ metadata.gz: 846ac47ae7600da4249d89e55e81e9e16f4de85e768697017f343b5f3952683232b29975055feddf2dcea83dc15a48e25c1e2f6d4ceee452b94f9bf32d4d590f
7
+ data.tar.gz: a5a9156736395bf84e9acbebbd533ce27a71436d74bc10923c893e06cf07c734e9756ddc2e42bfd64822823e322491a57b5cdb4facacbb71a7c09b17135f1715
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
data/.rbenv-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.5
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ # gem "queue-bus", path: "../queue-bus"
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Brian Leonard
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.mdown ADDED
@@ -0,0 +1,264 @@
1
+ ## Resque Bus
2
+
3
+ This gem uses Redis and Resque to allow simple asynchronous communication between apps.
4
+
5
+ ### Install
6
+
7
+ To install, include the 'resque-bus' gem and add the following to your Rakefile:
8
+
9
+ ```ruby
10
+ require "sidekiq_bus/tasks"
11
+ ```
12
+
13
+ ### Example
14
+
15
+ Application A can publish an event
16
+
17
+ ```ruby
18
+ # config
19
+ Resque.redis = "192.168.1.1:6379"
20
+
21
+ # business logic
22
+ ResqueBus.publish("user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
23
+
24
+ # or do it later
25
+ ResqueBus.publish_at(1.hour.from_now, "user_created", "id" => 42, "first_name" => "John", "last_name" => "Smith")
26
+ ```
27
+
28
+ Application B is subscribed to events
29
+
30
+ ```ruby
31
+ # config
32
+ Resque.redis = "192.168.1.1:6379"
33
+
34
+ # initializer
35
+ ResqueBus.dispatch("app_b") do
36
+ # processes event on app_b_default queue
37
+ # subscribe is short-hand to subscribe to your 'default' queue and this block with process events with the name "user_created"
38
+ subscribe "user_created" do |attributes|
39
+ NameCount.find_or_create_by_name(attributes["last_name"]).increment!
40
+ end
41
+
42
+ # processes event on app_b_critical queue
43
+ # critical is short-hand to subscribe to your 'critical' queue and this block with process events with the name "user_paid"
44
+ critical "user_paid" do |attributes|
45
+ CreditCard.charge!(attributes)
46
+ end
47
+
48
+ # you can pass any queue name you would like to process from as well IE: `banana "peeled" do |attributes|`
49
+
50
+ # and regexes work as well. note that with the above configuration along with this regex,
51
+ # the following as well as the corresponding block above would both be executed
52
+ subscribe /^user_/ do |attributes|
53
+ Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
54
+ end
55
+
56
+ # the above all filter on just the event_type, but you can filter on anything
57
+ # this would be _any_ event that has a user_id and the page value of homepage regardless of bus_event_type
58
+ subscribe "my_key", { "user_id" => :present, "page" => "homepage"} do
59
+ Mixpanel.homepage_action!(attributes["action"])
60
+ end
61
+ end
62
+ ```
63
+
64
+ Applications can also subscribe within classes using the provided `Subscriber` module.
65
+
66
+ ```ruby
67
+ class SimpleSubscriber
68
+ include ResqueBus::Subscriber
69
+ subscribe :my_method
70
+
71
+ def my_method(attributes)
72
+ # heavy lifting
73
+ end
74
+ end
75
+ ```
76
+
77
+ The following is equivalent to the original initializer and shows more options:
78
+
79
+ ```ruby
80
+ class OtherSubscriber
81
+ include ResqueBus::Subscriber
82
+ application :app_b
83
+
84
+ subscribe :user_created
85
+ subscribe_queue :app_b_critical, :user_paid
86
+ subscribe_queue :app_b_default, :user_action, :bus_event_type => /^user_/
87
+ subscribe :homepage_method, :user_id => :present, :page => "homepage"
88
+
89
+ def user_created(attributes)
90
+ NameCount.find_or_create_by_name(attributes["last_name"]).increment!
91
+ end
92
+
93
+ def user_paid(attributes)
94
+ CreditCard.charge!(attributes)
95
+ end
96
+
97
+ def user_action(attributes)
98
+ Metrics.record_user_action(attributes["bus_event_type"], attributes["id"])
99
+ end
100
+
101
+ def homepage_method
102
+ Mixpanel.homepage_action!(attributes["action"])
103
+ end
104
+ end
105
+ ```
106
+
107
+ 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.
108
+
109
+ ### Commands
110
+
111
+ Each app needs to tell Redis about its subscriptions:
112
+
113
+ $ rake resquebus:subscribe
114
+
115
+ The subscription block is run inside a Resque worker which needs to be started for each app.
116
+
117
+ $ rake resquebus:setup resque:work
118
+
119
+ The incoming queue also needs to be processed on a dedicated or all the app servers.
120
+
121
+ $ rake resquebus:driver resque:work
122
+
123
+ If you want retry to work for subscribing apps, you should run resque-scheduler
124
+
125
+ $ rake resque:scheduler
126
+
127
+ ### Adapters
128
+
129
+ ResqueBus now supports multiple adapters! By default ResqueBus uses Resque but you can now configure your application to use Sidekiq to drive and subscribe the bus.
130
+
131
+ First be sure to configure ResqueBus to use Sidekiq early in your applications' initialization cycle:
132
+ ```
133
+ ResqueBus.adapter = 'Sidekiq'
134
+ ```
135
+ You will be responsible for setting up the queues for your Sidekiq clients however you can get the appropriate queue names with the following tasks:
136
+ For driving applications:
137
+ ```
138
+ $ rake resquebus:driver:sidekiq
139
+ ```
140
+ For subscribing applications:
141
+ ```
142
+ $ rake resquebus:setup:sidekiq
143
+ ```
144
+ These tasks will provide the queue_names and some minimal suggestions for starting the client.
145
+
146
+ Your subscribing applications will still need to also use the appropriate rake task:
147
+ ```
148
+ $ rake resquebus:subscribe:sidekiq
149
+ ```
150
+
151
+ At the moment you are expected to include the Sidekiq gem in your own applications.
152
+
153
+ And yes we are planning on renaming and restructuring the project! Please contact the maintainer if you would like to add a different adapter.
154
+
155
+ ### Heartbeat
156
+
157
+ We've found it useful to have the bus act like `cron`, triggering timed jobs throughout the system. Resque Bus calls this a heartbeat.
158
+ It uses resque-scheduler to trigger the events. You can enable it in your Rakefile.
159
+
160
+ ```ruby
161
+ # resque.rake
162
+ namespace :resque do
163
+ task :setup => [:environment] do
164
+ ResqueBus.heartbeat!
165
+ end
166
+ end
167
+ ```
168
+
169
+ Or add it to your `schedule.yml` directly
170
+
171
+ ```yaml
172
+ resquebus_heartbeat:
173
+ cron: "* * * * *"
174
+ class: "::ResqueBus::Heartbeat"
175
+ queue: resquebus_incoming
176
+ description: "I publish a heartbeat_minutes event every minute"
177
+ ```
178
+
179
+ It is the equivalent of doing this every minute
180
+
181
+ ```ruby
182
+ seconds = minutes * (60)
183
+ hours = minutes / (60)
184
+ days = minutes / (60*24)
185
+
186
+ now = Time.at(seconds)
187
+
188
+ attributes = {}
189
+
190
+ now = Time.now
191
+ seconds = now.to_i
192
+ ResqueBus.publish("hearbeat_minutes", {
193
+ "epoch_seconds" => seconds,
194
+ "epoch_minutes" => seconds / 1.minute,
195
+ "epoch_hours" => seconds / 1.hour,
196
+ "epoch_days" => seconds / 1.day,
197
+ "minute" => now.min
198
+ "hour" => now.hour
199
+ "day" => now.day
200
+ "month" => now.month
201
+ "year" => now.year
202
+ "yday" => now.yday
203
+ "wday" => now.wday
204
+ })
205
+ ```
206
+
207
+ This allows you do something like this:
208
+
209
+ ```ruby
210
+ ResqueBus.dispatch("app_c") do
211
+ # runs at 10:20, 11:20, etc
212
+ subscribe "once_an_hour", 'bus_event_type' => 'heartbeat_minutes', 'minute' => 20 do |attributes|
213
+ Sitemap.generate!
214
+ end
215
+
216
+ # runs every five minutes
217
+ subscribe "every_five_minutes", 'bus_event_type' => 'heartbeat_minutes' do |attributes|
218
+ next unless attributes["epoch_minutes"] % 5 == 0
219
+ HealthCheck.run!
220
+ end
221
+
222
+ # runs at 8am on the first of every month
223
+ subscribe "new_month_morning", 'bus_event_type' => 'heartbeat_minutes', 'day' => 1, hour' => 8, 'minute' => 0, do |attributes|
224
+ next unless attributes["epoch_minutes"] % 5 == 0
225
+ Token.old.expire!
226
+ end
227
+ end
228
+ ```
229
+
230
+ ### Local Mode
231
+
232
+ For development, a local mode is provided and is specified in the configuration.
233
+
234
+ ```ruby
235
+ # config
236
+ ResqueBus.local_mode = :standalone
237
+ or
238
+ ResqueBus.local_mode = :inline
239
+ ```
240
+
241
+ Standalone mode does not require a separate resquebus:driver task to be running to process the
242
+ incoming queue. Simply publishing to the bus will distribute the incoming events
243
+ to the appropriate application specific queue. A separate resquebus:work task does
244
+ still need to be run to process these events
245
+
246
+ Inline mode skips queue processing entirely and directly dispatches the
247
+ event to the appropriate code block.
248
+
249
+ You can also say `ResqueBus.local_mode = :suppress` to turn off publishing altogether.
250
+ This can be helpful inside some sort of migration, for example.
251
+
252
+ ### TODO
253
+
254
+ * Sidekiq adapter
255
+ * Refactor rake tasks for resque/sidekiq
256
+ * Refactor to a storage adapter for Redis, so we can store subscription info in MySQL or something else
257
+ * Replace local modes with adapters
258
+ * There are a few spots in the code with TODO notes
259
+ * Make this not freak out in development without Redis or when Redis is down
260
+ * We might not actually need to publish in tests
261
+ * Add some rspec helpers for the apps to use: should_ post an event_publish or something along those lines
262
+ * Allow calling resquebus:setup and resquebus:driver together (append to ENV['QUEUES'], don't replace it)
263
+
264
+ Copyright (c) 2011 Brian Leonard, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift 'lib'
2
+ require 'sidekiq_bus/tasks'
@@ -0,0 +1,9 @@
1
+ require "queue-bus"
2
+ require "sidekiq_bus/adapter"
3
+ require "sidekiq_bus/version"
4
+
5
+ module ResqueBus
6
+
7
+ end
8
+
9
+ QueueBus.adapter = QueueBus::Adapters::Sidekiq.new
@@ -0,0 +1,28 @@
1
+ module QueueBus
2
+ module Adapters
3
+ class Sidekiq < QueueBus::Adapters::Base
4
+ def enabled!
5
+ # know we are using it
6
+ require 'sidekiq'
7
+ ::QueueBus::Worker.include ::Sidekiq::Worker
8
+ end
9
+
10
+ def redis(&block)
11
+ ::Sidekiq.redis(&block)
12
+ end
13
+
14
+ def enqueue(queue_name, klass, hash)
15
+ ::Sidekiq::Client.push('queue' => queue_name, 'class' => klass, 'args' => [hash])
16
+ end
17
+
18
+ def enqueue_at(epoch_seconds, queue_name, klass, hash)
19
+ ::Sidekiq::Client.push('queue' => queue_name, 'class' => klass, 'args' => [hash], 'at' => epoch_seconds)
20
+ end
21
+
22
+ def setup_heartbeat!(queue_name)
23
+ # TODO: not sure how to do this or what is means to set this up in Sidekiq
24
+ raise NotImplementedError
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,93 @@
1
+ # require 'sidekiq_bus/tasks'
2
+ # will give you these tasks
3
+
4
+
5
+ require "resque/tasks"
6
+ namespace :sidekiqbus do
7
+
8
+ desc "Setup will configure a resque task to run before resque:work"
9
+ task :setup => [ :preload ] do
10
+
11
+ if ENV['QUEUES'].nil?
12
+ manager = ::QueueBus::TaskManager.new(true)
13
+ queues = manager.queue_names
14
+ ENV['QUEUES'] = queues.join(",")
15
+ else
16
+ queues = ENV['QUEUES'].split(",")
17
+ end
18
+
19
+ if queues.size == 1
20
+ puts " >> Working Queue : #{queues.first}"
21
+ else
22
+ puts " >> Working Queues: #{queues.join(", ")}"
23
+ end
24
+ end
25
+
26
+ desc "Subscribes this application to QueueBus events"
27
+ task :subscribe => [ :preload ] do
28
+ manager = ::QueueBus::TaskManager.new(true)
29
+ count = manager.subscribe!
30
+ raise "No subscriptions created" if count == 0
31
+ end
32
+
33
+ desc "Unsubscribes this application from QueueBus events"
34
+ task :unsubscribe => [ :preload ] do
35
+ require 'resque-bus'
36
+ manager = ::QueueBus::TaskManager.new(true)
37
+ count = manager.unsubscribe!
38
+ puts "No subscriptions unsubscribed" if count == 0
39
+ end
40
+
41
+ desc "Sets the queue to work the driver Use: `rake sidekiqbus:driver resque:work`"
42
+ task :driver => [ :preload ] do
43
+ ENV['QUEUES'] = ::QueueBus.incoming_queue
44
+ end
45
+
46
+ # Preload app files if this is Rails
47
+ task :preload do
48
+ require "sidekiq"
49
+ end
50
+
51
+
52
+ # examples to test out the system
53
+ namespace :example do
54
+ desc "Publishes events to example applications"
55
+ task :publish => [ "sidekiqbus:preload", "sidekiqbus:setup" ] do
56
+ which = ["one", "two", "three", "other"][rand(4)]
57
+ QueueBus.publish("event_#{which}", { "rand" => rand(99999)})
58
+ QueueBus.publish("event_all", { "rand" => rand(99999)})
59
+ QueueBus.publish("none_subscribed", { "rand" => rand(99999)})
60
+ puts "published event_#{which}, event_all, none_subscribed"
61
+ end
62
+
63
+ desc "Sets up an example config"
64
+ task :register => [ "sidekiqbus:preload"] do
65
+ QueueBus.dispatch("example") do
66
+ subscribe "event_one" do
67
+ puts "event1 happened"
68
+ end
69
+
70
+ subscribe "event_two" do
71
+ puts "event2 happened"
72
+ end
73
+
74
+ high "event_three" do
75
+ puts "event3 happened (high)"
76
+ end
77
+
78
+ low "event_.*" do |attributes|
79
+ puts "LOG ALL: #{attributes.inspect}"
80
+ end
81
+ end
82
+ end
83
+
84
+ desc "Subscribes this application to QueueBus example events"
85
+ task :subscribe => [ :register, "sidekiqbus:subscribe" ]
86
+
87
+ desc "Start a QueueBus example worker"
88
+ task :work => [ :register, "sidekiqbus:setup", "resque:work" ]
89
+
90
+ desc "Start a QueueBus example worker"
91
+ task :driver => [ :register, "sidekiqbus:driver", "resque:work" ]
92
+ end
93
+ end