evt-consumer 1.1.0.0 → 2.3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2077ef49032cd7e88e66307072188bd14f3f5eb60c3889d3eaa2d8e32d398a91
4
- data.tar.gz: db192f1c6a7d6c7629200fdb71152d325ec05fcd27f1acf17d2f6f7a77088a77
3
+ metadata.gz: 93275cd1c618ed75c10cccc91aa7e5c8a6b0f3cca8afceef0d7d241d4820edef
4
+ data.tar.gz: 833c710b283472d05591f52f1c78abe8e19aca73dd519c141956294d3696757a
5
5
  SHA512:
6
- metadata.gz: e80126fd3557847a29603838d917b11fbdc93b617cca9ac17ea2c68471d6c1cef76c9e15656a306bf4959ff6ea80f9615db120fb8c72d37ca8096b3c2ed5c187
7
- data.tar.gz: 4c64c89911619b136db60fc135619c17573410083ce86cf0fc4438254133b8bd1724f0123536393f58b783830a60c46248ba2cf62f58b96b8d4dd0852d52dcfd
6
+ metadata.gz: 74baf13ab8a47615e7eb4d5de5e5c5064f53ce9b5506b171becbf145cc802b70ce3641e1dab38bbc8d25f5e17ad73271046e14f4f786441206717795900c42b5
7
+ data.tar.gz: 0752efbcb2193628bf5a0456c38ac8d3c4005e7e3fd904b76a147a80051b4f5300eaae27c4380663192334eb0b2854331ee5046754c45785ee3262938618d3e5
@@ -1,4 +1,6 @@
1
1
  module Consumer
2
+ Error = Class.new(RuntimeError)
3
+
2
4
  def self.included(cls)
3
5
  cls.class_exec do
4
6
  include Dependency
@@ -14,7 +16,7 @@ module Consumer
14
16
 
15
17
  prepend Configure
16
18
 
17
- initializer :stream_name
19
+ initializer :category
18
20
 
19
21
  attr_writer :identifier
20
22
  def identifier
@@ -31,11 +33,11 @@ module Consumer
31
33
  @position_update_counter ||= 0
32
34
  end
33
35
 
34
- attr_accessor :session
35
-
36
36
  attr_accessor :poll_interval_milliseconds
37
37
 
38
- dependency :get
38
+ attr_accessor :session
39
+
40
+ dependency :get, MessageStore::Get
39
41
  dependency :position_store, PositionStore
40
42
  dependency :subscription, Subscription
41
43
 
@@ -47,29 +49,18 @@ module Consumer
47
49
  end
48
50
  end
49
51
 
50
- def dispatch(message_data)
51
- logger.trace { "Dispatching message (#{LogText.message_data(message_data)})" }
52
+ def start(&probe)
53
+ logger.info(tags: [:consumer, :start]) { "Starting consumer: #{self.class.name} (Category: #{category}, Identifier: #{identifier || '(none)'}, Position: #{subscription.position})" }
52
54
 
53
- self.class.handler_registry.each do |handler|
54
- handler.(message_data, session: session)
55
+ if Defaults.startup_info?
56
+ print_info
55
57
  end
56
58
 
57
- update_position(message_data.global_position)
58
-
59
- logger.info { "Message dispatched (#{LogText.message_data(message_data)})" }
60
-
61
- rescue => error
62
- logger.error { "Error raised (Error Class: #{error.class}, Error Message: #{error.message}, #{LogText.message_data(message_data)})" }
63
- error_raised(error, message_data)
64
- end
59
+ log_info
60
+ starting if respond_to?(:starting)
65
61
 
66
- def start(&probe)
67
- logger.info(tag: :*) { "Starting consumer: #{self.class.name} (Stream: #{stream_name}, Identifier: #{identifier || '(none)'}, Position: #{subscription.position})" }
68
-
69
- starting() if respond_to?(:starting)
70
-
71
- self.class.handler_registry.each do |handler|
72
- logger.info(tag: :*) { "Handler: #{handler.name} (Stream Name: #{stream_name}, Consumer: #{self.class.name})" }
62
+ if not MessageStore::StreamName.category?(category)
63
+ raise Error, "Consumer's stream name must be a category (Stream Name: #{category})"
73
64
  end
