sidekiq-bus 0.5.2

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