queue-bus 0.8.0 → 0.11.0
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 +4 -4
- data/.circleci/config.yml +21 -0
- data/CHANGELOG.md +31 -0
- data/Gemfile +4 -2
- data/README.mdown +15 -3
- data/Rakefile +2 -0
- data/lib/queue-bus.rb +16 -12
- data/lib/queue_bus/adapters/base.rb +4 -2
- data/lib/queue_bus/adapters/data.rb +12 -11
- data/lib/queue_bus/application.rb +24 -16
- data/lib/queue_bus/config.rb +23 -2
- data/lib/queue_bus/dispatch.rb +14 -12
- data/lib/queue_bus/dispatchers.rb +12 -5
- data/lib/queue_bus/driver.rb +15 -10
- data/lib/queue_bus/heartbeat.rb +32 -30
- data/lib/queue_bus/local.rb +9 -9
- data/lib/queue_bus/matcher.rb +36 -27
- data/lib/queue_bus/publisher.rb +7 -5
- data/lib/queue_bus/publishing.rb +32 -24
- data/lib/queue_bus/rider.rb +26 -22
- data/lib/queue_bus/subscriber.rb +20 -14
- data/lib/queue_bus/subscription.rb +25 -15
- data/lib/queue_bus/subscription_list.rb +30 -12
- data/lib/queue_bus/task_manager.rb +25 -16
- data/lib/queue_bus/tasks.rb +35 -11
- data/lib/queue_bus/util.rb +11 -8
- data/lib/queue_bus/version.rb +3 -1
- data/lib/queue_bus/worker.rb +3 -2
- data/queue-bus.gemspec +19 -18
- data/spec/adapter/publish_at_spec.rb +28 -25
- data/spec/adapter/support.rb +7 -1
- data/spec/adapter_spec.rb +4 -2
- data/spec/application_spec.rb +138 -96
- data/spec/config_spec.rb +36 -0
- data/spec/dispatch_spec.rb +48 -51
- data/spec/driver_spec.rb +60 -58
- data/spec/heartbeat_spec.rb +26 -24
- data/spec/integration_spec.rb +41 -40
- data/spec/matcher_spec.rb +104 -102
- data/spec/publish_spec.rb +68 -46
- data/spec/publisher_spec.rb +3 -1
- data/spec/rider_spec.rb +16 -14
- data/spec/spec_helper.rb +2 -2
- data/spec/subscriber_spec.rb +227 -227
- data/spec/subscription_list_spec.rb +57 -31
- data/spec/subscription_spec.rb +37 -36
- data/spec/worker_spec.rb +17 -15
- metadata +12 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 139511a2de01b9ffb7e58b63ecf62cbc01ae94fe38896282de2ae0dbb13178de
|
4
|
+
data.tar.gz: 0e481639105b4dceff9991ee448f378923fdb3ae5820294f954602e96a177243
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ce3ebbeee0e6be7513af1ed093f5612877a6293a78bad68da502817c10f205ddf69bbc3071ef497f48baef2e66f2823311ef80e579805dbeedccec9b0794bef
|
7
|
+
data.tar.gz: 8b93e84ff4b5d6801d6b490560af46532c729eb7ff2b32b0d88e565bd620388d029f1a010027b9032e56731aef936945922bf7afc642453aeb9db9cbeacd0bcd
|
@@ -0,0 +1,21 @@
|
|
1
|
+
version: 2.1
|
2
|
+
orbs:
|
3
|
+
ruby: circleci/ruby@0.1.2
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
docker:
|
8
|
+
- image: circleci/ruby:2.6.3-stretch-node
|
9
|
+
- image: circleci/redis:4.0.12-alpine
|
10
|
+
executor: ruby/default
|
11
|
+
steps:
|
12
|
+
- checkout
|
13
|
+
- run:
|
14
|
+
name: Which bundler?
|
15
|
+
command: bundle -v
|
16
|
+
- ruby/bundle-install
|
17
|
+
- run:
|
18
|
+
name: Run tests
|
19
|
+
environment:
|
20
|
+
REDIS_URL: redis://127.0.0.1:6379
|
21
|
+
command: bundle exec rspec spec
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [0.11.0]
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Adds `QueueBus.in_context` method. Useful when working with a multithreaded environment to add a description for all events published within this scope.
|
14
|
+
|
15
|
+
## [0.10.0]
|
16
|
+
|
17
|
+
### Added
|
18
|
+
- Ability to unsubscribe from specific queues in an application (`Application#unsubscribe_queue`).
|
19
|
+
- `rake queuebus:unsubscribe` can now take two parameters to unsubscribe from specific queues, e.g. `rake queuebus:unsubscribe[my_app_key, my_queue_name]`.
|
20
|
+
|
21
|
+
## [0.9.1]
|
22
|
+
|
23
|
+
### Added
|
24
|
+
- Documented some of the major classes and modules
|
25
|
+
|
26
|
+
### Fixed
|
27
|
+
- Ran the rubocop autocorrect on the entire codebase.
|
28
|
+
- Fixed issue that prevented heartbeat events from firing under certain conditions
|
29
|
+
|
30
|
+
## [0.9.0]
|
31
|
+
|
32
|
+
### Added
|
33
|
+
- Adds rake tasks to list scheduled jobs as csv
|
34
|
+
|
35
|
+
## [0.8.1]
|
36
|
+
|
37
|
+
### Fixed
|
38
|
+
- `with_local_mode` breaks subsequent calls to `local_mode` on versions less than 2.6.
|
39
|
+
|
9
40
|
## [0.8.0]
|
10
41
|
|
11
42
|
### Added
|
data/Gemfile
CHANGED
data/README.mdown
CHANGED
@@ -141,13 +141,16 @@ event to the appropriate code block.
|
|
141
141
|
You can also say `QueueBus.local_mode = :suppress` to turn off publishing altogether.
|
142
142
|
This can be helpful inside some sort of migration, for example.
|
143
143
|
|
144
|
-
#### Thread Safe
|
144
|
+
#### Thread Safe Options
|
145
145
|
|
146
146
|
**!! This is important if you are using workers that utilize multiple threads, such as Sidekiq !!**
|
147
147
|
|
148
148
|
The above setting is global to the ruby process and modifying it will impact all threads that are
|
149
149
|
currently using QueueBus. If you want to isolate a thread or block of code from QueueBus, you can
|
150
|
-
use the
|
150
|
+
use the methods `with_local_mode` or `in_context`:
|
151
|
+
|
152
|
+
|
153
|
+
With local mode
|
151
154
|
|
152
155
|
```ruby
|
153
156
|
QueueBus.with_local_mode(:suppress) do
|
@@ -155,7 +158,16 @@ QueueBus.with_local_mode(:suppress) do
|
|
155
158
|
end
|
156
159
|
```
|
157
160
|
|
158
|
-
|
161
|
+
In context
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
QueueBus.in_context('some_context') do
|
165
|
+
# Context attribute will be set for all events published within this scope.
|
166
|
+
end
|
167
|
+
```
|
168
|
+
|
169
|
+
|
170
|
+
The previous values will be restored after the block exits.
|
159
171
|
|
160
172
|
### TODO
|
161
173
|
|
data/Rakefile
CHANGED
data/lib/queue-bus.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
-
|
2
|
-
require "forwardable"
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
3
|
+
require 'queue_bus/version'
|
4
|
+
require 'forwardable'
|
5
5
|
|
6
|
+
# The main QueueBus module. Most operations you will need to execute should be executed
|
7
|
+
# on this top level domain.
|
8
|
+
module QueueBus
|
6
9
|
autoload :Application, 'queue_bus/application'
|
7
10
|
autoload :Config, 'queue_bus/config'
|
8
11
|
autoload :Dispatch, 'queue_bus/dispatch'
|
@@ -22,25 +25,26 @@ module QueueBus
|
|
22
25
|
autoload :Util, 'queue_bus/util'
|
23
26
|
autoload :Worker, 'queue_bus/worker'
|
24
27
|
|
28
|
+
# A module for all adapters, current and future.
|
25
29
|
module Adapters
|
26
30
|
autoload :Base, 'queue_bus/adapters/base'
|
27
31
|
autoload :Data, 'queue_bus/adapters/data'
|
28
32
|
end
|
29
33
|
|
30
34
|
class << self
|
31
|
-
|
32
35
|
include Publishing
|
33
36
|
extend Forwardable
|
34
37
|
|
35
38
|
def_delegators :config, :default_app_key=, :default_app_key,
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
:default_queue=, :default_queue,
|
40
|
+
:local_mode=, :local_mode, :with_local_mode,
|
41
|
+
:before_publish=, :before_publish_callback,
|
42
|
+
:logger=, :logger, :log_application, :log_worker,
|
43
|
+
:hostname=, :hostname,
|
44
|
+
:adapter=, :adapter, :has_adapter?,
|
45
|
+
:incoming_queue=, :incoming_queue,
|
46
|
+
:redis, :worker_middleware_stack,
|
47
|
+
:context=, :context, :in_context
|
44
48
|
|
45
49
|
def_delegators :_dispatchers, :dispatch, :dispatchers, :dispatcher_by_key, :dispatcher_execute
|
46
50
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module QueueBus
|
2
4
|
module Adapters
|
3
5
|
class Base
|
@@ -19,12 +21,12 @@ module QueueBus
|
|
19
21
|
raise NotImplementedError
|
20
22
|
end
|
21
23
|
|
22
|
-
def enqueue(
|
24
|
+
def enqueue(_queue_name, _klass, _json)
|
23
25
|
# enqueue the given class (Driver/Rider) in your queue
|
24
26
|
raise NotImplementedError
|
25
27
|
end
|
26
28
|
|
27
|
-
def enqueue_at(
|
29
|
+
def enqueue_at(_epoch_seconds, _queue_name, _klass, _json)
|
28
30
|
# enqueue the given class (Publisher) in your queue to run at given time
|
29
31
|
raise NotImplementedError
|
30
32
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# a base adapter just for publishing and redis connection
|
2
4
|
module QueueBus
|
3
5
|
module Adapters
|
@@ -6,17 +8,16 @@ module QueueBus
|
|
6
8
|
# nothing to do
|
7
9
|
end
|
8
10
|
|
9
|
-
|
10
|
-
@redis = client
|
11
|
-
end
|
11
|
+
attr_writer :redis
|
12
12
|
|
13
13
|
def redis(&block)
|
14
|
-
raise
|
14
|
+
raise 'no redis instance set' unless @redis
|
15
|
+
|
15
16
|
block.call(@redis)
|
16
17
|
end
|
17
18
|
|
18
19
|
def enqueue(queue_name, klass, json)
|
19
|
-
push(queue_name, :
|
20
|
+
push(queue_name, class: klass.to_s, args: [json])
|
20
21
|
end
|
21
22
|
|
22
23
|
def enqueue_at(epoch_seconds, queue_name, klass, json)
|
@@ -24,7 +25,7 @@ module QueueBus
|
|
24
25
|
delayed_push(epoch_seconds, item)
|
25
26
|
end
|
26
27
|
|
27
|
-
def setup_heartbeat!(
|
28
|
+
def setup_heartbeat!(_queue_name)
|
28
29
|
raise NotImplementedError
|
29
30
|
end
|
30
31
|
|
@@ -32,13 +33,13 @@ module QueueBus
|
|
32
33
|
|
33
34
|
def push(queue, item)
|
34
35
|
watch_queue(queue)
|
35
|
-
|
36
|
+
redis { |redis| redis.rpush "queue:#{queue}", ::QueueBus::Util.encode(item) }
|
36
37
|
end
|
37
38
|
|
38
39
|
# Used internally to keep track of which queues we've created.
|
39
40
|
# Don't call this directly.
|
40
41
|
def watch_queue(queue)
|
41
|
-
|
42
|
+
redis { |redis| redis.sadd(:queues, queue.to_s) }
|
42
43
|
end
|
43
44
|
|
44
45
|
# Used internally to stuff the item into the schedule sorted list.
|
@@ -46,7 +47,7 @@ module QueueBus
|
|
46
47
|
# Insertion if O(log(n)).
|
47
48
|
# Returns true if it's the first job to be scheduled at that time, else false
|
48
49
|
def delayed_push(timestamp, item)
|
49
|
-
|
50
|
+
redis do |redis|
|
50
51
|
# First add this item to the list for this timestamp
|
51
52
|
redis.rpush("delayed:#{timestamp.to_i}", ::QueueBus::Util.encode(item))
|
52
53
|
|
@@ -56,9 +57,9 @@ module QueueBus
|
|
56
57
|
redis.zadd :delayed_queue_schedule, timestamp.to_i, timestamp.to_i
|
57
58
|
end
|
58
59
|
end
|
59
|
-
|
60
|
+
|
60
61
|
def delayed_job_to_hash_with_queue(queue, klass, args)
|
61
|
-
{:
|
62
|
+
{ class: klass.to_s, args: args, queue: queue }
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
@@ -1,36 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module QueueBus
|
4
|
+
# An Application is the top level unifier for a number of subscriptions. It allows for
|
5
|
+
# the toggling of an entire applications subscriptions.
|
2
6
|
class Application
|
3
|
-
|
4
7
|
class << self
|
5
|
-
|
6
8
|
def all
|
7
9
|
# note the names arent the same as we started with
|
8
|
-
::QueueBus.redis { |redis| redis.smembers(app_list_key).collect{ |val| new(val) } }
|
10
|
+
::QueueBus.redis { |redis| redis.smembers(app_list_key).collect { |val| new(val) } }
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
12
14
|
attr_reader :app_key, :redis_key
|
13
15
|
|
14
|
-
|
15
16
|
def initialize(app_key)
|
16
17
|
@app_key = self.class.normalize(app_key)
|
17
18
|
@redis_key = "#{self.class.app_single_key}:#{@app_key}"
|
18
19
|
# raise error if only other chars
|
19
|
-
raise
|
20
|
+
raise 'Invalid application name' if @app_key.gsub('_', '').empty?
|
20
21
|
end
|
21
22
|
|
22
23
|
def subscribe(subscription_list, log = false)
|
23
24
|
@subscriptions = nil
|
24
25
|
|
25
|
-
if subscription_list
|
26
|
+
if subscription_list.nil? || subscription_list.empty?
|
26
27
|
unsubscribe
|
27
28
|
return true
|
28
29
|
end
|
29
30
|
|
30
|
-
temp_key = "temp_#{redis_key}:#{rand(
|
31
|
+
temp_key = "temp_#{redis_key}:#{rand(999_999_999)}"
|
31
32
|
|
32
33
|
::QueueBus.redis do |redis|
|
33
34
|
redis_hash = subscription_list.to_redis
|
35
|
+
|
34
36
|
redis_hash.each do |key, hash|
|
35
37
|
redis.hset(temp_key, key, QueueBus::Util.encode(hash))
|
36
38
|
end
|
@@ -39,16 +41,23 @@ module QueueBus
|
|
39
41
|
redis.rename(temp_key, redis_key)
|
40
42
|
redis.sadd(self.class.app_list_key, app_key)
|
41
43
|
|
42
|
-
if log
|
43
|
-
redis.hgetall(redis_key).inspect
|
44
|
-
end
|
44
|
+
redis.hgetall(redis_key).inspect if log
|
45
45
|
end
|
46
46
|
|
47
47
|
true
|
48
48
|
end
|
49
49
|
|
50
|
+
def unsubscribe_queue(queue)
|
51
|
+
# Filters out all subscriptions that match the supplied queue name.
|
52
|
+
::QueueBus.redis do |redis|
|
53
|
+
read_redis_hash.each do |key, hash_details|
|
54
|
+
redis.hdel(redis_key, key) if queue == hash_details["queue_name"]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
50
59
|
def unsubscribe
|
51
|
-
#
|
60
|
+
# Remove everything.
|
52
61
|
::QueueBus.redis do |redis|
|
53
62
|
redis.srem(self.class.app_list_key, app_key)
|
54
63
|
redis.del(redis_key)
|
@@ -68,7 +77,7 @@ module QueueBus
|
|
68
77
|
def subscription_matches(attributes)
|
69
78
|
out = subscriptions.matches(attributes)
|
70
79
|
out.each do |sub|
|
71
|
-
sub.app_key =
|
80
|
+
sub.app_key = app_key
|
72
81
|
end
|
73
82
|
out
|
74
83
|
end
|
@@ -84,15 +93,15 @@ module QueueBus
|
|
84
93
|
protected
|
85
94
|
|
86
95
|
def self.normalize(val)
|
87
|
-
val.to_s.gsub(/\W/,
|
96
|
+
val.to_s.gsub(/\W/, '_').downcase
|
88
97
|
end
|
89
98
|
|
90
99
|
def self.app_list_key
|
91
|
-
|
100
|
+
'bus_apps'
|
92
101
|
end
|
93
102
|
|
94
103
|
def self.app_single_key
|
95
|
-
|
104
|
+
'bus_app'
|
96
105
|
end
|
97
106
|
|
98
107
|
def event_queues
|
@@ -116,6 +125,5 @@ module QueueBus
|
|
116
125
|
end
|
117
126
|
out
|
118
127
|
end
|
119
|
-
|
120
128
|
end
|
121
129
|
end
|
data/lib/queue_bus/config.rb
CHANGED
@@ -9,7 +9,7 @@ module QueueBus
|
|
9
9
|
attr_accessor :default_queue, :hostname, :incoming_queue, :logger
|
10
10
|
|
11
11
|
attr_reader :worker_middleware_stack
|
12
|
-
attr_writer :local_mode
|
12
|
+
attr_writer :local_mode, :context
|
13
13
|
|
14
14
|
def initialize
|
15
15
|
@worker_middleware_stack = QueueBus::Middleware::Stack.new
|
@@ -24,16 +24,26 @@ module QueueBus
|
|
24
24
|
Wrap = Struct.new(:value)
|
25
25
|
|
26
26
|
LOCAL_MODE_VAR = :queue_bus_local_mode
|
27
|
+
CONTEXT_VAR = :queue_bus_context
|
27
28
|
|
28
29
|
# Returns the current local mode of QueueBus
|
29
30
|
def local_mode
|
30
|
-
if Thread.current.
|
31
|
+
if Thread.current.thread_variable_get(LOCAL_MODE_VAR).is_a?(Wrap)
|
31
32
|
Thread.current.thread_variable_get(LOCAL_MODE_VAR).value
|
32
33
|
else
|
33
34
|
@local_mode
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
38
|
+
# Returns the current context of QueueBus
|
39
|
+
def context
|
40
|
+
if Thread.current.thread_variable_get(CONTEXT_VAR).is_a?(Wrap)
|
41
|
+
Thread.current.thread_variable_get(CONTEXT_VAR).value
|
42
|
+
else
|
43
|
+
@context
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
37
47
|
# Overrides the current local mode for the duration of a block. This is a threadsafe
|
38
48
|
# implementation. After, the global setting will be resumed.
|
39
49
|
#
|
@@ -46,6 +56,17 @@ module QueueBus
|
|
46
56
|
Thread.current.thread_variable_set(LOCAL_MODE_VAR, previous)
|
47
57
|
end
|
48
58
|
|
59
|
+
# Overrides the current bus context (if any) for the duration of a block, adding a
|
60
|
+
# `bus_context` attribute set to this value for all events published in this scope.
|
61
|
+
# This is a threadsafe implementation. After, the global setting will be resumed.
|
62
|
+
def in_context(context)
|
63
|
+
previous = Thread.current.thread_variable_get(CONTEXT_VAR)
|
64
|
+
Thread.current.thread_variable_set(CONTEXT_VAR, Wrap.new(context))
|
65
|
+
yield if block_given?
|
66
|
+
ensure
|
67
|
+
Thread.current.thread_variable_set(CONTEXT_VAR, previous)
|
68
|
+
end
|
69
|
+
|
49
70
|
def adapter=(val)
|
50
71
|
raise "Adapter already set to #{@adapter_instance.class.name}" if has_adapter?
|
51
72
|
|
data/lib/queue_bus/dispatch.rb
CHANGED
@@ -1,23 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# Creates a DSL for apps to define their blocks to run for event_types
|
2
4
|
|
3
5
|
module QueueBus
|
6
|
+
# A Dispatch object can be used to declare an application along with it's various subscriptions.
|
4
7
|
class Dispatch
|
5
|
-
|
6
8
|
attr_reader :app_key, :subscriptions
|
7
|
-
|
9
|
+
|
8
10
|
def initialize(app_key)
|
9
11
|
@app_key = Application.normalize(app_key)
|
10
12
|
@subscriptions = SubscriptionList.new
|
11
13
|
end
|
12
|
-
|
14
|
+
|
13
15
|
def size
|
14
16
|
@subscriptions.size
|
15
17
|
end
|
16
|
-
|
18
|
+
|
17
19
|
def subscribe(key, matcher_hash = nil, &block)
|
18
|
-
dispatch_event(
|
20
|
+
dispatch_event('default', key, matcher_hash, block)
|
19
21
|
end
|
20
|
-
|
22
|
+
|
21
23
|
# allows definitions of other queues
|
22
24
|
def method_missing(method_name, *args, &block)
|
23
25
|
if args.size == 1 && block
|
@@ -28,7 +30,7 @@ module QueueBus
|
|
28
30
|
super
|
29
31
|
end
|
30
32
|
end
|
31
|
-
|
33
|
+
|
32
34
|
def execute(key, attributes)
|
33
35
|
sub = subscriptions.key(key)
|
34
36
|
if sub
|
@@ -41,17 +43,17 @@ module QueueBus
|
|
41
43
|
def subscription_matches(attributes)
|
42
44
|
out = subscriptions.matches(attributes)
|
43
45
|
out.each do |sub|
|
44
|
-
sub.app_key =
|
46
|
+
sub.app_key = app_key
|
45
47
|
end
|
46
48
|
out
|
47
49
|
end
|
48
|
-
|
50
|
+
|
49
51
|
def dispatch_event(queue, key, matcher_hash, block)
|
50
52
|
# if not matcher_hash, assume key is a event_type regex
|
51
|
-
matcher_hash ||= {
|
52
|
-
add_subscription("#{app_key}_#{queue}", key,
|
53
|
+
matcher_hash ||= { 'bus_event_type' => key }
|
54
|
+
add_subscription("#{app_key}_#{queue}", key, '::QueueBus::Rider', matcher_hash, block)
|
53
55
|
end
|
54
|
-
|
56
|
+
|
55
57
|
def add_subscription(queue_name, key, class_name, matcher_hash = nil, block)
|
56
58
|
sub = Subscription.register(queue_name, key, class_name, matcher_hash, block)
|
57
59
|
subscriptions.add(sub)
|