waterdrop 1.4.0 → 2.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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.diffend.yml +3 -0
- data/.github/workflows/ci.yml +53 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +9 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +51 -33
- data/LICENSE +165 -0
- data/README.md +192 -53
- data/config/errors.yml +3 -16
- data/docker-compose.yml +17 -0
- data/lib/water_drop.rb +4 -24
- data/lib/water_drop/config.rb +41 -142
- data/lib/water_drop/contracts.rb +0 -2
- data/lib/water_drop/contracts/config.rb +8 -121
- data/lib/water_drop/contracts/message.rb +41 -0
- data/lib/water_drop/errors.rb +31 -5
- data/lib/water_drop/instrumentation.rb +7 -0
- data/lib/water_drop/instrumentation/monitor.rb +16 -23
- data/lib/water_drop/instrumentation/stdout_listener.rb +113 -32
- data/lib/water_drop/producer.rb +142 -0
- data/lib/water_drop/producer/async.rb +51 -0
- data/lib/water_drop/producer/buffer.rb +113 -0
- data/lib/water_drop/producer/builder.rb +63 -0
- data/lib/water_drop/producer/dummy_client.rb +32 -0
- data/lib/water_drop/producer/statistics_decorator.rb +71 -0
- data/lib/water_drop/producer/status.rb +52 -0
- data/lib/water_drop/producer/sync.rb +65 -0
- data/lib/water_drop/version.rb +1 -1
- data/waterdrop.gemspec +4 -4
- metadata +25 -24
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -35
- data/MIT-LICENCE +0 -18
- data/lib/water_drop/async_producer.rb +0 -26
- data/lib/water_drop/base_producer.rb +0 -57
- data/lib/water_drop/config_applier.rb +0 -52
- data/lib/water_drop/contracts/message_options.rb +0 -19
- data/lib/water_drop/sync_producer.rb +0 -24
metadata.gz.sig
CHANGED
Binary file
|
data/.travis.yml
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
services:
|
2
|
-
- docker
|
3
|
-
|
4
|
-
dist: trusty
|
5
|
-
cache: bundler
|
6
|
-
|
7
|
-
git:
|
8
|
-
depth: false
|
9
|
-
|
10
|
-
test: &test
|
11
|
-
stage: Test
|
12
|
-
language: ruby
|
13
|
-
before_install:
|
14
|
-
- yes | gem update --system
|
15
|
-
script: bundle exec rspec
|
16
|
-
|
17
|
-
jobs:
|
18
|
-
include:
|
19
|
-
- <<: *test
|
20
|
-
rvm: 2.7.1
|
21
|
-
- <<: *test
|
22
|
-
rvm: 2.6.6
|
23
|
-
- <<: *test
|
24
|
-
rvm: 2.5.8
|
25
|
-
|
26
|
-
- stage: coditsu
|
27
|
-
language: ruby
|
28
|
-
rvm: 2.7.1
|
29
|
-
before_install:
|
30
|
-
- yes | gem update --system
|
31
|
-
script: \curl -sSL https://api.coditsu.io/run/ci | bash
|
32
|
-
|
33
|
-
stages:
|
34
|
-
- test
|
35
|
-
- coditsu
|
data/MIT-LICENCE
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
2
|
-
a copy of this software and associated documentation files (the
|
3
|
-
"Software"), to deal in the Software without restriction, including
|
4
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
5
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
6
|
-
permit persons to whom the Software is furnished to do so, subject to
|
7
|
-
the following conditions:
|
8
|
-
|
9
|
-
The above copyright notice and this permission notice shall be
|
10
|
-
included in all copies or substantial portions of the Software.
|
11
|
-
|
12
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
13
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
14
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
15
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
16
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
17
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
18
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -1,26 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# WaterDrop library
|
4
|
-
module WaterDrop
|
5
|
-
# Async producer for messages
|
6
|
-
class AsyncProducer < 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
|
-
d_method = WaterDrop.config.raise_on_buffer_overflow ? :deliver_async! : :deliver_async
|
20
|
-
|
21
|
-
DeliveryBoy.send(d_method, message, **options)
|
22
|
-
rescue Kafka::Error => e
|
23
|
-
graceful_attempt?(attempts_count, message, options, e) ? retry : raise(e)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WaterDrop
|
4
|
-
# Base messages producer that contains all the logic that is exactly the same for both
|
5
|
-
# sync and async producers
|
6
|
-
class BaseProducer
|
7
|
-
# Contract for checking the correctness of the provided data that someone wants to
|
8
|
-
# dispatch to Kafka
|
9
|
-
SCHEMA = Contracts::MessageOptions.new.freeze
|
10
|
-
|
11
|
-
private_constant :SCHEMA
|
12
|
-
|
13
|
-
class << self
|
14
|
-
private
|
15
|
-
|
16
|
-
# Runs the message options validations and raises an error if anything is invalid
|
17
|
-
# @param options [Hash] hash that we want to validate
|
18
|
-
# @raise [WaterDrop::Errors::InvalidMessageOptions] raised when message options are
|
19
|
-
# somehow invalid and we cannot perform delivery because of that
|
20
|
-
def validate!(options)
|
21
|
-
validation_result = SCHEMA.call(options)
|
22
|
-
return true if validation_result.success?
|
23
|
-
|
24
|
-
raise Errors::InvalidMessageOptions, validation_result.errors
|
25
|
-
end
|
26
|
-
|
27
|
-
# Upon failed delivery, we may try to resend a message depending on the attempt number
|
28
|
-
# or re-raise an error if we're unable to do that after given number of retries
|
29
|
-
# This method checks that and also instruments errors and retries for the delivery
|
30
|
-
# @param attempts_count [Integer] number of attempt (starting from 1) for the delivery
|
31
|
-
# @param message [String] message that we want to send to Kafka
|
32
|
-
# @param options [Hash] options (including topic) for producer
|
33
|
-
# @param error [Kafka::Error] error that occurred
|
34
|
-
# @return [Boolean] true if this is a graceful attempt and we can retry or false it this
|
35
|
-
# was the final one and we should deal with the fact, that we cannot deliver a given
|
36
|
-
# message
|
37
|
-
def graceful_attempt?(attempts_count, message, options, error)
|
38
|
-
scope = "#{to_s.split('::').last.sub('Producer', '_producer').downcase}.call"
|
39
|
-
payload = {
|
40
|
-
caller: self,
|
41
|
-
message: message,
|
42
|
-
options: options,
|
43
|
-
error: error,
|
44
|
-
attempts_count: attempts_count
|
45
|
-
}
|
46
|
-
|
47
|
-
if attempts_count > WaterDrop.config.kafka.max_retries
|
48
|
-
WaterDrop.monitor.instrument("#{scope}.error", payload)
|
49
|
-
false
|
50
|
-
else
|
51
|
-
WaterDrop.monitor.instrument("#{scope}.retry", payload)
|
52
|
-
true
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WaterDrop
|
4
|
-
# Engine used to propagate config application to DeliveryBoy with corner case handling
|
5
|
-
module ConfigApplier
|
6
|
-
class << self
|
7
|
-
# @param delivery_boy_config [DeliveryBoy::Config] delivery boy config instance
|
8
|
-
# @param settings [Hash] hash with WaterDrop settings
|
9
|
-
def call(delivery_boy_config, settings)
|
10
|
-
# Recursive lambda for mapping config down to delivery boy
|
11
|
-
settings.each do |key, value|
|
12
|
-
call(delivery_boy_config, value) && next if value.is_a?(Hash)
|
13
|
-
|
14
|
-
# If this is a special case that needs manual setup instead of a direct reassignment
|
15
|
-
if respond_to?(key, true)
|
16
|
-
send(key, delivery_boy_config, value)
|
17
|
-
else
|
18
|
-
# If this setting is our internal one, we should not sync it with the delivery boy
|
19
|
-
next unless delivery_boy_config.respond_to?(:"#{key}=")
|
20
|
-
|
21
|
-
delivery_boy_config.public_send(:"#{key}=", value)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
# Extra setup for the compression codec as it behaves differently than other settings
|
29
|
-
# that are ported 1:1 from ruby-kafka
|
30
|
-
# For some crazy reason, delivery boy requires compression codec as a string, when
|
31
|
-
# ruby-kafka as a symbol. We follow ruby-kafka internal design, so we had to mimic
|
32
|
-
# that by assigning a string version that down the road will be symbolized again
|
33
|
-
# by delivery boy
|
34
|
-
# @param delivery_boy_config [DeliveryBoy::Config] delivery boy config instance
|
35
|
-
# @param codec_name [Symbol] codec name as a symbol
|
36
|
-
def compression_codec(delivery_boy_config, codec_name)
|
37
|
-
# If there is no compression codec, we don't apply anything
|
38
|
-
return unless codec_name
|
39
|
-
|
40
|
-
delivery_boy_config.compression_codec = codec_name.to_s
|
41
|
-
end
|
42
|
-
|
43
|
-
# We use the "seed_brokers" name and DeliveryBoy uses "brokers" so we pass the values
|
44
|
-
# manually
|
45
|
-
# @param delivery_boy_config [DeliveryBoy::Config] delivery boy config instance
|
46
|
-
# @param seed_brokers [Array<String>] kafka seed brokers
|
47
|
-
def seed_brokers(delivery_boy_config, seed_brokers)
|
48
|
-
delivery_boy_config.brokers = seed_brokers
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,19 +0,0 @@
|
|
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
|
@@ -1,24 +0,0 @@
|
|
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
|