74
65
 
75
66
  _, subscription_thread = ::Actor::Start.(subscription)
@@ -82,36 +73,88 @@ module Consumer
82
73
  probe.(self, [actor_thread, subscription_thread], [actor_address, subscription_address])
83
74
  end
84
75
 
85
- logger.info(tag: :*) { "Started consumer: #{self.class.name} (Stream: #{stream_name}, Identifier: #{identifier || '(none)'}, Position: #{subscription.position})" }
76
+ logger.info(tags: [:consumer, :start]) { "Started consumer: #{self.class.name} (Category: #{category}, Identifier: #{identifier || '(none)'}, Position: #{subscription.position})" }
86
77
 
87
78
  AsyncInvocation::Incorrect
88
79
  end
89
80
 
81
+ def print_info
82
+ STDOUT.puts
83
+ STDOUT.puts " Consumer: #{self.class.name}"
84
+ STDOUT.puts " Category: #{category}"
85
+ STDOUT.puts " Position: #{subscription.position}"
86
+ STDOUT.puts " Identifier: #{identifier || '(none)'}"
87
+
88
+ print_startup_info if respond_to?(:print_startup_info)
89
+
90
+ STDOUT.puts " Position Location: #{position_store.location || '(none)'}"
91
+
92
+ STDOUT.puts
93
+
94
+ STDOUT.puts " Handlers:"
95
+ self.class.handler_registry.each do |handler|
96
+ STDOUT.puts " Handler: #{handler.name}"
97
+ STDOUT.puts " Messages: #{handler.message_registry.message_types.join(', ')}"
98
+ end
99
+ end
100
+
101
+ def log_info
102
+ logger.info(tags: [:consumer, :start]) { "Category: #{category} (Consumer: #{self.class.name})" }
103
+ logger.info(tags: [:consumer, :start]) { "Position: #{subscription.position} (Consumer: #{self.class.name})" }
104
+ logger.info(tags: [:consumer, :start]) { "Identifier: #{identifier || 'nil'} (Consumer: #{self.class.name})" }
105
+
106
+ log_startup_info if respond_to?(:log_startup_info)
107
+
108
+ logger.info(tags: [:consumer, :start]) { "Position Update Interval: #{position_update_interval.inspect} (Consumer: #{self.class.name})" }
109
+
110
+ logger.info(tags: [:consumer, :start]) { "Poll Interval Milliseconds: #{poll_interval_milliseconds.inspect} (Consumer: #{self.class.name})" }
111
+
112
+ self.class.handler_registry.each do |handler|
113
+ logger.info(tags: [:consumer, :start]) { "Handler: #{handler.name} (Consumer: #{self.class.name})" }
114
+ logger.info(tags: [:consumer, :start]) { "Messages: #{handler.message_registry.message_types.join(', ')} (Handler: #{handler.name}, Consumer: #{self.class.name})" }
115
+ end
116
+ end
117
+
118
+ def dispatch(message_data)
119
+ logger.trace(tags: [:consumer, :dispatch, :message]) { "Dispatching message (#{LogText.message_data(message_data)})" }
120
+
121
+ self.class.handler_registry.each do |handler|
122
+ handler.(message_data, session: session)
123
+ end
124
+
125
+ update_position(message_data.global_position)
126
+
127
+ logger.debug(tags: [:consumer, :dispatch, :message]) { "Message dispatched (#{LogText.message_data(message_data)})" }
128
+ rescue => error
129
+ logger.error(tag: :*) { "Error raised (Error Class: #{error.class}, Error Message: #{error.message}, #{LogText.message_data(message_data)})" }
130
+ error_raised(error, message_data)
131
+ end
132
+
90
133
  def update_position(position)
91
- logger.trace { "Updating position (Global Position: #{position}, Counter: #{position_update_counter}/#{position_update_interval})" }
134
+ logger.trace(tags: [:consumer, :position]) { "Updating position (Global Position: #{position}, Counter: #{position_update_counter}/#{position_update_interval})" }
92
135
 
93
136
  self.position_update_counter += 1
94
137
 
95
138
  if position_update_counter >= position_update_interval
96
139
  position_store.put(position)
97
140
 
