bbk-app 1.0.0.72899

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