bbk-app 1.0.0.72899

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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +7 -0
  3. data/Gemfile.lock +177 -0
  4. data/README.md +38 -0
  5. data/bin/console +15 -0
  6. data/bin/setup +8 -0
  7. data/lib/bbk/app/dispatcher/message.rb +47 -0
  8. data/lib/bbk/app/dispatcher/message_stream.rb +42 -0
  9. data/lib/bbk/app/dispatcher/queue_stream_strategy.rb +54 -0
  10. data/lib/bbk/app/dispatcher/result.rb +20 -0
  11. data/lib/bbk/app/dispatcher/route.rb +37 -0
  12. data/lib/bbk/app/dispatcher.rb +210 -0
  13. data/lib/bbk/app/factory.rb +26 -0
  14. data/lib/bbk/app/handler.rb +69 -0
  15. data/lib/bbk/app/matchers/base.rb +50 -0
  16. data/lib/bbk/app/matchers/delivery_info.rb +35 -0
  17. data/lib/bbk/app/matchers/full.rb +41 -0
  18. data/lib/bbk/app/matchers/headers.rb +23 -0
  19. data/lib/bbk/app/matchers/payload.rb +23 -0
  20. data/lib/bbk/app/matchers.rb +28 -0
  21. data/lib/bbk/app/middlewares/active_record_pool.rb +21 -0
  22. data/lib/bbk/app/middlewares/base.rb +20 -0
  23. data/lib/bbk/app/middlewares/from_block.rb +26 -0
  24. data/lib/bbk/app/middlewares/self_killer.rb +66 -0
  25. data/lib/bbk/app/middlewares/watchdog.rb +78 -0
  26. data/lib/bbk/app/middlewares.rb +12 -0
  27. data/lib/bbk/app/processors/base.rb +46 -0
  28. data/lib/bbk/app/processors/ping.rb +26 -0
  29. data/lib/bbk/app/processors/pong.rb +16 -0
  30. data/lib/bbk/app/processors.rb +3 -0
  31. data/lib/bbk/app/proxy_logger.rb +42 -0
  32. data/lib/bbk/app/thread_pool.rb +75 -0
  33. data/lib/bbk/app/version.rb +8 -0
  34. data/lib/bbk/app.rb +23 -0
  35. data/sig/bbk/app/callable.rbs +3 -0
  36. data/sig/bbk/app/dispatcher/message.rbs +33 -0
  37. data/sig/bbk/app/dispatcher/message_stream.rbs +15 -0
  38. data/sig/bbk/app/dispatcher/queue_stream_strategy.rbs +12 -0
  39. data/sig/bbk/app/dispatcher/result.rbs +12 -0
  40. data/sig/bbk/app/dispatcher/route.rbs +18 -0
  41. data/sig/bbk/app/dispatcher/stream_strategy.rbs +13 -0
  42. data/sig/bbk/app/dispatcher.rbs +62 -0
  43. data/sig/bbk/app/factory.rbs +21 -0
  44. data/sig/bbk/app/handler.rbs +19 -0
  45. data/sig/bbk/app/matchers.rbs +12 -0
  46. data/sig/bbk/app/middlewares/self_killer.rbs +26 -0
  47. data/sig/bbk/app/middlewares/watchdog.rbs +40 -0
  48. data/sig/bbk/app/processors/base.rbs +21 -0
  49. metadata +327 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b47e943b8f8075f53603a17fdbe11c1c71de2dd330e9a338f7ab5f4901602ecc