98
- logger.debug { "Updated position (Global Position: #{position}, Counter: #{position_update_counter}/#{position_update_interval})" }
141
+ logger.debug(tags: [:consumer, :position]) { "Updated position (Global Position: #{position}, Counter: #{position_update_counter}/#{position_update_interval})" }
99
142
 
100
143
  self.position_update_counter = 0
101
144
  else
102
- logger.debug { "Interval not reached; position not updated (Global Position: #{position}, Counter: #{position_update_counter}/#{position_update_interval})" }
145
+ logger.debug(tags: [:consumer, :position]) { "Interval not reached; position not updated (Global Position: #{position}, Counter: #{position_update_counter}/#{position_update_interval})" }
103
146
  end
104
147
  end
105
148
 
106
149
  module LogText
107
150
  def self.message_data(message_data)
108
- "Type: #{message_data.type}, Stream: #{message_data.stream_name}, Position: #{message_data.position}, GlobalPosition: #{message_data.global_position}"
151
+ "Type: #{message_data.type}, Stream Name: #{message_data.stream_name}, Position: #{message_data.position}, GlobalPosition: #{message_data.global_position}"
109
152
  end
110
153
  end
111
154
 
112
155
  module Configure
113
156
  def configure(**kwargs)
114
- logger.trace { "Configuring (Stream Name: #{stream_name})" }
157
+ logger.trace(tag: :consumer) { "Configuring (Category: #{category})" }
115
158
 
116
159
  super(**kwargs)
117
160
 
@@ -124,13 +167,13 @@ module Consumer
124
167
  poll_interval_milliseconds: poll_interval_milliseconds
125
168
  )
126
169
 
127
- logger.debug { "Done configuring (Stream Name: #{stream_name}, Starting Position: #{starting_position})" }
170
+ logger.debug(tag: :consumer) { "Done configuring (Category: #{category}, Starting Position: #{starting_position})" }
128
171
  end
129
172
  end
130
173
 
131
174
  module Build
132
- def build(stream_name, position_update_interval: nil, poll_interval_milliseconds: nil, identifier: nil, **arguments)
133
- instance = new(stream_name)
175
+ def build(category, position_update_interval: nil, poll_interval_milliseconds: nil, identifier: nil, **arguments)
176
+ instance = new(category)
134
177
 
135
178
  unless identifier.nil?
136
179
  instance.identifier = identifier
@@ -146,8 +189,8 @@ module Consumer
146
189
  end
147
190
 
148
191
  module Start
149
- def start(stream_name, **arguments, &probe)
150
- instance = build stream_name, **arguments
192
+ def start(category, **arguments, &probe)
193
+ instance = build category, **arguments
151
194
  instance.start(&probe)
152
195
  end
153
196
  end
@@ -180,4 +223,24 @@ module Consumer
180
223
  end
181
224
  end
182
225
  end
226
+
227
+ module Defaults
228
+ def self.startup_info?
229
+ StartupInfo.get == 'on'
230
+ end
231
+
232
+ module StartupInfo
233
+ def self.get
234
+ ENV.fetch(env_var, default)
235
+ end
236
+
237
+ def self.env_var
238
+ 'STARTUP_INFO'
239
+ end
240
+
241
+ def self.default
242
+ 'on'
243
+ end
244
+ end
245
+ end
183
246
  end
@@ -1,12 +1,12 @@
1
1
  module Consumer
2
2
  module Controls
3
3
  module Consumer
4
- def self.example(stream_name=nil, identifier: nil, handlers: nil)
5
- stream_name ||= StreamName.example
4
+ def self.example(category=nil, identifier: nil, handlers: nil)
5
+ category ||= Category.example
6
6
 
7
7
  cls = example_class(identifier: identifier, handlers: handlers)
8
8
 
9
- cls.new(stream_name)
9
+ cls.new(category)
10
10
  end
11
11
 
12
12
  def self.example_class(identifier: nil, handlers: nil)
@@ -2,10 +2,10 @@ module Consumer
2
2
  module Controls
3
3
  module Consumer
4
4
  module ErrorHandler
5
- def self.example(stream_name=nil)
6
- stream_name ||= StreamName.example
5
+ def self.example(category=nil)
6
+ category ||= Category.example
7
7
 
8
- Example.new(stream_name)
8
+ Example.new(category)
9
9
  end
