waterdrop 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.coditsu/ci.yml +3 -0
- data/.github/FUNDING.yml +1 -0
- data/.gitignore +68 -0
- data/.rspec +1 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +35 -0
- data/CHANGELOG.md +158 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +102 -0
- data/MIT-LICENCE +18 -0
- data/README.md +127 -0
- data/certs/mensfeld.pem +25 -0
- data/config/errors.yml +19 -0
- data/lib/water_drop.rb +50 -0
- data/lib/water_drop/async_producer.rb +26 -0
- data/lib/water_drop/base_producer.rb +57 -0
- data/lib/water_drop/config.rb +162 -0
- data/lib/water_drop/config_applier.rb +52 -0
- data/lib/water_drop/contracts.rb +9 -0
- data/lib/water_drop/contracts/config.rb +139 -0
- data/lib/water_drop/contracts/message_options.rb +19 -0
- data/lib/water_drop/errors.rb +18 -0
- data/lib/water_drop/instrumentation/monitor.rb +46 -0
- data/lib/water_drop/instrumentation/stdout_listener.rb +45 -0
- data/lib/water_drop/sync_producer.rb +24 -0
- data/lib/water_drop/version.rb +7 -0
- data/lib/waterdrop.rb +4 -0
- data/log/.gitkeep +0 -0
- data/waterdrop.gemspec +36 -0
- metadata +189 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
module Contracts
|
5
|
+
# Contract with validation rules for validating that all the message options that
|
6
|
+
# we provide to producer ale valid and usable
|
7
|
+
# @note Does not validate message itself as it is not our concern
|
8
|
+
class MessageOptions < Dry::Validation::Contract
|
9
|
+
params do
|
10
|
+
required(:topic).filled(:str?, format?: TOPIC_REGEXP)
|
11
|
+
optional(:key).maybe(:str?, :filled?)
|
12
|
+
optional(:partition).filled(:int?, gteq?: 0)
|
13
|
+
optional(:partition_key).maybe(:str?, :filled?)
|
14
|
+
optional(:create_time).maybe(:time?)
|
15
|
+
optional(:headers).maybe(:hash?)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
# Namespace used to encapsulate all the internal errors of WaterDrop
|
5
|
+
module Errors
|
6
|
+
# Base class for all the WaterDrop internal errors
|
7
|
+
BaseError = Class.new(StandardError)
|
8
|
+
|
9
|
+
# Raised when configuration doesn't match with validation contract
|
10
|
+
InvalidConfiguration = Class.new(BaseError)
|
11
|
+
|
12
|
+
# Raised when we try to send message with invalid options
|
13
|
+
InvalidMessageOptions = Class.new(BaseError)
|
14
|
+
|
15
|
+
# Raised when want to hook up to an event that is not registered and supported
|
16
|
+
UnregisteredMonitorEvent = Class.new(BaseError)
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
# Namespace for all the things related with WaterDrop instrumentation process
|
5
|
+
module Instrumentation
|
6
|
+
# Monitor is used to hookup external monitoring services to monitor how WaterDrop works
|
7
|
+
# Since it is a pub-sub based on dry-monitor, you can use as many subscribers/loggers at the
|
8
|
+
# same time, which means that you might have for example file logging and NewRelic at the same
|
9
|
+
# time
|
10
|
+
# @note This class acts as a singleton because we are only permitted to have single monitor
|
11
|
+
# per running process (just as logger)
|
12
|
+
class Monitor < Dry::Monitor::Notifications
|
13
|
+
# List of events that we support in the system and to which a monitor client can hook up
|
14
|
+
# @note The non-error once support timestamp benchmarking
|
15
|
+
BASE_EVENTS = %w[
|
16
|
+
async_producer.call.error
|
17
|
+
async_producer.call.retry
|
18
|
+
sync_producer.call.error
|
19
|
+
sync_producer.call.retry
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
private_constant :BASE_EVENTS
|
23
|
+
|
24
|
+
# @return [WaterDrop::Instrumentation::Monitor] monitor instance for system instrumentation
|
25
|
+
def initialize
|
26
|
+
super(:waterdrop)
|
27
|
+
BASE_EVENTS.each(&method(:register_event))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Allows us to subscribe to events with a code that will be yielded upon events
|
31
|
+
# @param event_name_or_listener [String, Object] name of the event we want to subscribe to
|
32
|
+
# or a listener if we decide to go with object listener
|
33
|
+
def subscribe(event_name_or_listener)
|
34
|
+
return super unless event_name_or_listener.is_a?(String)
|
35
|
+
return super if available_events.include?(event_name_or_listener)
|
36
|
+
|
37
|
+
raise Errors::UnregisteredMonitorEvent, event_name_or_listener
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Array<String>] names of available events to which we can subscribe
|
41
|
+
def available_events
|
42
|
+
__bus__.events.keys
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
module Instrumentation
|
5
|
+
# Default listener that hooks up to our instrumentation and uses its events for logging
|
6
|
+
# It can be removed/replaced or anything without any harm to the Waterdrop flow
|
7
|
+
# @note It is a module as we can use it then as a part of the Karafka framework listener
|
8
|
+
# as well as we can use it standalone
|
9
|
+
class StdoutListener
|
10
|
+
# Log levels that we use in this particular listener
|
11
|
+
USED_LOG_LEVELS = %i[
|
12
|
+
info
|
13
|
+
error
|
14
|
+
].freeze
|
15
|
+
|
16
|
+
%i[
|
17
|
+
sync_producer
|
18
|
+
async_producer
|
19
|
+
].each do |producer_type|
|
20
|
+
error_name = :"on_#{producer_type}_call_error"
|
21
|
+
retry_name = :"on_#{producer_type}_call_retry"
|
22
|
+
|
23
|
+
define_method error_name do |event|
|
24
|
+
options = event[:options]
|
25
|
+
error = event[:error]
|
26
|
+
error "Delivery failure to: #{options} because of #{error}"
|
27
|
+
end
|
28
|
+
|
29
|
+
define_method retry_name do |event|
|
30
|
+
attempts_count = event[:attempts_count]
|
31
|
+
options = event[:options]
|
32
|
+
error = event[:error]
|
33
|
+
|
34
|
+
info "Attempt #{attempts_count} of delivery to: #{options} because of #{error}"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
USED_LOG_LEVELS.each do |log_level|
|
39
|
+
define_method log_level do |*args|
|
40
|
+
WaterDrop.logger.send(log_level, *args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# WaterDrop library
|
4
|
+
module WaterDrop
|
5
|
+
# Sync producer for messages
|
6
|
+
class SyncProducer < BaseProducer
|
7
|
+
# Performs message delivery using deliver_async method
|
8
|
+
# @param message [String] message that we want to send to Kafka
|
9
|
+
# @param options [Hash] options (including topic) for producer
|
10
|
+
# @raise [WaterDrop::Errors::InvalidMessageOptions] raised when message options are
|
11
|
+
# somehow invalid and we cannot perform delivery because of that
|
12
|
+
def self.call(message, options)
|
13
|
+
attempts_count ||= 0
|
14
|
+
attempts_count += 1
|
15
|
+
|
16
|
+
validate!(options)
|
17
|
+
return unless WaterDrop.config.deliver
|
18
|
+
|
19
|
+
DeliveryBoy.deliver(message, options)
|
20
|
+
rescue Kafka::Error => e
|
21
|
+
graceful_attempt?(attempts_count, message, options, e) ? retry : raise(e)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/waterdrop.rb
ADDED
data/log/.gitkeep
ADDED
File without changes
|
data/waterdrop.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
|
6
|
+
require 'water_drop/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'waterdrop'
|
10
|
+
spec.version = ::WaterDrop::VERSION
|
11
|
+
spec.platform = Gem::Platform::RUBY
|
12
|
+
spec.authors = ['Maciej Mensfeld']
|
13
|
+
spec.email = %w[maciej@mensfeld.pl]
|
14
|
+
spec.homepage = 'https://github.com/karafka/waterdrop'
|
15
|
+
spec.summary = 'Kafka messaging made easy!'
|
16
|
+
spec.description = spec.summary
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.add_dependency 'delivery_boy', '>= 0.2', '< 2.x'
|
20
|
+
spec.add_dependency 'dry-configurable', '~> 0.8'
|
21
|
+
spec.add_dependency 'dry-monitor', '~> 0.3'
|
22
|
+
spec.add_dependency 'dry-validation', '~> 1.2'
|
23
|
+
spec.add_dependency 'ruby-kafka', '>= 0.7.8'
|
24
|
+
spec.add_dependency 'zeitwerk', '~> 2.1'
|
25
|
+
|
26
|
+
spec.required_ruby_version = '>= 2.5.0'
|
27
|
+
|
28
|
+
if $PROGRAM_NAME.end_with?('gem')
|
29
|
+
spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
|
30
|
+
end
|
31
|
+
|
32
|
+
spec.cert_chain = %w[certs/mensfeld.pem]
|
33
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
34
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = %w[lib]
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: waterdrop
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maciej Mensfeld
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIEODCCAqCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhtYWNp
|
14
|
+
ZWovREM9bWVuc2ZlbGQvREM9cGwwHhcNMjAwODExMDkxNTM3WhcNMjEwODExMDkx
|
15
|
+
NTM3WjAjMSEwHwYDVQQDDBhtYWNpZWovREM9bWVuc2ZlbGQvREM9cGwwggGiMA0G
|
16
|
+
CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDCpXsCgmINb6lHBXXBdyrgsBPSxC4/
|
17
|
+
2H+weJ6L9CruTiv2+2/ZkQGtnLcDgrD14rdLIHK7t0o3EKYlDT5GhD/XUVhI15JE
|
18
|
+
N7IqnPUgexe1fbZArwQ51afxz2AmPQN2BkB2oeQHXxnSWUGMhvcEZpfbxCCJH26w
|
19
|
+
hS0Ccsma8yxA6hSlGVhFVDuCr7c2L1di6cK2CtIDpfDaWqnVNJEwBYHIxrCoWK5g
|
20
|
+
sIGekVt/admS9gRhIMaIBg+Mshth5/DEyWO2QjteTodItlxfTctrfmiAl8X8T5JP
|
21
|
+
VXeLp5SSOJ5JXE80nShMJp3RFnGw5fqjX/ffjtISYh78/By4xF3a25HdWH9+qO2Z
|
22
|
+
tx0wSGc9/4gqNM0APQnjN/4YXrGZ4IeSjtE+OrrX07l0TiyikzSLFOkZCAp8oBJi
|
23
|
+
Fhlosz8xQDJf7mhNxOaZziqASzp/hJTU/tuDKl5+ql2icnMv5iV/i6SlmvU29QNg
|
24
|
+
LCV71pUv0pWzN+OZbHZKWepGhEQ3cG9MwvkCAwEAAaN3MHUwCQYDVR0TBAIwADAL
|
25
|
+
BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFImGed2AXS070ohfRidiCEhXEUN+MB0GA1Ud
|
26
|
+
EQQWMBSBEm1hY2llakBtZW5zZmVsZC5wbDAdBgNVHRIEFjAUgRJtYWNpZWpAbWVu
|
27
|
+
c2ZlbGQucGwwDQYJKoZIhvcNAQELBQADggGBAKiHpwoENVrMi94V1zD4o8/6G3AU
|
28
|
+
gWz4udkPYHTZLUy3dLznc/sNjdkJFWT3E6NKYq7c60EpJ0m0vAEg5+F5pmNOsvD3
|
29
|
+
2pXLj9kisEeYhR516HwXAvtngboUcb75skqvBCU++4Pu7BRAPjO1/ihLSBexbwSS
|
30
|
+
fF+J5OWNuyHHCQp+kGPLtXJe2yUYyvSWDj3I2//Vk0VhNOIlaCS1+5/P3ZJThOtm
|
31
|
+
zJUBI7h3HgovwRpcnmk2mXTmU4Zx/bCzX8EA6VY0khEvnmiq7S6eBF0H9qH8KyQ6
|
32
|
+
EkVLpvmUDFcf/uNaBQdazEMB5jYtwoA8gQlANETNGPi51KlkukhKgaIEDMkBDJOx
|
33
|
+
65N7DzmkcyY0/GwjIVIxmRhcrCt1YeCUElmfFx0iida1/YRm6sB2AXqScc1+ECRi
|
34
|
+
2DND//YJUikn1zwbz1kT70XmHd97B4Eytpln7K+M1u2g1pHVEPW4owD/ammXNpUy
|
35
|
+
nt70FcDD4yxJQ+0YNiHd0N8IcVBM1TMIVctMNQ==
|
36
|
+
-----END CERTIFICATE-----
|
37
|
+
date: 2020-08-25 00:00:00.000000000 Z
|
38
|
+
dependencies:
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: delivery_boy
|
41
|
+
requirement: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0.2'
|
46
|
+
- - "<"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 2.x
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0.2'
|
56
|
+
- - "<"
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 2.x
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
name: dry-configurable
|
61
|
+
requirement: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - "~>"
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0.8'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - "~>"
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0.8'
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: dry-monitor
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0.3'
|
80
|
+
type: :runtime
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - "~>"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0.3'
|
87
|
+
- !ruby/object:Gem::Dependency
|
88
|
+
name: dry-validation
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - "~>"
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.2'
|
94
|
+
type: :runtime
|
95
|
+
prerelease: false
|
96
|
+
version_requirements: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - "~>"
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '1.2'
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: ruby-kafka
|
103
|
+
requirement: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 0.7.8
|
108
|
+
type: :runtime
|
109
|
+
prerelease: false
|
110
|
+
version_requirements: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: 0.7.8
|
115
|
+
- !ruby/object:Gem::Dependency
|
116
|
+
name: zeitwerk
|
117
|
+
requirement: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - "~>"
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '2.1'
|
122
|
+
type: :runtime
|
123
|
+
prerelease: false
|
124
|
+
version_requirements: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - "~>"
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '2.1'
|
129
|
+
description: Kafka messaging made easy!
|
130
|
+
email:
|
131
|
+
- maciej@mensfeld.pl
|
132
|
+
executables: []
|
133
|
+
extensions: []
|
134
|
+
extra_rdoc_files: []
|
135
|
+
files:
|
136
|
+
- ".coditsu/ci.yml"
|
137
|
+
- ".github/FUNDING.yml"
|
138
|
+
- ".gitignore"
|
139
|
+
- ".rspec"
|
140
|
+
- ".ruby-gemset"
|
141
|
+
- ".ruby-version"
|
142
|
+
- ".travis.yml"
|
143
|
+
- CHANGELOG.md
|
144
|
+
- Gemfile
|
145
|
+
- Gemfile.lock
|
146
|
+
- MIT-LICENCE
|
147
|
+
- README.md
|
148
|
+
- certs/mensfeld.pem
|
149
|
+
- config/errors.yml
|
150
|
+
- lib/water_drop.rb
|
151
|
+
- lib/water_drop/async_producer.rb
|
152
|
+
- lib/water_drop/base_producer.rb
|
153
|
+
- lib/water_drop/config.rb
|
154
|
+
- lib/water_drop/config_applier.rb
|
155
|
+
- lib/water_drop/contracts.rb
|
156
|
+
- lib/water_drop/contracts/config.rb
|
157
|
+
- lib/water_drop/contracts/message_options.rb
|
158
|
+
- lib/water_drop/errors.rb
|
159
|
+
- lib/water_drop/instrumentation/monitor.rb
|
160
|
+
- lib/water_drop/instrumentation/stdout_listener.rb
|
161
|
+
- lib/water_drop/sync_producer.rb
|
162
|
+
- lib/water_drop/version.rb
|
163
|
+
- lib/waterdrop.rb
|
164
|
+
- log/.gitkeep
|
165
|
+
- waterdrop.gemspec
|
166
|
+
homepage: https://github.com/karafka/waterdrop
|
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: 2.5.0
|
179
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
180
|
+
requirements:
|
181
|
+
- - ">="
|
182
|
+
- !ruby/object:Gem::Version
|
183
|
+
version: '0'
|
184
|
+
requirements: []
|
185
|
+
rubygems_version: 3.1.4
|
186
|
+
signing_key:
|
187
|
+
specification_version: 4
|
188
|
+
summary: Kafka messaging made easy!
|
189
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|