eventhub-processor2 1.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.
@@ -0,0 +1,158 @@
1
+ # EventHub module
2
+ module EventHub
3
+ # Message class
4
+ class Message
5
+ include Helper
6
+
7
+ VERSION = '1.0.0'.freeze
8
+
9
+ # Headers that are required (value can be nil) in order to pass valid?
10
+ REQUIRED_HEADERS = [
11
+ 'message_id',
12
+ 'version',
13
+ 'created_at',
14
+ 'origin.module_id',
15
+ 'origin.type',
16
+ 'origin.site_id',
17
+ 'process.name',
18
+ 'process.step_position',
19
+ 'process.execution_id',
20
+ 'status.retried_count',
21
+ 'status.code',
22
+ 'status.message'
23
+ ].freeze
24
+
25
+ attr_accessor :header, :body, :raw, :vhost, :routing_key
26
+
27
+ # Build accessors for all required headers
28
+ REQUIRED_HEADERS.each do |header|
29
+ name = header.tr('.', '_')
30
+
31
+ define_method(name) do
32
+ self.header.get(header)
33
+ end
34
+
35
+ define_method("#{name}=") do |value|
36
+ self.header.set(header, value)
37
+ end
38
+ end
39
+
40
+ def self.from_json(raw)
41
+ data = JSON.parse(raw)
42
+ Message.new(data.get('header'), data.get('body'), raw)
43
+ rescue => e
44
+ Message.new(
45
+ {
46
+ 'status' =>
47
+ {
48
+ 'code' => STATUS_INVALID,
49
+ 'message' => "JSON parse error: #{e}"
50
+ }
51
+ },
52
+ {
53
+ 'original_message_base64_encoded' => Base64.encode64(raw)
54
+ },
55
+ raw
56
+ )
57
+ end
58
+
59
+ def initialize(header = nil, body = nil, raw = nil)
60
+ @header = header || {}
61
+ @body = body || {}
62
+ @raw = raw
63
+
64
+ # set message defaults, that we have required headers
65
+ @header.set('message_id', UUIDTools::UUID.timestamp_create.to_s, false)
66
+ @header.set('version', VERSION, false)
67
+ @header.set('created_at', now_stamp, false)
68
+
69
+ @header.set('origin.module_id', 'undefined', false)
70
+ @header.set('origin.type', 'undefined', false)
71
+ @header.set('origin.site_id', 'undefined', false)
72
+
73
+ @header.set('process.name', 'undefined', false)
74
+ @header.set('process.execution_id',
75
+ UUIDTools::UUID.timestamp_create.to_s, false)
76
+ @header.set('process.step_position', 0, false)
77
+
78
+ @header.set('status.retried_count', 0, false)
79
+ @header.set('status.code', STATUS_INITIAL, false)
80
+ @header.set('status.message', '', false)
81
+ end
82
+
83
+ def valid?
84
+ # check for existence and defined value
85
+ REQUIRED_HEADERS.all? do |key|
86
+ @header.all_keys_with_path.include?(key) &&
87
+ !send(key.tr('.', '_').to_sym).nil?
88
+ end
89
+ end
90
+
91
+ def success?
92
+ status_code == STATUS_SUCCESS
93
+ end
94
+
95
+ def retry?
96
+ status_code == STATUS_RETRY
97
+ end
98
+
99
+ def initial?
100
+ status_code == STATUS_INITIAL
101
+ end
102
+
103
+ def retry_pending?
104
+ status_code == STATUS_RETRY_PENDING
105
+ end
106
+
107
+ def invalid?
108
+ status_code == STATUS_INVALID
109
+ end
110
+
111
+ def schedule?
112
+ status_code == STATUS_SCHEDULE
113
+ end
114
+
115
+ def schedule_retry?
116
+ status_code == STATUS_SCHEDULE_RETRY
117
+ end
118
+
119
+ def schedule_pending?
120
+ status_code == STATUS_SCHEDULE_PENDING
121
+ end
122
+
123
+ def to_json
124
+ { 'header' => header, 'body' => body }.to_json
125
+ end
126
+
127
+ def to_s
128
+ 'Msg: process '\
129
+ "[#{process_name}, #{process_step_position}, #{process_execution_id}]"\
130
+ ", status [#{status_code},#{status_message},#{status_retried_count}]"
131
+ end
132
+
133
+ # copies the message and set's provided status code (default: success),
134
+ # actual stamp, and a new message id
135
+ def copy(status_code = STATUS_SUCCESS)
136
+ # use Marshal dump and load to make a deep object copy
137
+ copied_header = Marshal.load(Marshal.dump(header))
138
+ copied_body = Marshal.load(Marshal.dump(body))
139
+
140
+ copied_header.set('message_id', UUIDTools::UUID.timestamp_create.to_s)
141
+ copied_header.set('created_at', now_stamp)
142
+ copied_header.set('status.code', status_code)
143
+
144
+ Message.new(copied_header, copied_body)
145
+ end
146
+
147
+ def append_to_execution_history(processor_name)
148
+ header.set('execution_history', []) unless \
149
+ header.get('execution_history')
150
+ header.get('execution_history') << \
151
+ { 'processor' => processor_name, 'timestamp' => now_stamp }
152
+ end
153
+
154
+ def self.translate_status_code(code)
155
+ STATUS_CODE_TRANSLATION[code]
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,121 @@
1
+ # EventHub module
2
+ module EventHub
3
+ # Processor2 class
4
+ class Processor2
5
+ include Helper
6
+
7
+ SIGNALS_FOR_TERMINATION = [:INT, :TERM, :QUIT]
8
+ SIGNALS_FOR_RELOAD_CONFIG = [:HUP]
9
+ ALL_SIGNALS = SIGNALS_FOR_TERMINATION + SIGNALS_FOR_RELOAD_CONFIG
10
+
11
+ attr_reader :started_at, :statistics
12
+
13
+ def initialize(args = {})
14
+ # Set processor name
15
+ EventHub::Configuration.name = args[:name] ||
16
+ get_name_from_class(self)
17
+
18
+ # Parse comand line options
19
+ EventHub::Configuration.parse_options
20
+
21
+ # Load configuration file
22
+ EventHub::Configuration.load!(args)
23
+
24
+ @command_queue = []
25
+
26
+ @started_at = Time.now
27
+ @statistics = EventHub::Statistics.new
28
+ end
29
+
30
+ def start
31
+ EventHub.logger.info("#{Configuration.name} (#{version}): has been started")
32
+
33
+ before_start
34
+ main_event_loop
35
+ after_stop
36
+
37
+ EventHub.logger.info("#{Configuration.name} (#{version}): has been stopped")
38
+ rescue => ex
39
+ EventHub.logger.error("Unexpected error in Processor2.start: #{ex}")
40
+ end
41
+
42
+ def stop
43
+ # used by rspec
44
+ @command_queue << :TERM
45
+ end
46
+
47
+ def version
48
+ EventHub::VERSION
49
+ end
50
+
51
+ # get message as EventHub::Message class instance
52
+ # args contain :queue_name, :content_type, :priority, :delivery_tag
53
+ def handle_message(_message, _args = {})
54
+ raise 'need to be implemented in derived class'
55
+ end
56
+
57
+ # pass message: '{ "header": ... , "body": { .. }}'
58
+ # and optionally exchange_name: 'your exchange name'
59
+ def publish(args = {})
60
+ Celluloid::Actor[:actor_listener].publish(args)
61
+ end
62
+
63
+ def before_start
64
+ # can be implemented in derived class
65
+ end
66
+
67
+ def after_stop
68
+ # can be implemented in derived class
69
+ end
70
+
71
+ private
72
+
73
+ def setup_signal_handler
74
+ # have a re-entrant signal handler by just using a simple array
75
+ # https://www.sitepoint.com/the-self-pipe-trick-explained/
76
+ ALL_SIGNALS.each do |signal|
77
+ Signal.trap(signal) { @command_queue << signal }
78
+ end
79
+ end
80
+
81
+ def start_supervisor
82
+ @config = Celluloid::Supervision::Configuration.define([
83
+ {type: ActorHeartbeat, as: :actor_heartbeat, args: [ self ]},
84
+ {type: ActorListener, as: :actor_listener, args: [ self ]}
85
+ ])
86
+
87
+ @config.injection!(:before_restart, proc do
88
+ EventHub.logger.info('Restarting in 10 seconds...')
89
+ sleep 10
90
+ end )
91
+
92
+ @config.deploy
93
+ end
94
+
95
+ def main_event_loop
96
+ setup_signal_handler
97
+ start_supervisor
98
+
99
+ loop do
100
+ command = @command_queue.pop
101
+ case
102
+ when SIGNALS_FOR_TERMINATION.include?(command)
103
+ EventHub.logger.info("Command [#{command}] received")
104
+ break
105
+ when SIGNALS_FOR_RELOAD_CONFIG.include?(command)
106
+ EventHub::Configuration.load!
107
+ EventHub.logger.info('Configuration file reloaded')
108
+ Celluloid::Actor[:actor_listener].async.restart
109
+ else
110
+ sleep 0.5
111
+ end
112
+ end
113
+
114
+ Celluloid.shutdown
115
+ # make sure all actors are gone
116
+ while Celluloid.running?
117
+ sleep 0.1
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,53 @@
1
+ class EventHub::Statistics
2
+ attr_reader :messages_successful, :messages_unsuccessful, :messages_average_size, :messages_average_process_time
3
+
4
+ def initialize
5
+ @messages_successful = 0
6
+ @messages_unsuccessful = 0
7
+ @messages_average_size = 0
8
+ @messages_average_process_time = 0
9
+ @messages_total_process_time = 0
10
+ @mutex = Mutex.new
11
+ end
12
+
13
+
14
+ def measure(size, &block)
15
+ begin
16
+ start = Time.now
17
+ yield
18
+ success(Time.now - start, size)
19
+ rescue
20
+ failure
21
+ raise
22
+ end
23
+ end
24
+
25
+ def success(process_time, size)
26
+ @mutex.lock
27
+ @messages_total_process_time += process_time
28
+ @messages_average_process_time = (messages_total_process_time + process_time) / (messages_successful + 1).to_f
29
+ @messages_average_size = (messages_total_size + size) / (messages_successful + 1).to_f
30
+ @messages_successful += 1
31
+ ensure
32
+ @mutex.unlock
33
+ end
34
+
35
+ def failure
36
+ @mutex.lock
37
+ @messages_unsuccessful += 1
38
+ ensure
39
+ @mutex.unlock
40
+ end
41
+
42
+ def messages_total
43
+ messages_unsuccessful + messages_successful
44
+ end
45
+
46
+ def messages_total_process_time
47
+ messages_average_process_time * messages_successful
48
+ end
49
+
50
+ def messages_total_size
51
+ messages_average_size * messages_successful
52
+ end
53
+ end
@@ -0,0 +1,3 @@
1
+ module EventHub
2
+ VERSION = '1.0.0'.freeze
3
+ end
data/lib/eventhub.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'uuidtools'
2
+ require 'json'
3
+ require 'base64'
4
+
5
+ require 'eventhub/components'
6
+ require 'logstash-logger'
7
+ require 'bunny'
8
+ require 'celluloid/current'
9
+
10
+ require_relative 'eventhub/version'
11
+ require_relative 'eventhub/constant'
12
+ require_relative 'eventhub/logger'
13
+ require_relative 'eventhub/helper'
14
+ require_relative 'eventhub/hash_extensions'
15
+ require_relative 'eventhub/configuration'
16
+ require_relative 'eventhub/message'
17
+ require_relative 'eventhub/statistics'
18
+ require_relative 'eventhub/consumer'
19
+ require_relative 'eventhub/actor_heartbeat'
20
+ require_relative 'eventhub/actor_watchdog'
21
+ require_relative 'eventhub/actor_listener'
22
+ require_relative 'eventhub/processor2'
23
+
24
+ Celluloid.logger = nil
25
+ Celluloid.exception_handler { |ex| EventHub.logger.info "Exception occured: #{ex}" }
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eventhub-processor2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Steiner, Thomas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: celluloid
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.17'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.17'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bunny
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.7'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ - !ruby/object:Gem::Dependency
42
+ name: eventhub-components
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.2'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: uuidtools
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.16'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.16'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '12.2'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '12.2'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.15'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.15'
125
+ description: Next generation gem to build ruby based eventhub processor
126
+ email:
127
+ - thomas.steiner@ikey.ch
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - Gemfile.lock
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - docker/Dockerfile
143
+ - docker/README.md
144
+ - docker/definitions.json
145
+ - docker/rabbitmq.config
146
+ - eventhub-processor2.gemspec
147
+ - example/README.md
148
+ - example/config/example.json
149
+ - example/crasher.rb
150
+ - example/example.rb
151
+ - example/publisher.rb
152
+ - lib/eventhub.rb
153
+ - lib/eventhub/actor_heartbeat.rb
154
+ - lib/eventhub/actor_listener.rb
155
+ - lib/eventhub/actor_watchdog.rb
156
+ - lib/eventhub/configuration.rb
157
+ - lib/eventhub/constant.rb
158
+ - lib/eventhub/consumer.rb
159
+ - lib/eventhub/hash_extensions.rb
160
+ - lib/eventhub/helper.rb
161
+ - lib/eventhub/logger.rb
162
+ - lib/eventhub/message.rb
163
+ - lib/eventhub/processor2.rb
164
+ - lib/eventhub/statistics.rb
165
+ - lib/eventhub/version.rb
166
+ homepage: https://github.com/thomis/eventhub-processor2
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.6.12
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Next generation gem to build ruby based eventhub processor
190
+ test_files: []