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.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/.diffend.yml +3 -0
  5. data/.github/workflows/ci.yml +53 -0
  6. data/.gitignore +2 -0
  7. data/.ruby-version +1 -1
  8. data/CHANGELOG.md +9 -0
  9. data/Gemfile +9 -0
  10. data/Gemfile.lock +51 -33
  11. data/LICENSE +165 -0
  12. data/README.md +192 -53
  13. data/config/errors.yml +3 -16
  14. data/docker-compose.yml +17 -0
  15. data/lib/water_drop.rb +4 -24
  16. data/lib/water_drop/config.rb +41 -142
  17. data/lib/water_drop/contracts.rb +0 -2
  18. data/lib/water_drop/contracts/config.rb +8 -121
  19. data/lib/water_drop/contracts/message.rb +41 -0
  20. data/lib/water_drop/errors.rb +31 -5
  21. data/lib/water_drop/instrumentation.rb +7 -0
  22. data/lib/water_drop/instrumentation/monitor.rb +16 -23
  23. data/lib/water_drop/instrumentation/stdout_listener.rb +113 -32
  24. data/lib/water_drop/producer.rb +142 -0
  25. data/lib/water_drop/producer/async.rb +51 -0
  26. data/lib/water_drop/producer/buffer.rb +113 -0
  27. data/lib/water_drop/producer/builder.rb +63 -0
  28. data/lib/water_drop/producer/dummy_client.rb +32 -0
  29. data/lib/water_drop/producer/statistics_decorator.rb +71 -0
  30. data/lib/water_drop/producer/status.rb +52 -0
  31. data/lib/water_drop/producer/sync.rb +65 -0
  32. data/lib/water_drop/version.rb +1 -1
  33. data/waterdrop.gemspec +4 -4
  34. metadata +25 -24
  35. metadata.gz.sig +0 -0
  36. data/.travis.yml +0 -35
  37. data/MIT-LICENCE +0 -18
  38. data/lib/water_drop/async_producer.rb +0 -26
  39. data/lib/water_drop/base_producer.rb +0 -57
  40. data/lib/water_drop/config_applier.rb +0 -52
  41. data/lib/water_drop/contracts/message_options.rb +0 -19
  42. data/lib/water_drop/sync_producer.rb +0 -24
metadata.gz.sig CHANGED
Binary file
@@ -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
@@ -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