eventboss 1.1.0 → 1.1.1
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/.github/workflows/ruby.yml +20 -0
- data/Gemfile.lock +8 -8
- data/README.md +14 -9
- data/lib/eventboss/configuration.rb +0 -7
- data/lib/eventboss/railtie.rb +4 -0
- data/lib/eventboss/runner.rb +0 -33
- data/lib/eventboss/version.rb +1 -1
- data/lib/eventboss.rb +0 -3
- data/lib/tasks/eventboss.rake +8 -8
- metadata +3 -5
- data/lib/eventboss/manager.rb +0 -116
- data/lib/eventboss/polling/basic.rb +0 -68
- data/lib/eventboss/polling/timed_round_robin.rb +0 -42
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63a1b730db853663d1a26cc59b1935a8176fb9aa0bde0be6e5761499a8127109
|
4
|
+
data.tar.gz: cae74a3c182b09e6c2a1ee34149388263428ddb3a9db5a8b7058de2d304b8544
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e52a3a0aafc32e09c275157bfcb9325167be90b8a6ab4cda02ddeccc5577c25e8477c64492751b24b3a1222640ce147a311b01c598500b9f6b1a7a1b67f1b88d
|
7
|
+
data.tar.gz: d8ade264acf142b682e05ce90a4ca32464f76a301e2e2c0440b4bf52e30d9d22f8ca84f58d02024c48c06c36f726b3b0fc050505f77ab262928f47ae5399cbd5
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
|
10
|
+
steps:
|
11
|
+
- uses: actions/checkout@v1
|
12
|
+
- name: Set up Ruby 2.6
|
13
|
+
uses: actions/setup-ruby@v1
|
14
|
+
with:
|
15
|
+
ruby-version: 2.6.x
|
16
|
+
- name: Build and test with Rake
|
17
|
+
run: |
|
18
|
+
gem install bundler
|
19
|
+
bundle install --jobs 4 --retry 3
|
20
|
+
bundle exec rake
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
eventboss (1.1.
|
4
|
+
eventboss (1.1.1)
|
5
5
|
aws-sdk-sns (>= 1.1.0)
|
6
6
|
aws-sdk-sqs (>= 1.3.0)
|
7
7
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
@@ -11,23 +11,23 @@ GEM
|
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
13
|
aws-eventstream (1.0.3)
|
14
|
-
aws-partitions (1.
|
15
|
-
aws-sdk-core (3.
|
14
|
+
aws-partitions (1.206.0)
|
15
|
+
aws-sdk-core (3.64.0)
|
16
16
|
aws-eventstream (~> 1.0, >= 1.0.2)
|
17
17
|
aws-partitions (~> 1.0)
|
18
18
|
aws-sigv4 (~> 1.1)
|
19
19
|
jmespath (~> 1.0)
|
20
|
-
aws-sdk-sns (1.
|
21
|
-
aws-sdk-core (~> 3, >= 3.
|
20
|
+
aws-sdk-sns (1.19.0)
|
21
|
+
aws-sdk-core (~> 3, >= 3.61.1)
|
22
22
|
aws-sigv4 (~> 1.1)
|
23
|
-
aws-sdk-sqs (1.
|
24
|
-
aws-sdk-core (~> 3, >= 3.
|
23
|
+
aws-sdk-sqs (1.21.0)
|
24
|
+
aws-sdk-core (~> 3, >= 3.61.1)
|
25
25
|
aws-sigv4 (~> 1.1)
|
26
26
|
aws-sigv4 (1.1.0)
|
27
27
|
aws-eventstream (~> 1.0, >= 1.0.2)
|
28
28
|
concurrent-ruby (1.1.5)
|
29
29
|
diff-lcs (1.3)
|
30
|
-
dotenv (2.7.
|
30
|
+
dotenv (2.7.5)
|
31
31
|
jmespath (1.4.0)
|
32
32
|
rake (12.3.1)
|
33
33
|
rspec (3.7.0)
|
data/README.md
CHANGED
@@ -102,24 +102,29 @@ AWS_SNS_ENDPOINT=http://localhost:4575 # when using with localstack
|
|
102
102
|
AWS_SQS_ENDPOINT=http://localhost:4576 # when using with localstack
|
103
103
|
```
|
104
104
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
Logger is used as default error handler. There is Airbrake handler available, to use it ensure you have `airbrake` or `airbrake-ruby` gem and add it to error handlers stack:
|
105
|
+
Be aware that `eventbus:deadletter:reload` rake task won't load your configuration if you are not using ENVs
|
106
|
+
in non Rails app, although to make it work you can extend your `Rakefile` with:
|
109
107
|
|
110
108
|
```ruby
|
111
|
-
|
112
|
-
|
109
|
+
load File.join(Gem::Specification.find_by_name('eventboss').gem_dir, 'lib', 'tasks', 'eventboss.rake')
|
110
|
+
|
111
|
+
task :environment do
|
112
|
+
# Load your environment
|
113
|
+
# Example:
|
114
|
+
# require_relative 'config/application'
|
113
115
|
end
|
116
|
+
|
117
|
+
task 'eventboss:deadletter:reload': :environment
|
114
118
|
```
|
115
119
|
|
116
|
-
###
|
120
|
+
### Logging and error handling
|
121
|
+
To have more verbose logging, set `log_level` in configuration (default is `info`).
|
117
122
|
|
118
|
-
|
123
|
+
Logger is used as default error handler. There is Airbrake handler available, to use it ensure you have `airbrake` or `airbrake-ruby` gem and add it to error handlers stack:
|
119
124
|
|
120
125
|
```ruby
|
121
126
|
Eventboss.configure do |config|
|
122
|
-
config.
|
127
|
+
config.error_handlers << Eventboss::ErrorHandlers::Airbrake.new
|
123
128
|
end
|
124
129
|
```
|
125
130
|
|
@@ -11,7 +11,6 @@ module Eventboss
|
|
11
11
|
:eventboss_account_id,
|
12
12
|
:aws_access_key_id,
|
13
13
|
:aws_secret_access_key,
|
14
|
-
:polling_strategy,
|
15
14
|
:aws_sns_endpoint,
|
16
15
|
:aws_sqs_endpoint,
|
17
16
|
:sns_sqs_name_infix
|
@@ -81,12 +80,6 @@ module Eventboss
|
|
81
80
|
defined_or_default('aws_sns_endpoint') { ENV['AWS_SNS_ENDPOINT'] }
|
82
81
|
end
|
83
82
|
|
84
|
-
def polling_strategy
|
85
|
-
defined_or_default('polling_strategy') do
|
86
|
-
lambda { |queues| Eventboss::Polling::Basic.new(queues) }
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
83
|
def sns_sqs_name_infix
|
91
84
|
defined_or_default('sns_sqs_name_infix') { ENV['EVENTBUS_SQS_SNS_NAME_INFIX'] || 'eventboss' }
|
92
85
|
end
|
data/lib/eventboss/railtie.rb
CHANGED
data/lib/eventboss/runner.rb
CHANGED
@@ -23,38 +23,6 @@ module Eventboss
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
def start
|
27
|
-
configuration = Eventboss.configuration
|
28
|
-
|
29
|
-
queue_listeners = Eventboss::QueueListener.list
|
30
|
-
Eventboss::Instrumentation.add(queue_listeners)
|
31
|
-
polling_strategy = configuration.polling_strategy.call(queue_listeners.keys)
|
32
|
-
|
33
|
-
fetcher = Eventboss::Fetcher.new(configuration)
|
34
|
-
executor = Concurrent.global_io_executor
|
35
|
-
|
36
|
-
manager = Eventboss::Manager.new(
|
37
|
-
fetcher,
|
38
|
-
polling_strategy,
|
39
|
-
executor,
|
40
|
-
queue_listeners,
|
41
|
-
configuration.concurrency,
|
42
|
-
configuration.error_handlers
|
43
|
-
)
|
44
|
-
|
45
|
-
manager.start
|
46
|
-
|
47
|
-
self_read = setup_signals([:SIGTERM])
|
48
|
-
|
49
|
-
begin
|
50
|
-
handle_signals(self_read)
|
51
|
-
rescue Interrupt
|
52
|
-
executor.shutdown
|
53
|
-
executor.wait_for_termination
|
54
|
-
exit 0
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
26
|
private
|
59
27
|
|
60
28
|
def setup_signals(signals)
|
@@ -69,7 +37,6 @@ module Eventboss
|
|
69
37
|
self_read
|
70
38
|
end
|
71
39
|
|
72
|
-
|
73
40
|
def handle_signals(self_read, launcher)
|
74
41
|
while readable_io = IO.select([self_read])
|
75
42
|
signal = readable_io.first[0].gets.strip
|
data/lib/eventboss/version.rb
CHANGED
data/lib/eventboss.rb
CHANGED
@@ -19,11 +19,8 @@ require 'eventboss/worker'
|
|
19
19
|
require 'eventboss/fetcher'
|
20
20
|
require 'eventboss/publisher'
|
21
21
|
require 'eventboss/sender'
|
22
|
-
require 'eventboss/manager'
|
23
22
|
require 'eventboss/runner'
|
24
23
|
require 'eventboss/logger'
|
25
|
-
require 'eventboss/polling/basic'
|
26
|
-
require 'eventboss/polling/timed_round_robin'
|
27
24
|
require 'eventboss/extensions'
|
28
25
|
|
29
26
|
# For Rails use railtie, for plain Ruby apps use custom scripts loader
|
data/lib/tasks/eventboss.rake
CHANGED
@@ -3,7 +3,7 @@ require 'rake'
|
|
3
3
|
namespace :eventboss do
|
4
4
|
namespace :deadletter do
|
5
5
|
desc 'Reload deadletter queue'
|
6
|
-
task :reload, [:event_name, :source_app, :max_messages] do |
|
6
|
+
task :reload, [:event_name, :source_app, :max_messages] do |task, args|
|
7
7
|
source_app = args[:source_app]
|
8
8
|
event_name = args[:event_name]
|
9
9
|
|
@@ -13,7 +13,7 @@ namespace :eventboss do
|
|
13
13
|
# Ensure we don't fetch more than 10 messages from SQS
|
14
14
|
batch_size = max_messages == 0 ? 10 : [10, max_messages].min
|
15
15
|
|
16
|
-
abort
|
16
|
+
abort "[#{task.name}] At least event name should be passed as argument" unless event_name
|
17
17
|
|
18
18
|
queue_name = [
|
19
19
|
Eventboss.configuration.eventboss_app_name,
|
@@ -21,14 +21,14 @@ namespace :eventboss do
|
|
21
21
|
source_app,
|
22
22
|
event_name,
|
23
23
|
Eventboss.env
|
24
|
-
].join('-')
|
24
|
+
].compact.join('-')
|
25
|
+
puts "[#{task.name}] Reloading #{queue_name}-deadletter (max: #{ max_messages }, batch: #{ batch_size })"
|
25
26
|
queue = Eventboss::Queue.new("#{queue_name}-deadletter")
|
26
27
|
send_queue = Eventboss::Queue.new(queue_name)
|
27
28
|
|
28
|
-
puts "
|
29
|
-
puts "
|
30
|
-
puts
|
31
|
-
puts " #{send_queue.url}"
|
29
|
+
puts "[#{task.name}] #{queue.url}"
|
30
|
+
puts "[#{task.name}] to"
|
31
|
+
puts "[#{task.name}] #{send_queue.url}"
|
32
32
|
|
33
33
|
fetcher = Eventboss::Fetcher.new(Eventboss.configuration)
|
34
34
|
client = fetcher.client
|
@@ -38,7 +38,7 @@ namespace :eventboss do
|
|
38
38
|
break if messages.count.zero?
|
39
39
|
|
40
40
|
messages.each do |message|
|
41
|
-
puts "Publishing message: #{message.body}"
|
41
|
+
puts "[#{task.name}] Publishing message: #{message.body}"
|
42
42
|
client.send_message(queue_url: send_queue.url, message_body: message.body)
|
43
43
|
fetcher.delete(queue, message)
|
44
44
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eventboss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AirHelp
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -128,6 +128,7 @@ executables:
|
|
128
128
|
extensions: []
|
129
129
|
extra_rdoc_files: []
|
130
130
|
files:
|
131
|
+
- ".github/workflows/ruby.yml"
|
131
132
|
- ".gitignore"
|
132
133
|
- ".rspec"
|
133
134
|
- CHANGELOG.md
|
@@ -151,9 +152,6 @@ files:
|
|
151
152
|
- lib/eventboss/logger.rb
|
152
153
|
- lib/eventboss/logging.rb
|
153
154
|
- lib/eventboss/long_poller.rb
|
154
|
-
- lib/eventboss/manager.rb
|
155
|
-
- lib/eventboss/polling/basic.rb
|
156
|
-
- lib/eventboss/polling/timed_round_robin.rb
|
157
155
|
- lib/eventboss/publisher.rb
|
158
156
|
- lib/eventboss/queue.rb
|
159
157
|
- lib/eventboss/queue_listener.rb
|
data/lib/eventboss/manager.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
module Eventboss
|
2
|
-
class Manager
|
3
|
-
MIN_DISPATCH_INTERVAL = 0.1
|
4
|
-
|
5
|
-
def initialize(fetcher, polling_strategy, executor, queue_listeners, concurrency, error_handlers)
|
6
|
-
@fetcher = fetcher
|
7
|
-
@polling_strategy = polling_strategy
|
8
|
-
@max_processors = concurrency
|
9
|
-
@busy_processors = Concurrent::AtomicFixnum.new(0)
|
10
|
-
@executor = executor
|
11
|
-
@queue_listeners = queue_listeners
|
12
|
-
@error_handlers = Array(error_handlers)
|
13
|
-
end
|
14
|
-
|
15
|
-
def start
|
16
|
-
Eventboss::Logger.debug('Starting dispatch loop...')
|
17
|
-
|
18
|
-
dispatch_loop
|
19
|
-
end
|
20
|
-
|
21
|
-
private
|
22
|
-
|
23
|
-
def running?
|
24
|
-
@executor.running?
|
25
|
-
end
|
26
|
-
|
27
|
-
def dispatch_loop
|
28
|
-
return unless running?
|
29
|
-
|
30
|
-
Eventboss::Logger.debug('Posting task to executor')
|
31
|
-
|
32
|
-
@executor.post { dispatch }
|
33
|
-
end
|
34
|
-
|
35
|
-
def dispatch
|
36
|
-
return unless running?
|
37
|
-
|
38
|
-
if ready <= 0 || (queue = @polling_strategy.next_queue).nil?
|
39
|
-
return sleep(MIN_DISPATCH_INTERVAL)
|
40
|
-
end
|
41
|
-
dispatch_single_messages(queue)
|
42
|
-
rescue => ex
|
43
|
-
handle_dispatch_error(ex)
|
44
|
-
ensure
|
45
|
-
Eventboss::Logger.debug('Ensuring dispatch loop')
|
46
|
-
dispatch_loop
|
47
|
-
end
|
48
|
-
|
49
|
-
def busy
|
50
|
-
@busy_processors.value
|
51
|
-
end
|
52
|
-
|
53
|
-
def ready
|
54
|
-
@max_processors - busy
|
55
|
-
end
|
56
|
-
|
57
|
-
def processor_done(processor)
|
58
|
-
Eventboss::Logger.info("Success", processor.jid)
|
59
|
-
@busy_processors.decrement
|
60
|
-
end
|
61
|
-
|
62
|
-
def processor_error(processor, exception)
|
63
|
-
@error_handlers.each { |handler| handler.call(exception, processor) }
|
64
|
-
@busy_processors.decrement
|
65
|
-
end
|
66
|
-
|
67
|
-
def assign(queue, sqs_msg)
|
68
|
-
return unless running?
|
69
|
-
|
70
|
-
@busy_processors.increment
|
71
|
-
processor = @queue_listeners[queue].new
|
72
|
-
|
73
|
-
Concurrent::Promise.execute(executor: @executor) do
|
74
|
-
body = JSON.parse(sqs_msg.body) rescue sqs_msg.body
|
75
|
-
Eventboss::Logger.info("Started", processor.jid)
|
76
|
-
processor.receive(body)
|
77
|
-
end.then do
|
78
|
-
cleanup(processor)
|
79
|
-
postpone_if_needed(queue, sqs_msg, processor) || delete_from_queue(queue, sqs_msg)
|
80
|
-
processor_done(processor)
|
81
|
-
end.rescue do |e|
|
82
|
-
cleanup(processor)
|
83
|
-
postpone_if_needed(queue, sqs_msg, processor)
|
84
|
-
processor_error(processor, e)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def cleanup(_processor)
|
89
|
-
if defined?(ActiveRecord)
|
90
|
-
::ActiveRecord::Base.clear_active_connections!
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def delete_from_queue(queue, sqs_msg)
|
95
|
-
@fetcher.delete(queue, sqs_msg)
|
96
|
-
end
|
97
|
-
|
98
|
-
def postpone_if_needed(queue, sqs_msg, processor)
|
99
|
-
return false unless processor.postponed_by
|
100
|
-
@fetcher.change_message_visibility(queue, sqs_msg, processor.postponed_by)
|
101
|
-
rescue => error
|
102
|
-
Eventboss::Logger.info("Could not postpone message #{error.message}", processor.jid)
|
103
|
-
end
|
104
|
-
|
105
|
-
def dispatch_single_messages(queue)
|
106
|
-
messages = @fetcher.fetch(queue, ready)
|
107
|
-
@polling_strategy.messages_found(queue, messages.size)
|
108
|
-
messages.each { |message| assign(queue, message) }
|
109
|
-
end
|
110
|
-
|
111
|
-
def handle_dispatch_error(ex)
|
112
|
-
Eventboss::Logger.error("Error dispatching #{ex.message}")
|
113
|
-
Process.kill('USR1', Process.pid)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
module Eventboss
|
2
|
-
module Polling
|
3
|
-
class Basic
|
4
|
-
PAUSE_AFTER_EMPTY = 2 # seconds
|
5
|
-
|
6
|
-
def initialize(queues, timer = Time)
|
7
|
-
@queues = queues.to_a
|
8
|
-
@timer = timer
|
9
|
-
@paused_until = @queues.each_with_object(Hash.new) do |queue, hash|
|
10
|
-
hash[queue] = @timer.at(0)
|
11
|
-
end
|
12
|
-
|
13
|
-
reset_next_queue
|
14
|
-
end
|
15
|
-
|
16
|
-
def next_queue
|
17
|
-
next_active_queue
|
18
|
-
end
|
19
|
-
|
20
|
-
def messages_found(queue, messages_count)
|
21
|
-
if messages_count == 0
|
22
|
-
pause(queue)
|
23
|
-
else
|
24
|
-
reset_next_queue
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def active_queues
|
29
|
-
@queues.reject { |q, _| queue_paused?(q) }
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def next_active_queue
|
35
|
-
reset_next_queue if queues_unpaused_since?
|
36
|
-
|
37
|
-
size = @queues.length
|
38
|
-
size.times do
|
39
|
-
queue = @queues[@next_queue_index]
|
40
|
-
@next_queue_index = (@next_queue_index + 1) % size
|
41
|
-
return queue unless queue_paused?(queue)
|
42
|
-
end
|
43
|
-
|
44
|
-
nil
|
45
|
-
end
|
46
|
-
|
47
|
-
def queues_unpaused_since?
|
48
|
-
last = @last_unpause_check
|
49
|
-
now = @last_unpause_check = @timer.now
|
50
|
-
|
51
|
-
last && @paused_until.values.any? { |t| t > last && t <= now }
|
52
|
-
end
|
53
|
-
|
54
|
-
def reset_next_queue
|
55
|
-
@next_queue_index = 0
|
56
|
-
end
|
57
|
-
|
58
|
-
def queue_paused?(queue)
|
59
|
-
@paused_until[queue] > @timer.now
|
60
|
-
end
|
61
|
-
|
62
|
-
def pause(queue)
|
63
|
-
return unless PAUSE_AFTER_EMPTY > 0
|
64
|
-
@paused_until[queue] = @timer.now + PAUSE_AFTER_EMPTY
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module Eventboss
|
2
|
-
module Polling
|
3
|
-
class TimedRoundRobin
|
4
|
-
PAUSE_AFTER_EMPTY = 2 # seconds
|
5
|
-
|
6
|
-
def initialize(queues, timer = Time)
|
7
|
-
@queues = queues.to_a
|
8
|
-
@timer = timer
|
9
|
-
@next_queue_index = 0
|
10
|
-
@paused_until = @queues.each_with_object(Hash.new) do |queue, hash|
|
11
|
-
hash[queue] = @timer.at(0)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def next_queue
|
16
|
-
size = @queues.length
|
17
|
-
size.times do
|
18
|
-
queue = @queues[@next_queue_index]
|
19
|
-
@next_queue_index = (@next_queue_index + 1) % size
|
20
|
-
return queue unless queue_paused?(queue)
|
21
|
-
end
|
22
|
-
|
23
|
-
nil
|
24
|
-
end
|
25
|
-
|
26
|
-
def messages_found(queue, messages_count)
|
27
|
-
pause(queue) if messages_count == 0
|
28
|
-
end
|
29
|
-
|
30
|
-
private
|
31
|
-
|
32
|
-
def queue_paused?(queue)
|
33
|
-
@paused_until[queue] > @timer.now
|
34
|
-
end
|
35
|
-
|
36
|
-
def pause(queue)
|
37
|
-
return unless PAUSE_AFTER_EMPTY > 0
|
38
|
-
@paused_until[queue] = @timer.now + PAUSE_AFTER_EMPTY
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|