10
10
 
11
11
  class Example
@@ -15,7 +15,7 @@ module Consumer
15
15
  attr_accessor :failed_message
16
16
 
17
17
  handler Handle::Example
18
- handler Handle::RaiseError
18
+ handler Handle::RaiseError::Example
19
19
 
20
20
  def error_raised(error, message)
21
21
  self.handled_error = error
@@ -32,7 +32,7 @@ module Consumer
32
32
  sleep_duration = ENV['SLEEP_DURATION'] || 100
33
33
  sleep_duration = sleep_duration.to_i
34
34
 
35
- Get::Incrementing.configure(self, stream_name, sleep_duration)
35
+ Get::Incrementing.configure(self, sleep_duration)
36
36
 
37
37
  PositionStore::File.configure(self, identifier: identifier)
38
38
  end
@@ -7,16 +7,23 @@ module Consumer
7
7
 
8
8
  configure :get
9
9
 
10
- initializer :stream_name, :frequency_milliseconds
10
+ def frequency_milliseconds
11
+ @frequency_milliseconds ||= Defaults.frequency_milliseconds
12
+ end
13
+ attr_writer :frequency_milliseconds
11
14
 
12
15
  def frequency_seconds
13
16
  frequency_milliseconds.to_f / 1000
14
17
  end
15
18
 
16
- def self.build(stream_name, frequency_milliseconds=nil)
17
- frequency_milliseconds ||= Defaults.frequency_milliseconds
19
+ def self.build(frequency_milliseconds=nil)
20
+ instance = new
21
+ instance.frequency_milliseconds = frequency_milliseconds
22
+ instance
23
+ end
18
24
 
19
- new(stream_name, frequency_milliseconds)
25
+ def category
26
+ Category.example
20
27
  end
21
28
 
22
29
  def batch_size
@@ -30,7 +37,6 @@ module Consumer
30
37
 
31
38
  batch_size.times.map do |offset|
32
39
  MessageData.example(
33
- stream_name,
34
40
  position + offset,
35
41
  offset
36
42
  )
@@ -48,14 +54,13 @@ module Consumer
48
54
  end
49
55
 
50
56
  class MessageData
51
- def self.example(stream_name, global_position, position)
57
+ def self.example(global_position, position)
52
58
  data = {
53
59
  :position => position,
54
60
  :global_position => global_position
55
61
  }
56
62
 
