hawkei 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rubocop.yml +48 -0
- data/.ruby-version +1 -0
- data/.tool-versions +1 -0
- data/.travis.yml +11 -0
- data/Gemfile +12 -0
- data/LICENCE +21 -0
- data/Makefile +9 -0
- data/README.md +17 -0
- data/Rakefile +4 -0
- data/hawkei.gemspec +28 -0
- data/lib/hawkei/api_operation/delete.rb +38 -0
- data/lib/hawkei/api_operation/save.rb +57 -0
- data/lib/hawkei/api_resource.rb +130 -0
- data/lib/hawkei/batch.rb +18 -0
- data/lib/hawkei/config.rb +123 -0
- data/lib/hawkei/errors.rb +41 -0
- data/lib/hawkei/formated_logger.rb +45 -0
- data/lib/hawkei/hawkei_object.rb +179 -0
- data/lib/hawkei/library_name.rb +3 -0
- data/lib/hawkei/message.rb +79 -0
- data/lib/hawkei/plugins/rack/middleware.rb +139 -0
- data/lib/hawkei/plugins/rails/data.rb +19 -0
- data/lib/hawkei/plugins/rails/middleware_data.rb +28 -0
- data/lib/hawkei/plugins/rails/railtie.rb +23 -0
- data/lib/hawkei/plugins/sidekiq/client_middleware.rb +19 -0
- data/lib/hawkei/plugins/sidekiq/load.rb +14 -0
- data/lib/hawkei/plugins/sidekiq/server_middleware.rb +48 -0
- data/lib/hawkei/plugins.rb +17 -0
- data/lib/hawkei/processor/async.rb +50 -0
- data/lib/hawkei/processor/batch.rb +84 -0
- data/lib/hawkei/processor/worker.rb +113 -0
- data/lib/hawkei/request.rb +134 -0
- data/lib/hawkei/store.rb +49 -0
- data/lib/hawkei/util.rb +180 -0
- data/lib/hawkei/version.rb +3 -0
- data/lib/hawkei/watcher.rb +15 -0
- data/lib/hawkei.rb +170 -0
- data/spec/lib/hawkei/api_resource_spec.rb +109 -0
- data/spec/lib/hawkei/batch_spec.rb +14 -0
- data/spec/lib/hawkei/config_spec.rb +36 -0
- data/spec/lib/hawkei/formated_logger_spec.rb +99 -0
- data/spec/lib/hawkei/hawkei_object_spec.rb +123 -0
- data/spec/lib/hawkei/message_spec.rb +178 -0
- data/spec/lib/hawkei/plugins/rack/middleware_spec.rb +88 -0
- data/spec/lib/hawkei/plugins/rails/data_spec.rb +22 -0
- data/spec/lib/hawkei/plugins/rails/middleware_data_spec.rb +46 -0
- data/spec/lib/hawkei/plugins/sidekiq/client_middleware_spec.rb +15 -0
- data/spec/lib/hawkei/plugins/sidekiq/server_middleware_spec.rb +58 -0
- data/spec/lib/hawkei/processor/async_spec.rb +36 -0
- data/spec/lib/hawkei/processor/batch_spec.rb +51 -0
- data/spec/lib/hawkei/processor/worker_spec.rb +100 -0
- data/spec/lib/hawkei/store_spec.rb +82 -0
- data/spec/lib/hawkei/util_spec.rb +132 -0
- data/spec/lib/hawkei/watcher_spec.rb +25 -0
- data/spec/lib/hawkei_spec.rb +175 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/support/rack_app.rb +12 -0
- metadata +206 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
module Hawkei
|
2
|
+
##
|
3
|
+
# == Hawkei \Object
|
4
|
+
#
|
5
|
+
# Define the API objects
|
6
|
+
class HawkeiObject
|
7
|
+
##
|
8
|
+
# @return [Hash] JSON parsed response
|
9
|
+
attr_reader :raw
|
10
|
+
|
11
|
+
##
|
12
|
+
# @return [Hash] JSON parsed response
|
13
|
+
attr_reader :data
|
14
|
+
|
15
|
+
attr_reader :errors
|
16
|
+
|
17
|
+
attr_reader :successful
|
18
|
+
alias successful? successful
|
19
|
+
|
20
|
+
class << self
|
21
|
+
|
22
|
+
##
|
23
|
+
# Initialize from the API response
|
24
|
+
#
|
25
|
+
# @return [Hawkei::HawkeiObject]
|
26
|
+
def initialize_from(response, object = new)
|
27
|
+
object.load_response_api(response.is_a?(Hash) ? response : Util.safe_json_parse(response))
|
28
|
+
object.update_attributes(Hawkei::Util.except_keys(object.raw, :data))
|
29
|
+
if object.raw[:object] == 'list'
|
30
|
+
object.raw[:data].each do |response_object|
|
31
|
+
data = object.type_from_string_object(response_object[:object]).initialize_from(response_object)
|
32
|
+
|
33
|
+
object.add_data(data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
object
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
##
|
43
|
+
# Update the object based on the response from the API
|
44
|
+
# Remove and new accessor
|
45
|
+
#
|
46
|
+
# @param [Hash] response
|
47
|
+
#
|
48
|
+
# @return [Hawkei::HawkeiObject]
|
49
|
+
def update_from(response)
|
50
|
+
self.class.initialize_from(response, self)
|
51
|
+
|
52
|
+
(@values.keys - raw.keys).each { |key| remove_accessor(key) }
|
53
|
+
|
54
|
+
self
|
55
|
+
end
|
56
|
+
|
57
|
+
##
|
58
|
+
# Initialize and create accessor for values
|
59
|
+
#
|
60
|
+
# @params [Hash] values
|
61
|
+
def initialize(values = {})
|
62
|
+
@data = []
|
63
|
+
@values = {}
|
64
|
+
|
65
|
+
update_attributes(values)
|
66
|
+
end
|
67
|
+
|
68
|
+
##
|
69
|
+
# get attribute value
|
70
|
+
def [](key)
|
71
|
+
@values[key.to_sym]
|
72
|
+
end
|
73
|
+
|
74
|
+
##
|
75
|
+
# set attribute value
|
76
|
+
def []=(key, value)
|
77
|
+
send(:"#{key}=", value)
|
78
|
+
end
|
79
|
+
|
80
|
+
##
|
81
|
+
# @return [Array] all the keys
|
82
|
+
def keys
|
83
|
+
@values.keys
|
84
|
+
end
|
85
|
+
|
86
|
+
##
|
87
|
+
# @return [Hash] values to hash
|
88
|
+
def to_hash
|
89
|
+
@values
|
90
|
+
end
|
91
|
+
|
92
|
+
##
|
93
|
+
# @return [JSON] values to JSON
|
94
|
+
def to_json(_object = nil)
|
95
|
+
JSON.generate(@values)
|
96
|
+
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Update the attribute and add accessor for new attributes
|
100
|
+
#
|
101
|
+
# @param [Hash] values
|
102
|
+
def update_attributes(attributes)
|
103
|
+
attributes.each do |(key, value)|
|
104
|
+
add_accessor(key, value)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Load the root components for the API response
|
110
|
+
#
|
111
|
+
# @param [Hash] values
|
112
|
+
def load_response_api(response)
|
113
|
+
@raw = Hawkei::Util.deep_underscore_key(response)
|
114
|
+
@successful = true
|
115
|
+
end
|
116
|
+
|
117
|
+
##
|
118
|
+
# Add data for sub-object
|
119
|
+
#
|
120
|
+
# @param [Object] data
|
121
|
+
def add_data(data)
|
122
|
+
@data << data
|
123
|
+
end
|
124
|
+
|
125
|
+
##
|
126
|
+
# Create an object from a string
|
127
|
+
#
|
128
|
+
# @param [String] object to be build
|
129
|
+
#
|
130
|
+
# @return [Object]
|
131
|
+
def type_from_string_object(string_object)
|
132
|
+
klass_name = Hawkei::Util.camelize(string_object.to_s)
|
133
|
+
|
134
|
+
Object.const_get("Hawkei::#{klass_name}")
|
135
|
+
end
|
136
|
+
|
137
|
+
def inspect
|
138
|
+
id_string = respond_to?(:id) && !id.nil? ? " id=#{id}" : ''
|
139
|
+
"#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " + JSON.pretty_generate(@values)
|
140
|
+
end
|
141
|
+
|
142
|
+
def method_missing(name, *args)
|
143
|
+
super unless name.to_s.end_with?('=')
|
144
|
+
|
145
|
+
attribute = name.to_s[0...-1].to_sym
|
146
|
+
value = args.first
|
147
|
+
|
148
|
+
add_accessor(attribute, value)
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def add_accessors(keys, payload = raw)
|
154
|
+
keys.each do |key|
|
155
|
+
add_accessor(key, payload[key])
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def add_accessor(name, value)
|
160
|
+
@values[name] = value
|
161
|
+
|
162
|
+
define_singleton_method(name) { @values[name] }
|
163
|
+
define_singleton_method(:"#{name}=") do |v|
|
164
|
+
@values[name] = v
|
165
|
+
end
|
166
|
+
|
167
|
+
define_singleton_method(:"#{name}?") { value } if [FalseClass, TrueClass].include?(value.class)
|
168
|
+
end
|
169
|
+
|
170
|
+
def remove_accessor(name)
|
171
|
+
@values.delete(name)
|
172
|
+
|
173
|
+
singleton_class.class_eval { remove_method name.to_sym } if singleton_methods.include?(name.to_sym)
|
174
|
+
singleton_class.class_eval { remove_method "#{name}=".to_sym } if singleton_methods.include?("#{name}=".to_sym)
|
175
|
+
singleton_class.class_eval { remove_method "#{name}?".to_sym } if singleton_methods.include?("#{name}?".to_sym)
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Hawkei
|
2
|
+
##
|
3
|
+
# == Hawkei \Message
|
4
|
+
#
|
5
|
+
# Return the base message for an event
|
6
|
+
class Message
|
7
|
+
class << self
|
8
|
+
|
9
|
+
def base
|
10
|
+
Util.deep_compact(
|
11
|
+
message_id: SecureRandom.uuid,
|
12
|
+
timestamp: Time.now.utc.iso8601(3),
|
13
|
+
session_tracker_id: session_tracker_id,
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def extended
|
18
|
+
Util.deep_compact(
|
19
|
+
library: library,
|
20
|
+
server: server,
|
21
|
+
request: Hawkei::Store.get(:request),
|
22
|
+
worker: Hawkei::Store.get(:worker),
|
23
|
+
environment: Hawkei::Store.get(:environment) || Hawkei.configurations.environment_name,
|
24
|
+
metadata: metadata
|
25
|
+
).merge(base)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def session_tracker_id
|
31
|
+
return Hawkei::Store.get(:session_tracker_id) if Hawkei::Store.exist?(:session_tracker_id)
|
32
|
+
|
33
|
+
Hawkei::Store.set(:session_tracker_id, SecureRandom.uuid)
|
34
|
+
end
|
35
|
+
|
36
|
+
def library
|
37
|
+
{
|
38
|
+
name: Hawkei::LIBRARY_NAME,
|
39
|
+
language: 'ruby',
|
40
|
+
version: Hawkei::VERSION,
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def server
|
45
|
+
{
|
46
|
+
host: Socket.gethostname,
|
47
|
+
pid: Process.pid,
|
48
|
+
software: Hawkei::Store.get(:server_software) || program_name,
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def program_name
|
53
|
+
name = $PROGRAM_NAME
|
54
|
+
|
55
|
+
name.split('/').last
|
56
|
+
rescue StandardError => _e
|
57
|
+
name
|
58
|
+
end
|
59
|
+
|
60
|
+
def metadata
|
61
|
+
return nil if Hawkei.configurations.metadata.nil?
|
62
|
+
|
63
|
+
Hawkei.configurations.metadata.each_with_object({}) do |(key, value), hash|
|
64
|
+
if value.is_a?(Proc)
|
65
|
+
begin
|
66
|
+
hash[key] = value.call
|
67
|
+
rescue StandardError => _e
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
next
|
71
|
+
end
|
72
|
+
|
73
|
+
hash[key] = value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Hawkei
|
4
|
+
module Plugins
|
5
|
+
module Rack
|
6
|
+
##
|
7
|
+
# Hawkei \Plugins \Rack \Middleware
|
8
|
+
#
|
9
|
+
# Middleware for Rack
|
10
|
+
#
|
11
|
+
class Middleware
|
12
|
+
|
13
|
+
def initialize(app)
|
14
|
+
@app = app
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
request = ::Rack::Request.new(env)
|
19
|
+
|
20
|
+
store_tracker(request)
|
21
|
+
store_request_data(request, env)
|
22
|
+
|
23
|
+
Hawkei::Plugins::Rails::MiddlewareData.store_data(request) if defined?(::Rails)
|
24
|
+
status, headers, body = @app.call(env)
|
25
|
+
|
26
|
+
write_cookie_session_tracker_id!(headers)
|
27
|
+
[status, headers, body]
|
28
|
+
ensure
|
29
|
+
Hawkei::Store.clear! unless env['hawkei_test']
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def session_tracker_id_name
|
35
|
+
'_hawkei_stid'.freeze
|
36
|
+
end
|
37
|
+
|
38
|
+
def store_tracker(request)
|
39
|
+
Hawkei::Store.set(
|
40
|
+
:session_tracker_id,
|
41
|
+
request.cookies[session_tracker_id_name] || SecureRandom.uuid
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def store_request_data(request, env)
|
46
|
+
Hawkei::Store.set(
|
47
|
+
:request,
|
48
|
+
url: obfuscate_uri(request.url),
|
49
|
+
ssl: request.ssl?,
|
50
|
+
host: request.host,
|
51
|
+
port: request.port,
|
52
|
+
path: request.path,
|
53
|
+
referrer: obfuscate_uri(request.referrer),
|
54
|
+
method: request.request_method,
|
55
|
+
xhr: request.xhr?,
|
56
|
+
user_agent: request.user_agent,
|
57
|
+
ip: request.ip,
|
58
|
+
get_params: obfuscation_get_params(request, 'GET'),
|
59
|
+
post_params: obfuscation_get_params(request, 'POST'),
|
60
|
+
headers: obfuscate_headers(env)
|
61
|
+
)
|
62
|
+
|
63
|
+
Hawkei::Store.bulk_set(
|
64
|
+
server_software: request.env['SERVER_SOFTWARE']
|
65
|
+
)
|
66
|
+
end
|
67
|
+
|
68
|
+
def write_cookie_session_tracker_id!(headers)
|
69
|
+
::Rack::Utils.set_cookie_header!(
|
70
|
+
headers || {},
|
71
|
+
session_tracker_id_name,
|
72
|
+
Util.deep_compact(
|
73
|
+
value: Hawkei::Store.get(:session_tracker_id),
|
74
|
+
path: '/',
|
75
|
+
domain: Hawkei.configurations.domain,
|
76
|
+
http_only: false,
|
77
|
+
max_age: (10 * 365 * 24 * 60 * 60),
|
78
|
+
)
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def obfuscate_uri(url)
|
83
|
+
uri = URI.parse(url)
|
84
|
+
|
85
|
+
params =
|
86
|
+
Util.deep_obfuscate_value(
|
87
|
+
::Rack::Utils.parse_query(uri.query),
|
88
|
+
Hawkei.configurations.obfuscated_fields,
|
89
|
+
'HIDDEN'
|
90
|
+
)
|
91
|
+
|
92
|
+
return url if params.empty?
|
93
|
+
|
94
|
+
uri.merge(
|
95
|
+
"?#{::Rack::Utils.build_query(params)}"
|
96
|
+
).to_s
|
97
|
+
rescue StandardError => _e
|
98
|
+
''
|
99
|
+
end
|
100
|
+
|
101
|
+
def obfuscation_get_params(request, type)
|
102
|
+
Util.deep_obfuscate_value(
|
103
|
+
request.send(type),
|
104
|
+
Hawkei.configurations.obfuscated_fields
|
105
|
+
)
|
106
|
+
rescue StandardError => _e
|
107
|
+
{}
|
108
|
+
end
|
109
|
+
|
110
|
+
def https_request?(env)
|
111
|
+
env['HTTPS'] == 'on' ||
|
112
|
+
env['HTTP_X_FORWARDED_SSL'] == 'on' ||
|
113
|
+
env['HTTP_X_FORWARDED_PROTO'].to_s.split(',').first == 'https' ||
|
114
|
+
env['rack.url_scheme'] == 'https'
|
115
|
+
end
|
116
|
+
|
117
|
+
def obfuscate_headers(env)
|
118
|
+
skip_headers = %w[HTTP_COOKIE]
|
119
|
+
|
120
|
+
headers = env.keys.grep(/^HTTP_|^CONTENT_/).each_with_object({}) do |key, hash|
|
121
|
+
next if skip_headers.include?(key)
|
122
|
+
|
123
|
+
name = key.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
|
124
|
+
|
125
|
+
hash[name] = env[key]
|
126
|
+
end
|
127
|
+
|
128
|
+
Util.deep_obfuscate_value(
|
129
|
+
headers,
|
130
|
+
Hawkei.configurations.obfuscated_fields
|
131
|
+
)
|
132
|
+
rescue StandardError => _e
|
133
|
+
{}
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Hawkei
|
2
|
+
module Plugins
|
3
|
+
module Rails
|
4
|
+
class MiddlewareData
|
5
|
+
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def store_data(request = nil)
|
9
|
+
store_request_data(request) if request
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def store_request_data(request)
|
15
|
+
object = Hawkei::Store.get(:request) || {}
|
16
|
+
|
17
|
+
Hawkei::Store.set(
|
18
|
+
:request,
|
19
|
+
object.merge(id: request.env['action_dispatch.request_id'])
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Hawkei
|
2
|
+
module Plugins
|
3
|
+
module Rails
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
|
6
|
+
initializer 'hawkei.middleware' do |app|
|
7
|
+
app.config.middleware.use(Hawkei::Plugins::Rack::Middleware)
|
8
|
+
|
9
|
+
if ActiveSupport.const_defined?(:Reloader) && ActiveSupport::Reloader.respond_to?(:to_complete)
|
10
|
+
ActiveSupport::Reloader.to_complete do
|
11
|
+
Hawkei::Store.clear!
|
12
|
+
end
|
13
|
+
elsif ActionDispatch.const_defined?(:Reloader) && ActionDispatch::Reloader.respond_to?(:to_cleanup)
|
14
|
+
ActionDispatch::Reloader.to_cleanup do
|
15
|
+
Hawkei::Store.clear!
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Hawkei
|
2
|
+
module Plugins
|
3
|
+
module Sidekiq
|
4
|
+
##
|
5
|
+
# Hawkei \Plugins \Sidekiq \Client \Middleware
|
6
|
+
#
|
7
|
+
# Client middleware for sidekiq
|
8
|
+
#
|
9
|
+
class ClientMiddleware
|
10
|
+
|
11
|
+
def call(_worker_class, job, _queue, _redis_pool)
|
12
|
+
job['_hawkei_stid'] = Hawkei::Store.store[:session_tracker_id]
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'hawkei/plugins/sidekiq/client_middleware'
|
2
|
+
require 'hawkei/plugins/sidekiq/server_middleware'
|
3
|
+
|
4
|
+
Sidekiq.configure_client do |config|
|
5
|
+
config.client_middleware do |chain|
|
6
|
+
chain.add Hawkei::Plugins::Sidekiq::ClientMiddleware
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Sidekiq.configure_server do |config|
|
11
|
+
config.server_middleware do |chain|
|
12
|
+
chain.add Hawkei::Plugins::Sidekiq::ServerMiddleware
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Hawkei
|
2
|
+
module Plugins
|
3
|
+
module Sidekiq
|
4
|
+
##
|
5
|
+
# Hawkei \Plugins \Sidekiq \Server \Middleware
|
6
|
+
#
|
7
|
+
# Server middleware for sidekiq
|
8
|
+
#
|
9
|
+
class ServerMiddleware
|
10
|
+
|
11
|
+
def call(_worker, job, _queue)
|
12
|
+
Hawkei::Store.set(:session_tracker_id, job['_hawkei_stid'] || SecureRandom.uuid)
|
13
|
+
Hawkei::Store.set(:server_software, "Sidekiq #{::Sidekiq::VERSION}")
|
14
|
+
store_worker_data(job)
|
15
|
+
yield
|
16
|
+
ensure
|
17
|
+
Hawkei::Store.clear!
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def store_worker_data(job)
|
23
|
+
Hawkei::Store.set(
|
24
|
+
:worker,
|
25
|
+
name: 'sidekiq',
|
26
|
+
version: ::Sidekiq::VERSION,
|
27
|
+
queue: job['queue'],
|
28
|
+
class: job['class'],
|
29
|
+
id: job['jid'],
|
30
|
+
created_at: convert_to_iso8601(job['created_at']),
|
31
|
+
process_at: convert_to_iso8601(job['enqueued_at']),
|
32
|
+
args: job['args'],
|
33
|
+
retried: job['retry_count'].is_a?(Integer),
|
34
|
+
retry_number: job['retry_count'],
|
35
|
+
failed_at: convert_to_iso8601(job['failed_at'])
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_to_iso8601(int_time)
|
40
|
+
Time.at(int_time).utc.iso8601(3)
|
41
|
+
rescue StandardError => _e
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
##
|
2
|
+
# Rack & Rails
|
3
|
+
#
|
4
|
+
if defined?(Rack)
|
5
|
+
require 'hawkei/plugins/rack/middleware'
|
6
|
+
|
7
|
+
if defined?(Rails)
|
8
|
+
require 'hawkei/plugins/rails/railtie'
|
9
|
+
require 'hawkei/plugins/rails/middleware_data'
|
10
|
+
require 'hawkei/plugins/rails/data'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
##
|
15
|
+
# Sidekiq
|
16
|
+
#
|
17
|
+
require 'hawkei/plugins/sidekiq/load' if defined?(Sidekiq)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Hawkei
|
2
|
+
module Processor
|
3
|
+
class Async
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@queue = Queue.new
|
7
|
+
@state_worker = Concurrent::AtomicBoolean.new(true)
|
8
|
+
@worker = Worker.new(@queue, @state_worker)
|
9
|
+
|
10
|
+
at_exit do
|
11
|
+
shutdown_worker if worker_running?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def enqueue(attributes)
|
16
|
+
ensure_worker_running
|
17
|
+
|
18
|
+
@queue << attributes
|
19
|
+
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def shutdown_worker
|
26
|
+
@state_worker.make_false
|
27
|
+
@queue << Hawkei::Processor::Worker::SHUTDOWN_MESSAGE
|
28
|
+
@worker_thread.wait
|
29
|
+
|
30
|
+
executor.shutdown
|
31
|
+
executor.wait_for_termination
|
32
|
+
end
|
33
|
+
|
34
|
+
def executor
|
35
|
+
@executor ||= Concurrent.global_io_executor
|
36
|
+
end
|
37
|
+
|
38
|
+
def ensure_worker_running
|
39
|
+
return if worker_running?
|
40
|
+
|
41
|
+
@worker_thread = Concurrent::Future.execute { @worker.run }
|
42
|
+
end
|
43
|
+
|
44
|
+
def worker_running?
|
45
|
+
@worker_thread && @worker_thread.incomplete?
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Hawkei
|
2
|
+
module Processor
|
3
|
+
class Batch
|
4
|
+
|
5
|
+
attr_reader :messages
|
6
|
+
|
7
|
+
MAX_BYTES = 204_800 # 200Kb
|
8
|
+
MAX_MESSAGES = 100
|
9
|
+
MAX_MESSAGE_BYTES = 32_768 # 32Kb
|
10
|
+
|
11
|
+
DEFAULT_RETRY_TIME = 60
|
12
|
+
|
13
|
+
MAX_RETRY = 10
|
14
|
+
RETRY_MAP = {
|
15
|
+
1 => 2,
|
16
|
+
2 => 5,
|
17
|
+
3 => 10,
|
18
|
+
4 => 20,
|
19
|
+
5 => 30,
|
20
|
+
6 => 30,
|
21
|
+
7 => 30,
|
22
|
+
8 => 30,
|
23
|
+
9 => 30,
|
24
|
+
10 => 30,
|
25
|
+
}.freeze
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
@retry_count = 0
|
29
|
+
@total_bytes = 0
|
30
|
+
@messages = []
|
31
|
+
end
|
32
|
+
|
33
|
+
def <<(message)
|
34
|
+
message_json_size = message.to_json.bytesize
|
35
|
+
|
36
|
+
if max_message_reached?(message_json_size)
|
37
|
+
Hawkei.configurations.logger.error('Message is too big to be send')
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
|
41
|
+
@total_bytes += message_json_size
|
42
|
+
|
43
|
+
@messages << message
|
44
|
+
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def empty?
|
49
|
+
@messages.size.zero?
|
50
|
+
end
|
51
|
+
|
52
|
+
def full?
|
53
|
+
max_messages_reached? || max_size_reached?
|
54
|
+
end
|
55
|
+
|
56
|
+
def next_retry
|
57
|
+
RETRY_MAP[@retry_count] || DEFAULT_RETRY_TIME
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_retry
|
61
|
+
@retry_count += 1
|
62
|
+
end
|
63
|
+
|
64
|
+
def can_retry?
|
65
|
+
@retry_count < MAX_RETRY
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def max_messages_reached?
|
71
|
+
@messages.length >= MAX_MESSAGES
|
72
|
+
end
|
73
|
+
|
74
|
+
def max_size_reached?
|
75
|
+
@total_bytes >= MAX_BYTES
|
76
|
+
end
|
77
|
+
|
78
|
+
def max_message_reached?(message_json_size)
|
79
|
+
message_json_size > MAX_MESSAGE_BYTES
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|