eventboss 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|