57
63
  Controls::MessageData.example(
58
- stream_name: stream_name,
59
64
  data: data,
60
65
  global_position: global_position,
61
66
  position: position
@@ -1,13 +1,11 @@
1
1
  module Consumer
2
2
  module Controls
3
3
  module MessageData
4
- def self.example(stream_name: nil, data: nil, position: nil, global_position: nil)
4
+ def self.example(data: nil, position: nil, global_position: nil)
5
5
  global_position ||= position
6
6
 
7
7
  message_data = MessageStore::Controls::MessageData::Read.example(data: data)
8
8
 
9
- message_data.stream_name = stream_name unless stream_name.nil?
10
-
11
9
  message_data.position = position unless position.nil?
12
10
  message_data.global_position = global_position unless global_position.nil?
13
11
 
@@ -1,20 +1,35 @@
1
1
  module Consumer
2
2
  module Controls
3
3
  module PositionStore
4
- def self.example
5
- Example.build
4
+ def self.example(&block)
5
+ if block.nil?
6
+ cls = Example
7
+ else
8
+ cls = example_class(&block)
9
+ end
10
+
11
+ cls.build
6
12
  end
7
13
 
8
- class Example
9
- include ::Consumer::PositionStore
14
+ def self.example_class(&block)
15
+ Class.new do
16
+ include ::Consumer::PositionStore
10
17
 
11
- attr_accessor :telemetry_sink
18
+ def self.build
19
+ instance = new
20
+ instance.configure
21
+ instance
22
+ end
12
23
 
13
- def self.build
14
- instance = new
15
- instance.configure
16
- instance
24
+ def configure
25
+ end
26
+
27
+ class_exec(&block) unless block.nil?
17
28
  end
29
+ end
30
+
31
+ Example = example_class do
32
+ attr_accessor :telemetry_sink
18
33
 
19
34
  def configure
20
35
  self.telemetry_sink = ::Consumer::PositionStore::Telemetry::Sink.new
@@ -29,6 +44,12 @@ module Consumer
29
44
  def put(_)
30
45
  end
31
46
  end
47
+
48
+ module Location
49
+ def self.example
50
+ 'somePositionStream'
51
+ end
52
+ end
32
53
  end
33
54
  end
34
55
  end
@@ -1,3 +1,4 @@
1
+ ## Consider whether this is still needed after category rename
1
2
  module Consumer
2
3
  module Controls
3
4
  StreamName = Messaging::Controls::StreamName
@@ -1,8 +1,8 @@
1
1
  module Consumer
2
2
  module Controls
3
3
  module Subscription
4
- def self.example(stream_name: nil, next_batch: nil, position: nil, batch_size: nil, count: nil)
5
- get = Get.example(stream_name: stream_name, batch_size: batch_size, count: count)
4
+ def self.example(category: nil, next_batch: nil, position: nil, batch_size: nil, count: nil)
5
+ get = Get.example(stream_name: category, batch_size: batch_size, count: count)
6
6
 
7
7
  subscription = ::Consumer::Subscription.new(get)
8
8
 
@@ -3,6 +3,7 @@ module Consumer
3
3
  def self.included(cls)
4
4
  cls.class_exec do
5
5
  include Dependency
6
+ include Virtual
6
7
  include Log::Dependency
7
8
 
8
9
  extend Build
@@ -13,6 +14,8 @@ module Consumer
13
14
  prepend Put
14
15
 
15
16
  dependency :telemetry, ::Telemetry
17
+
18
+ virtual :location
16
19
  end
17
20
  end
18
21
 
@@ -60,7 +63,7 @@ module Consumer
60
63
 
61
64
  position = super
62
65
 
63
- logger.info(tags: [:position_store, :get]) { "Get position done (Position: #{position || '(none)'})" }
66
+ logger.debug(tags: [:position_store, :get]) { "Get position done (Position: #{position || '(none)'})" }
64
67
 
65
68
  telemetry.record(:get, Telemetry::Get.new(position))
66
69
 
@@ -46,20 +46,20 @@ module Consumer
46
46
  end
47
47
 
48
48
  handle :resupply do
49
- logger.trace { "Resupplying (Stream Name: #{get.stream_name}, Position: #{position})" }
49
+ logger.trace { "Resupplying (Category: #{get.category}, Position: #{position})" }
50
50
 
51
51
  batch = poll.() do
52
52
  get.(position)
53
53
  end
54
54
 
55
55
  if batch.nil? || batch.empty?
56
- logger.debug { "Did not resupply; no events available (Stream Name: #{get.stream_name}, Position: #{position})" }
56
+ logger.debug { "Did not resupply; no events available (Stream: #{get.stream_name}, Position: #{position})" }
57
57
 
58
58
  :resupply
59
59
  else
60
60
  self.next_batch = batch
61
61
 
62
- logger.debug { "Resupplied (Stream Name: #{get.stream_name}, Position: #{position}, Batch Size: #{batch.count})" }
62
+ logger.debug { "Resupplied (Category: #{get.category}, Position: #{position}, Batch Size: #{batch.count})" }
63
63
  end
64
64
  end
65
65
 
@@ -2,6 +2,9 @@ module Consumer
2
2
  class Subscription
3
3
  module Defaults
4
4
  def self.poll_interval_milliseconds
5
+ env_interval = ENV['POLL_INTERVAL_MILLISECONDS']
6
+ return env_interval.to_i if !env_interval.nil?
7
+
5
8
  100
6
9
  end
7
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: evt-consumer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0.0
4
+ version: 2.3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - The Eventide Project
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-10-02 00:00:00.000000000 Z
11
+ date: 2020-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ntl-actor
@@ -155,8 +155,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
155
155
  - !ruby/object:Gem::Version
156
156
  version: '0'
157
157
  requirements: []
158
- rubygems_version: 3.0.1
158
+ rubygems_version: 3.1.2
159
159
  signing_key:
160
160
  specification_version: 4
161
- summary: Continuous subscription to a stream and message dispatching to handlers
161
+ summary: Continuous subscription to a category and message dispatching to handlers
162
162
  test_files: []