4
+ data.tar.gz: f0f7ebd29ed819ebc6e2e0d7999ff138989bd637bc382bb8be5c42397a80dc32
5
+ SHA512:
6
+ metadata.gz: 261f9ce6914823596cd18afc01ff4ec8717c472ba5b956e3ee3fcfdde69c21bdee6193ff48d959aeab6211fdda8e44e4f47e21a7c2f2be2990377a6f5129cd29
7
+ data.tar.gz: ca5a3164cc571ba52c9d04b704ab199b700027ce65c935b08e220cb364cd047f27b6613f12de152a7b26c8d989f1cf1faa7b7307ae8bc570ea7978e235f4c3fb
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in bbk-app.gemspec
6
+ gemspec
7
+
data/Gemfile.lock ADDED
@@ -0,0 +1,177 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ bbk-app (1.0.0.72899)
5
+ activesupport
6
+ bbk-utils (> 1.0.1)
7
+ timeouter
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activemodel (6.1.4.4)
13
+ activesupport (= 6.1.4.4)
14
+ activerecord (6.1.4.4)
15
+ activemodel (= 6.1.4.4)
16
+ activesupport (= 6.1.4.4)
17
+ activesupport (6.1.4.4)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 1.6, < 2)
20
+ minitest (>= 5.1)
21
+ tzinfo (~> 2.0)
22
+ zeitwerk (~> 2.3)
23
+ addressable (2.8.0)
24
+ public_suffix (>= 2.0.2, < 5.0)
25
+ amq-protocol (2.3.2)
26
+ ansi (1.5.0)
27
+ ast (2.4.2)
28
+ axiom-types (0.1.1)
29
+ descendants_tracker (~> 0.0.4)
30
+ ice_nine (~> 0.11.0)
31
+ thread_safe (~> 0.3, >= 0.3.1)
32
+ bbk-utils (1.0.1.72735)
33
+ activesupport (~> 6.0)
34
+ russian
35
+ bunny (2.19.0)
36
+ amq-protocol (~> 2.3, >= 2.3.1)
37
+ sorted_set (~> 1, >= 1.0.2)
38
+ bunny-mock (1.7.0)
39
+ bunny (>= 1.7)
40
+ byebug (11.1.3)
41
+ coercible (1.0.0)
42
+ descendants_tracker (~> 0.0.1)
43
+ concurrent-ruby (1.1.9)
44
+ database_cleaner (1.99.0)
45
+ descendants_tracker (0.0.4)
46
+ thread_safe (~> 0.3, >= 0.3.1)
47
+ diff-lcs (1.5.0)
48
+ docile (1.4.0)
49
+ equalizer (0.0.11)
50
+ erubis (2.7.0)
51
+ faker (2.19.0)
52
+ i18n (>= 1.6, < 2)
53
+ flay (2.12.1)
54
+ erubis (~> 2.7.0)
55
+ path_expander (~> 1.0)
56
+ ruby_parser (~> 3.0)
57
+ sexp_processor (~> 4.0)
58
+ flog (4.6.4)
59
+ path_expander (~> 1.0)
60
+ ruby_parser (~> 3.1, > 3.1.0)
61
+ sexp_processor (~> 4.8)
62
+ i18n (1.8.11)
63
+ concurrent-ruby (~> 1.0)
64
+ ice_nine (0.11.2)
65
+ kwalify (0.7.2)
66
+ launchy (2.5.0)
67
+ addressable (~> 2.7)
68
+ minitest (5.15.0)
69
+ parallel (1.21.0)
70
+ parser (3.0.3.2)
71
+ ast (~> 2.4.1)
72
+ path_expander (1.1.0)
73
+ public_suffix (4.0.6)
74
+ rainbow (3.1.1)
75
+ rake (12.3.3)
76
+ rbtree (0.4.4)
77
+ reek (6.0.6)
78
+ kwalify (~> 0.7.0)
79
+ parser (~> 3.0.0)
80
+ rainbow (>= 2.0, < 4.0)
81
+ regexp_parser (2.2.0)
82
+ rexml (3.2.5)
83
+ rspec (3.10.0)
84
+ rspec-core (~> 3.10.0)
85
+ rspec-expectations (~> 3.10.0)
86
+ rspec-mocks (~> 3.10.0)
87
+ rspec-core (3.10.1)
88
+ rspec-support (~> 3.10.0)
89
+ rspec-expectations (3.10.1)
90
+ diff-lcs (>= 1.2.0, < 2.0)
91
+ rspec-support (~> 3.10.0)
92
+ rspec-mocks (3.10.2)
93
+ diff-lcs (>= 1.2.0, < 2.0)
94
+ rspec-support (~> 3.10.0)
95
+ rspec-support (3.10.3)
96
+ rspec_junit_formatter (0.5.1)
97
+ rspec-core (>= 2, < 4, != 2.12.0)
98
+ rubocop (1.24.1)
99
+ parallel (~> 1.10)
100
+ parser (>= 3.0.0.0)
101
+ rainbow (>= 2.2.2, < 4.0)
102
+ regexp_parser (>= 1.8, < 3.0)
103
+ rexml
104
+ rubocop-ast (>= 1.15.1, < 2.0)
105
+ ruby-progressbar (~> 1.7)
106
+ unicode-display_width (>= 1.4.0, < 3.0)
107
+ rubocop-ast (1.15.1)
108
+ parser (>= 3.0.1.1)
109
+ ruby-progressbar (1.11.0)
110
+ ruby_parser (3.18.1)
111
+ sexp_processor (~> 4.16)
112
+ rubycritic (4.6.1)
113
+ flay (~> 2.8)
114
+ flog (~> 4.4)
115
+ launchy (>= 2.0.0)
116
+ parser (>= 2.6.0)
117
+ rainbow (~> 3.0)
118
+ reek (~> 6.0, < 7.0)
119
+ ruby_parser (~> 3.8)
120
+ simplecov (>= 0.17.0)
121
+ tty-which (~> 0.4.0)
122
+ virtus (~> 1.0)
123
+ russian (0.6.0)
124
+ i18n (>= 0.5.0)
125
+ set (1.0.2)
126
+ sexp_processor (4.16.0)
127
+ simplecov (0.21.2)
128
+ docile (~> 1.1)
129
+ simplecov-html (~> 0.11)
130
+ simplecov_json_formatter (~> 0.1)
131
+ simplecov-console (0.9.1)
132
+ ansi
133
+ simplecov
134
+ terminal-table
135
+ simplecov-html (0.12.3)
136
+ simplecov_json_formatter (0.1.3)
137
+ sorted_set (1.0.3)
138
+ rbtree
139
+ set (~> 1.0)
140
+ sqlite3 (1.4.2)
141
+ terminal-table (3.0.2)
142
+ unicode-display_width (>= 1.1.1, < 3)
143
+ thread_safe (0.3.6)
144
+ timeouter (0.1.3.38794)
145
+ tty-which (0.4.2)
146
+ tzinfo (2.0.4)
147
+ concurrent-ruby (~> 1.0)
148
+ unicode-display_width (2.1.0)
149
+ virtus (1.0.5)
150
+ axiom-types (~> 0.1)
151
+ coercible (~> 1.0)
152
+ descendants_tracker (~> 0.0, >= 0.0.3)
153
+ equalizer (~> 0.0, >= 0.0.9)
154
+ zeitwerk (2.5.3)
155
+
156
+ PLATFORMS
157
+ ruby
158
+
159
+ DEPENDENCIES
160
+ activerecord (~> 6.0)
161
+ bbk-app!
162
+ bundler (~> 2.0)
163
+ bunny-mock (~> 1.7.0)
164
+ byebug
165
+ database_cleaner (~> 1.7)
166
+ faker (~> 2)
167
+ rake (~> 12.0)
168
+ rspec (~> 3.0)
169
+ rspec_junit_formatter
170
+ rubocop
171
+ rubycritic
172
+ simplecov
173
+ simplecov-console
174
+ sqlite3 (~> 1.4)
175
+
176
+ BUNDLED WITH
177
+ 2.2.33
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # BBK::App
2
+
3
+ Classes for building services based on BBK stack.
4
+
5
+ ## Installation
6
+
7
+ Adding to a gem:
8
+
9
+ ```ruby
10
+ # my-cool-gem.gemspec
11
+
12
+ Gem::Specification.new do |spec|
13
+ # ...
14
+ spec.add_dependency "bbk-app", "~> 1.0.0"
15
+ # ...
16
+ end
17
+ ```
18
+
19
+ Or adding to your project:
20
+
21
+ ```ruby
22
+ # Gemfile
23
+
24
+ gem "bbk-app", "~> 1.0.0"
25
+ ```
26
+
27
+ ### Supported Ruby versions
28
+
29
+ * Ruby (MRI) >= 2.5.0
30
+
31
+ ### Tested Ruby versions
32
+
33
+ * Ruby (MRI) 2.5.x
34
+ * Ruby (MRI) 3.0.x
35
+
36
+ ## License
37
+
38
+ The gem is available as open source under the terms of the MIT License.
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'bbk/app'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start(__FILE__)
15
+
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,47 @@
1
+ module BBK
2
+ module App
3
+ class Dispatcher
4
+ class Message
5
+
6
+ attr_reader :consumer, :delivery_info, :headers, :payload, :body
7
+
8
+ def initialize(consumer, delivery_info, headers, body, *_args, **_kwargs)
9
+ @consumer = consumer
10
+ @delivery_info = delivery_info
11
+ @headers = headers.to_h.with_indifferent_access
12
+ @body = body
13
+ @payload = begin
14
+ JSON(body).with_indifferent_access
15
+ rescue StandardError
16
+ {}.with_indifferent_access
17
+ end
18
+ end
19
+
20
+ def ack(*args, answer: nil, **kwargs)
21
+ consumer.ack(self, *args, answer: answer, **kwargs)
22
+ end
23
+
24
+ def nack(*args, error: nil, **kwargs)
25
+ consumer.nack(self, *args, error: error, **kwargs)
26
+ end
27
+
28
+ def message_id
29
+ raise NotImplementedError
30
+ end
31
+
32
+ def reply_message_id(addon)
33
+ Digest::SHA1.hexdigest("#{addon}#{message_id}")
34
+ end
35
+
36
+ def to_h
37
+ {
38
+ headers: headers,
39
+ body: body
40
+ }
41
+ end
42
+
43
+ end
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBK
4
+ module App
5
+ class Dispatcher
6
+ class MessageStream
7
+
8
+ CLOSE_VALUE = :close
9
+ attr_reader :queue, :stream
10
+
11
+ def initialize(size: 10)
12
+ @queue = SizedQueue.new(size)
13
+ @closed = false
14
+ end
15
+
16
+ def push(message)
17
+ @queue.push(message) unless @closed
18
+ end
19
+ alias << push
20
+
21
+ def each
22
+ return to_enum unless block_given?
23
+ return nil if @closed
24
+
25
+ loop do
26
+ value = @queue.pop
27
+ break if value == CLOSE_VALUE
28
+
29
+ yield(value)
30
+ end
31
+ end
32
+
33
+ def close
34
+ @closed = true
35
+ @queue << CLOSE_VALUE
36
+ end
37
+
38
+ end
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,54 @@
1
+ require 'bbk/app/dispatcher/message_stream'
2
+
3
+ module BBK
4
+ module App
5
+ class Dispatcher
6
+ class QueueStreamStrategy
7
+
8
+ def initialize(pool, logger:)
9
+ @pool = pool
10
+ @logger = logger
11
+ end
12
+
13
+ def run(consumers, &block)
14
+ @unblocker = Queue.new
15
+ @stream = BBK::App::Dispatcher::MessageStream.new(size: 10)
16
+
17
+ consumers.each {|cons| cons.run(@stream) }
18
+ @stream.each do |msg|
19
+ @logger.debug "[#{self.class}] Consumed message #{msg.headers}"
20
+ @pool.post(msg) do |m|
21
+ block.call(m)
22
+ end
23
+ end
24
+
25
+ begin
26
+ @pool.shutdown
27
+ rescue StandardError
28
+ nil
29
+ end
30
+ @pool.kill unless @pool.wait_for_termination(@stop_queue_timeout)
31
+ ensure
32
+ @unblocker.push(:ok)
33
+ end
34
+
35
+ def push(*args)
36
+ @stream.push(*args)
37
+ end
38
+
39
+ def stop(timeout = 5)
40
+ @stop_queue_timeout = timeout
41
+
42
+ begin
43
+ @stream.close
44
+ rescue StandardError
45
+ nil
46
+ end
47
+ @unblocker.pop
48
+ end
49
+
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -0,0 +1,20 @@
1
+ module BBK
2
+ module App
3
+ class Dispatcher
4
+ class Result
5
+
6
+ attr_accessor :route, :message
7
+
8
+ def initialize(route, message)
9
+ @route = route.is_a?(String) ? Dispatcher::Route.new(route) : route
10
+
11
+ raise 'route must be of type Dispatcher::Route' unless @route.is_a?(Dispatcher::Route)
12
+
13
+ @message = message
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBK
4
+ module App
5
+ class Dispatcher
6
+ class Route
7
+
8
+ attr_reader :uri, :scheme, :domain, :routing_key
9
+
10
+ # Example: mq://gw@service.smev.request
11
+ def initialize(string)
12
+ @uri = URI(string)
13
+ @scheme = uri.scheme
14
+ @domain = uri.user
15
+ @routing_key = "#{uri.host}#{uri.path}"
16
+
17
+ # raise 'domain must present in route' if @domain.blank?
18
+ raise 'routing_key must present in route' if @routing_key.blank?
19
+ end
20
+
21
+ def to_s
22
+ @uri.to_s
23
+ end
24
+
25
+ def ==(other)
26
+ if other.is_a?(String)
27
+ to_s == other
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,210 @@
1
+ require 'concurrent'
2
+ require 'bbk/app/thread_pool'
3
+ require 'bbk/app/dispatcher/message_stream'
4
+ require 'bbk/app/dispatcher/message'
5
+ require 'bbk/app/dispatcher/queue_stream_strategy'
6
+ require 'bbk/app/dispatcher/result'
7
+ require 'bbk/app/dispatcher/route'
8
+ require 'bbk/utils/proxy_logger'
9
+
10
+ module BBK
11
+ module App
12
+
13
+ class SimplePoolFactory
14
+
15
+ def self.call(pool_size, queue_size)
16
+ BBK::App::ThreadPool.new(pool_size, queue: queue_size)
17
+ end
18
+
19
+ end
20
+
21
+ class ConcurrentPoolFactory
22
+
23
+ def self.call(pool_size, queue_size)
24
+ Concurrent::FixedThreadPool.new(pool_size, max_queue: queue_size,
25
+ fallback_policy: :caller_runs)
26
+ end
27
+
28
+ end
29
+
30
+ class Dispatcher
31
+
32
+ attr_accessor :force_quit
33
+ attr_reader :consumers, :publishers, :observer, :middlewares, :logger
34
+
35
+ ANSWER_DOMAIN = 'answer'
36
+
37
+ def initialize(observer, pool_size: 3, logger: BBK::App.logger, pool_factory: SimplePoolFactory, stream_strategy: QueueStreamStrategy)
38
+ @observer = observer
39
+ @pool_size = pool_size
40
+ logger = logger.respond_to?(:tagged) ? logger : ActiveSupport::TaggedLogging.new(logger)
41
+ @logger = BBK::Utils::ProxyLogger.new(logger, tags: 'Dispatcher')
42
+ @consumers = []
43
+ @publishers = []
44
+ @middlewares = []
45
+ @pool_factory = pool_factory
46
+ @stream_strategy_class = stream_strategy
47
+ @force_quit = false
48
+ end
49
+
50
+ def register_consumer(consumer)
51
+ consumers << consumer
52
+ end
53
+
54
+ def register_publisher(publisher)
55
+ publishers << publisher
56
+ end
57
+
58
+ def register_middleware(middleware)
59
+ middlewares << middleware
60
+ end
61
+
62
+ # Run all consumers and blocks on message processing
63
+ def run
64
+ @pool = @pool_factory.call(@pool_size, 10)
65
+ @stream_strategy = @stream_strategy_class.new(@pool, logger: logger)
66
+ ActiveSupport::Notifications.instrument 'dispatcher.run', dispatcher: self
67
+
68
+ @stream_strategy.run(consumers) do |msg|
69
+ begin
70
+ logger.tagged(msg.headers[:message_id]) do
71
+ process msg
72
+ end
73
+ rescue StandardError => e
74
+ logger.fatal "E[#{@stream_strategy_class}]: #{e}"
75
+ logger.fatal "E[#{@stream_strategy_class}]: #{e.backtrace.join("\n")}"
76
+ end
77
+ end
78
+ end
79
+
80
+ # stop dispatcher and wait for termination
81
+ # Чтоб остановить диспетчер надо:
82
+ # 1. остановить консьюмеры
83
+ # 2. остановить прием новых сообщений - @stream.close
84
+ # 3. дождаться обработки всего в очереди или таймаут
85
+ # 4. остановить потоки
86
+ # 5. остановить паблишеры
87
+ def close(_timeout = 5)
88
+ ActiveSupport::Notifications.instrument 'dispatcher.close', dispatcher: self
89
+ consumers.each do |cons|
90
+ begin
91
+ cons.stop
92
+ rescue StandardError => e
93
+ logger.error "Consumer #{cons} stop error: #{e}"
94
+ logger.debug e.backtrace
95
+ end
96
+ end
97
+
98
+ @stream_strategy.stop(5)
99
+
100
+ consumers.each do |cons|
101
+ begin
102
+ cons.close
103
+ rescue StandardError => e
104
+ logger.error "Consumer #{cons} close error: #{e}"
105
+ logger.debug e.backtrace
106
+ end
107
+ end
108
+
109
+ publishers.each do |pub|
110
+ begin
111
+ pub.close
112
+ rescue StandardError => e
113
+ logger.error "Publisher #{pub} close error: #{e}"
114
+ logger.debug e.backtrace
115
+ end
116
+ end
117
+ end
118
+
119
+ protected
120
+
121
+ def process(message)
122
+ results = build_processing_stack.call(message).select do |e|
123
+ e.is_a? BBK::App::Dispatcher::Result
124
+ end
125
+ logger.debug "There are #{results.count} results to send from #{message.headers[:message_id]}..."
126
+ send_results(message, results).value
127
+ rescue StandardError => e
128
+ ActiveSupport::Notifications.instrument 'dispatcher.exception', msg: message, exception: e
129
+ message.nack(error: e)
130
+ close if force_quit
131
+ end
132
+
133
+ def process_message(message)
134
+ matched, processor = find_processor(message)
135
+ results = []
136
+ begin
137
+ is_unknown = @observer.instance_variable_get('@default') == processor
138
+ ActiveSupport::Notifications.instrument 'dispatcher.request.process', msg: message, match: matched, unknown: is_unknown do
139
+ processor.call(message, results: results)
140
+ end
141
+ rescue StandardError => e
142
+ if processor.respond_to?(:on_error)
143
+ results = processor.on_error(message, e)
144
+ else
145
+ raise
146
+ end
147
+ end
148
+ [results].flatten
149
+ rescue StandardError => e
150
+ ActiveSupport::Notifications.instrument 'dispatcher.request.exception', msg: message, match: matched, processor: processor, exception: e
151
+ raise
152
+ end
153
+
154
+ def find_processor(msg)
155
+ matched, callback = @observer.match(msg.headers, msg.payload, msg.delivery_info)
156
+ [matched, callback.is_a?(BBK::App::Factory) ? callback.create : callback]
157
+ end
158
+
159
+ def build_processing_stack
160
+ stack = proc{|msg| process_message(msg) }
161
+ middlewares.reduce(stack) do |stack, middleware|
162
+ if middleware.respond_to?(:build)
163
+ middleware.build(stack)
164
+ else
165
+ middleware.new(stack)
166
+ end
167
+ end
168
+ end
169
+
170
+ def send_results(incoming, results)
171
+ message_id = incoming.headers[:message_id]
172
+
173
+ answer = results.find {|msg| msg.route.domain == ANSWER_DOMAIN }
174
+ Concurrent::Promises.zip_futures(*results.map do |result|
175
+ publish_result(result)
176
+ end).then do |_successes|
177
+ incoming.ack(answer: answer)
178
+ end.rescue do |*errors|
179
+ error = errors.compact.first
180
+ ActiveSupport::Notifications.instrument 'dispatcher.request.result_rejected',
181
+ msg: incoming, message: error.inspect
182
+ logger.error "[Message#{message_id}] Publish failed: #{error.inspect}"
183
+ incoming.nack(error: error)
184
+ close if force_quit
185
+ rescue StandardError => e
186
+ warn e.backtrace
187
+ warn "[CRITICAL] #{self.class} [#{Process.pid}] failure exiting: #{e.inspect}"
188
+ ActiveSupport::Notifications.instrument 'dispatcher.exception', msg: incoming,
189
+ exception: e
190
+ sleep(10)
191
+ exit!(1)
192
+ end
193
+ end
194
+
195
+ # @return [Concurrent::Promises::ResolvableFuture]
196
+ def publish_result(result)
197
+ route = result.route
198
+ logger.debug "Publish result to #{route} ..."
199
+ publisher = publishers.find {|pub| pub.protocols.include?(route.scheme) }
200
+ raise "Not found publisher for scheme #{route.scheme}" if publisher.nil?
201
+
202
+ # return Concurrent::Promises.resolvable_future
203
+ publisher.publish(result)
204
+ end
205
+
206
+ end
207
+
208
+ end
209
+ end
210
+
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BBK
4
+ module App
5
+ class Factory
6
+
7
+ attr_accessor :klass, :instanceargs, :instancekwargs
8
+
9
+ def initialize(klass, *args, **kwargs)
10
+ @klass = klass
11
+ @instanceargs = args
12
+ @instancekwargs = kwargs
13
+ end
14
+
15
+ def create
16
+ klass.new(*instanceargs, **instancekwargs)
17
+ end
18
+
19
+ def call(*args, **kwargs)
20
+ create.call(*args, **kwargs)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+