waterdrop 0.4.0 → 1.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +10 -2
- data/CHANGELOG.md +13 -1
- data/Gemfile +6 -0
- data/Gemfile.lock +51 -29
- data/README.md +68 -57
- data/config/errors.yml +6 -0
- data/lib/water_drop.rb +35 -22
- data/lib/water_drop/async_producer.rb +8 -0
- data/lib/water_drop/base_producer.rb +35 -0
- data/lib/water_drop/config.rb +80 -23
- data/lib/water_drop/errors.rb +15 -0
- data/lib/water_drop/schemas/config.rb +72 -0
- data/lib/water_drop/schemas/message_options.rb +18 -0
- data/lib/water_drop/sync_producer.rb +10 -0
- data/lib/water_drop/version.rb +1 -1
- data/waterdrop.gemspec +5 -9
- metadata +23 -78
- data/Rakefile +0 -8
- data/lib/water_drop/message.rb +0 -40
- data/lib/water_drop/pool.rb +0 -22
- data/lib/water_drop/producer_proxy.rb +0 -80
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93db4ab2979f93cc621a72ee8792a6e7f7f6c1f0
|
4
|
+
data.tar.gz: acbeb2ccbb8eee1e53b47e1b9cb4da1d484e6b19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b0abf19f2a2fd07db54d472c7a8f16c32205688377d3ae6038d5dc7d19ece08d3bbdde42e6031647dd914b3e4ccee612929992a4ee991cc3f020c93702e8e365
|
7
|
+
data.tar.gz: d177d752b481ba9fde09cd63edc6793e735cdf5516e28071bfa553269acb6fd3b2acace3cea22a870f5885eccb9f08006745246f39a49a065e6e92b1ff33c1b1
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.4.
|
1
|
+
2.4.2
|
data/.travis.yml
CHANGED
@@ -1,9 +1,17 @@
|
|
1
1
|
language: ruby
|
2
|
+
sudo: false
|
2
3
|
rvm:
|
3
4
|
- 2.3.0
|
4
5
|
- 2.3.1
|
5
6
|
- 2.3.2
|
6
7
|
- 2.3.3
|
8
|
+
- 2.3.4
|
7
9
|
- 2.4.0
|
8
|
-
|
9
|
-
-
|
10
|
+
- 2.4.1
|
11
|
+
- 2.4.2
|
12
|
+
- jruby-head
|
13
|
+
script: bundle exec rspec spec/
|
14
|
+
env:
|
15
|
+
global:
|
16
|
+
- JRUBY_OPTS='--debug'
|
17
|
+
install: bundle install --jobs=3 --retry=3
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
# WaterDrop changelog
|
2
2
|
|
3
|
-
## 0.
|
3
|
+
## 1.0.0 unreleased
|
4
|
+
|
5
|
+
- #37 - ack level for producer
|
6
|
+
- Gem bump
|
7
|
+
- Ruby 2.4.2 support
|
8
|
+
- Raw ruby-kafka driver is now replaced with delivery_boy
|
9
|
+
- Sync and async producers
|
10
|
+
- Complete update of the API
|
11
|
+
- Much better validations for config details
|
12
|
+
- Complete API remodel - please read the new README
|
13
|
+
- Renamed send_messages to deliver
|
14
|
+
|
15
|
+
## 0.4
|
4
16
|
- Bump to match Karafka
|
5
17
|
- Renamed ```hosts``` to ```seed_brokers```
|
6
18
|
- Removed the ```ssl``` scoping for ```kafka``` config namespace to better match Karafka conventions
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,57 +1,79 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
waterdrop (0.
|
5
|
-
|
6
|
-
|
7
|
-
dry-
|
4
|
+
waterdrop (1.0.0.alpha1)
|
5
|
+
delivery_boy (>= 0.2.2)
|
6
|
+
dry-configurable (~> 0.7)
|
7
|
+
dry-validation (~> 0.11)
|
8
8
|
null-logger
|
9
|
-
rake
|
10
|
-
ruby-kafka (~> 0.4)
|
11
9
|
|
12
10
|
GEM
|
13
11
|
remote: https://rubygems.org/
|
14
12
|
specs:
|
15
13
|
concurrent-ruby (1.0.5)
|
16
|
-
|
14
|
+
delivery_boy (0.2.2)
|
15
|
+
king_konf (~> 0.1.8)
|
16
|
+
ruby-kafka (~> 0.4)
|
17
17
|
diff-lcs (1.3)
|
18
18
|
docile (1.1.5)
|
19
19
|
dry-configurable (0.7.0)
|
20
20
|
concurrent-ruby (~> 1.0)
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
dry-container (0.6.0)
|
22
|
+
concurrent-ruby (~> 1.0)
|
23
|
+
dry-configurable (~> 0.1, >= 0.1.3)
|
24
|
+
dry-core (0.3.4)
|
25
|
+
concurrent-ruby (~> 1.0)
|
26
|
+
dry-equalizer (0.2.0)
|
27
|
+
dry-logic (0.4.2)
|
28
|
+
dry-container (~> 0.2, >= 0.2.6)
|
29
|
+
dry-core (~> 0.2)
|
30
|
+
dry-equalizer (~> 0.2)
|
31
|
+
dry-types (0.12.1)
|
32
|
+
concurrent-ruby (~> 1.0)
|
33
|
+
dry-configurable (~> 0.1)
|
34
|
+
dry-container (~> 0.3)
|
35
|
+
dry-core (~> 0.2, >= 0.2.1)
|
36
|
+
dry-equalizer (~> 0.2)
|
37
|
+
dry-logic (~> 0.4, >= 0.4.2)
|
38
|
+
inflecto (~> 0.0.0, >= 0.0.2)
|
39
|
+
dry-validation (0.11.1)
|
40
|
+
concurrent-ruby (~> 1.0)
|
41
|
+
dry-configurable (~> 0.1, >= 0.1.3)
|
42
|
+
dry-core (~> 0.2, >= 0.2.1)
|
43
|
+
dry-equalizer (~> 0.2)
|
44
|
+
dry-logic (~> 0.4, >= 0.4.0)
|
45
|
+
dry-types (~> 0.12.0)
|
46
|
+
inflecto (0.0.2)
|
24
47
|
json (2.1.0)
|
48
|
+
king_konf (0.1.8)
|
25
49
|
null-logger (0.1.4)
|
26
|
-
|
27
|
-
|
28
|
-
rspec-
|
29
|
-
rspec-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
rspec-expectations (3.6.0)
|
50
|
+
rspec (3.7.0)
|
51
|
+
rspec-core (~> 3.7.0)
|
52
|
+
rspec-expectations (~> 3.7.0)
|
53
|
+
rspec-mocks (~> 3.7.0)
|
54
|
+
rspec-core (3.7.0)
|
55
|
+
rspec-support (~> 3.7.0)
|
56
|
+
rspec-expectations (3.7.0)
|
34
57
|
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
-
rspec-support (~> 3.
|
36
|
-
rspec-mocks (3.
|
58
|
+
rspec-support (~> 3.7.0)
|
59
|
+
rspec-mocks (3.7.0)
|
37
60
|
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
-
rspec-support (~> 3.
|
39
|
-
rspec-support (3.
|
40
|
-
ruby-kafka (0.
|
41
|
-
|
42
|
-
simplecov (0.14.1)
|
61
|
+
rspec-support (~> 3.7.0)
|
62
|
+
rspec-support (3.7.0)
|
63
|
+
ruby-kafka (0.5.0)
|
64
|
+
simplecov (0.15.1)
|
43
65
|
docile (~> 1.1.0)
|
44
66
|
json (>= 1.8, < 3)
|
45
67
|
simplecov-html (~> 0.10.0)
|
46
|
-
simplecov-html (0.10.
|
68
|
+
simplecov-html (0.10.2)
|
47
69
|
|
48
70
|
PLATFORMS
|
49
71
|
ruby
|
50
72
|
|
51
73
|
DEPENDENCIES
|
52
|
-
rspec
|
53
|
-
simplecov
|
74
|
+
rspec
|
75
|
+
simplecov
|
54
76
|
waterdrop!
|
55
77
|
|
56
78
|
BUNDLED WITH
|
57
|
-
1.
|
79
|
+
1.15.4
|
data/README.md
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
# WaterDrop
|
2
2
|
|
3
3
|
[![Build Status](https://travis-ci.org/karafka/waterdrop.png)](https://travis-ci.org/karafka/waterdrop)
|
4
|
-
[![Code Climate](https://codeclimate.com/github/karafka/waterdrop/badges/gpa.svg)](https://codeclimate.com/github/karafka/waterdrop)
|
5
|
-
[![Gem Version](https://badge.fury.io/rb/waterdrop.svg)](http://badge.fury.io/rb/waterdrop)
|
6
4
|
[![Join the chat at https://gitter.im/karafka/karafka](https://badges.gitter.im/karafka/karafka.svg)](https://gitter.im/karafka/karafka?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
5
|
|
8
|
-
Gem used to send messages to Kafka in an easy way.
|
6
|
+
Gem used to send messages to Kafka in an easy way with an extra validation layer. It is a part of the [Karafka](https://github.com/karafka/karafka) ecosystem.
|
7
|
+
|
8
|
+
WaterDrop is based on Zendesks [delivery_boy](https://github.com/zendesk/delivery_boy) gem.
|
9
|
+
|
10
|
+
It is:
|
11
|
+
|
12
|
+
- Thread safe
|
13
|
+
- Supports sync and async producers
|
9
14
|
|
10
15
|
## Installation
|
11
16
|
|
@@ -27,97 +32,103 @@ bundle install
|
|
27
32
|
|
28
33
|
## Setup
|
29
34
|
|
30
|
-
WaterDrop
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
| socket_timeout | false | Integer | Number of seconds to wait when reading from or writing to a socket |
|
37
|
-
| connection_pool.size | true | Integer | Kafka connection pool size |
|
38
|
-
| connection_pool.timeout | true | Integer | Kafka connection pool timeout |
|
39
|
-
| kafka.seed_brokers | true | Array<String> | Kafka servers hosts with ports |
|
40
|
-
| raise_on_failure | true | Boolean | Should we raise an exception when we cannot send message to Kafka - if false will silently ignore failures |
|
41
|
-
| kafka.ssl_ca_cert | false | String | SSL CA certificate |
|
42
|
-
| kafka.ssl_ca_cert_file_path | false | String | SSL CA certificate file path |
|
43
|
-
| kafka.ssl_client_cert | false | String | SSL client certificate |
|
44
|
-
| kafka.ssl_client_cert_key | false | String | SSL client certificate password |
|
45
|
-
| kafka.sasl_gssapi_principal | false | String | SASL principal |
|
46
|
-
| kafka.sasl_gssapi_keytab | false | String | SASL keytab |
|
47
|
-
| kafka.sasl_plain_authzid | false | String | The authorization identity to use |
|
48
|
-
| kafka.sasl_plain_username | false | String | The username used to authenticate |
|
49
|
-
| kafka.sasl_plain_password | false | String | The password used to authenticate |
|
50
|
-
|
51
|
-
To apply this configuration, you need to use a *setup* method:
|
35
|
+
WaterDrop is a complex tool, that contains multiple configuration options. To keep everything organized, all the configuration options were divided into two groups:
|
36
|
+
|
37
|
+
- WaterDrop options - options directly related to Karafka framework and it's components
|
38
|
+
- Ruby-Kafka driver options - options related to Ruby-Kafka/Delivery boy
|
39
|
+
|
40
|
+
To apply all those configuration options, you need to use the ```#setup``` method:
|
52
41
|
|
53
42
|
```ruby
|
54
43
|
WaterDrop.setup do |config|
|
55
|
-
config.
|
56
|
-
config.
|
57
|
-
config.connection_pool.timeout = 1
|
58
|
-
config.kafka.seed_brokers = ['localhost:9092']
|
59
|
-
config.raise_on_failure = true
|
44
|
+
config.deliver = true
|
45
|
+
config.kafka.seed_brokers = %w[kafka://localhost:9092]
|
60
46
|
end
|
61
47
|
```
|
62
48
|
|
63
|
-
|
49
|
+
### WaterDrop configuration options
|
50
|
+
|
51
|
+
| Option | Description |
|
52
|
+
|-----------------------------|------------------------------------------------------------------|
|
53
|
+
| client_id | This is how the client will identify itself to the Kafka brokers |
|
54
|
+
| logger | Logger that we want to use |
|
55
|
+
| deliver | Should we send messages to Kafka |
|
56
|
+
|
57
|
+
### Ruby-Kafka driver and Delivery boy configuration options
|
58
|
+
|
59
|
+
**Note:** We've listed here only **the most important** configuration options. If you're interested in all the options, please go to the [config.rb](https://github.com/karafka/waterdrop/blob/master/lib/water_drop/config.rb) file for more details.
|
60
|
+
|
61
|
+
**Note:** All the options are subject to validations. In order to check what is and what is not acceptable, please go to the [config.rb validation schema](https://github.com/karafka/waterdrop/blob/master/lib/water_drop/schemas/config.rb) file.
|
62
|
+
|
63
|
+
| Option | Description |
|
64
|
+
|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
|
65
|
+
| delivery_interval | The number of seconds between background message deliveries. Disable timer-based background deliveries by setting this to 0. |
|
66
|
+
| delivery_threshold | The number of buffered messages that will trigger a background message delivery. Disable buffer size based background deliveries by setting this to 0.|
|
67
|
+
| required_acks | The number of Kafka replicas that must acknowledge messages before they're considered as successfully written. |
|
68
|
+
| ack_timeout | A timeout executed by a broker when the client is sending messages to it. |
|
69
|
+
| max_retries | The number of retries when attempting to deliver messages. |
|
70
|
+
| retry_backoff | The number of seconds to wait after a failed attempt to send messages to a Kafka broker before retrying. |
|
71
|
+
| max_buffer_bytesize | The maximum number of bytes allowed in the buffer before new messages are rejected. |
|
72
|
+
| max_buffer_size | The maximum number of messages allowed in the buffer before new messages are rejected. |
|
73
|
+
| max_queue_size | The maximum number of messages allowed in the queue before new messages are rejected. |
|
74
|
+
| sasl_plain_username | The username used to authenticate. |
|
75
|
+
| sasl_plain_password | The password used to authenticate. |
|
76
|
+
|
77
|
+
This configuration can be also placed in *config/initializers* and can vary based on the environment:
|
64
78
|
|
65
79
|
```ruby
|
66
80
|
WaterDrop.setup do |config|
|
67
|
-
config.
|
68
|
-
config.
|
69
|
-
config.connection_pool.timeout = 1
|
70
|
-
config.kafka.seed_brokers = [Rails.env.production? ? 'prod-host:9091' : 'localhost:9092']
|
71
|
-
config.raise_on_failure = Rails.env.production?
|
81
|
+
config.deliver = Rails.env.production?
|
82
|
+
config.kafka.seed_brokers = [Rails.env.production? ? 'kafka://prod-host:9091' : 'kafka://localhost:9092']
|
72
83
|
end
|
73
84
|
```
|
74
85
|
|
75
86
|
## Usage
|
76
87
|
|
77
|
-
|
78
|
-
|
79
|
-
To send Kafka messages, just create and send messages directly:
|
88
|
+
To send Kafka messages, just use one of the producers:
|
80
89
|
|
81
90
|
```ruby
|
82
|
-
|
83
|
-
|
91
|
+
WaterDrop::SyncProducer.call('message', topic: 'my-topic')
|
92
|
+
|
93
|
+
# or for async
|
84
94
|
|
85
|
-
|
86
|
-
message.send!
|
95
|
+
WaterDrop::AsyncProducer.call('message', topic: 'my-topic')
|
87
96
|
```
|
88
97
|
|
89
|
-
|
98
|
+
Both ```SyncProducer``` and ```AsyncProducer``` accept following options:
|
90
99
|
|
91
|
-
|
100
|
+
| Option | Required | Value type | Description |
|
101
|
+
|-------------------- |----------|----------------|---------------------------------------------------------------------|
|
102
|
+
| ```topic``` | true | String, Symbol | The Kafka topic that should be written to |
|
103
|
+
| ```key``` | false | String | The key that should be set on the Kafka message |
|
104
|
+
| ```partition``` | false | Integer | A specific partition number that should be written to |
|
105
|
+
| ```partition_key``` | false | String | A string that can be used to deterministically select the partition |
|
92
106
|
|
93
|
-
|
107
|
+
Keep in mind, that message you want to send should be either binary or stringified (to_s, to_json, etc).
|
94
108
|
|
95
109
|
## References
|
96
110
|
|
97
111
|
* [Karafka framework](https://github.com/karafka/karafka)
|
98
|
-
* [Waterdrop](https://github.com/karafka/waterdrop)
|
99
|
-
* [Worker Glass](https://github.com/karafka/worker-glass)
|
100
|
-
* [Envlogic](https://github.com/karafka/envlogic)
|
101
|
-
* [Null Logger](https://github.com/karafka/null-logger)
|
102
|
-
* [Aspector](https://github.com/gcao/aspector)
|
103
112
|
* [WaterDrop Travis CI](https://travis-ci.org/karafka/waterdrop)
|
104
|
-
* [WaterDrop
|
113
|
+
* [WaterDrop Coditsu](https://app.coditsu.io/karafka/repositories/waterdrop)
|
105
114
|
|
106
115
|
## Note on Patches/Pull Requests
|
107
116
|
|
108
117
|
Fork the project.
|
109
118
|
Make your feature addition or bug fix.
|
110
|
-
Add tests for it. This is important so
|
111
|
-
Commit, do not mess with
|
119
|
+
Add tests for it. This is important so we don't break it in a future versions unintentionally.
|
120
|
+
Commit, do not mess with version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull). Send me a pull request. Bonus points for topic branches.
|
121
|
+
|
122
|
+
[![coditsu](https://coditsu.io/assets/quality_bar.svg)](https://app.coditsu.io/karafka/repositories/waterdrop)
|
112
123
|
|
113
|
-
[
|
124
|
+
Each pull request must pass our quality requirements. To check if everything is as it should be, we use [Coditsu](https://coditsu.io) that combines multiple linters and code analyzers for both code and documentation.
|
114
125
|
|
115
|
-
|
126
|
+
Unfortunately, it does not yet support independent forks, however you should be fine by looking at what we require.
|
116
127
|
|
117
128
|
Please run:
|
118
129
|
|
119
130
|
```bash
|
120
|
-
bundle exec
|
131
|
+
bundle exec rspec
|
121
132
|
```
|
122
133
|
|
123
134
|
to check if everything is in order. After that you can submit a pull request.
|
data/config/errors.yml
ADDED
data/lib/water_drop.rb
CHANGED
@@ -2,49 +2,62 @@
|
|
2
2
|
|
3
3
|
# External components
|
4
4
|
%w[
|
5
|
-
rake
|
6
|
-
rubygems
|
7
|
-
bundler
|
8
|
-
logger
|
9
|
-
pathname
|
10
5
|
json
|
11
|
-
|
12
|
-
forwardable
|
13
|
-
connection_pool
|
6
|
+
delivery_boy
|
14
7
|
null_logger
|
15
8
|
dry-configurable
|
9
|
+
dry-validation
|
16
10
|
].each { |lib| require lib }
|
17
11
|
|
18
12
|
# Internal components
|
19
13
|
base_path = File.dirname(__FILE__) + '/water_drop'
|
20
14
|
|
21
|
-
%w[
|
22
|
-
version
|
23
|
-
producer_proxy
|
24
|
-
pool
|
25
|
-
config
|
26
|
-
message
|
27
|
-
].each { |lib| require "#{base_path}/#{lib}" }
|
28
|
-
|
29
15
|
# WaterDrop library
|
30
16
|
module WaterDrop
|
31
17
|
class << self
|
32
|
-
|
33
|
-
|
34
|
-
# @return [Logger] logger that we want to use
|
35
|
-
def logger
|
36
|
-
@logger ||= NullLogger.new
|
37
|
-
end
|
18
|
+
attr_accessor :logger
|
38
19
|
|
39
20
|
# Sets up the whole configuration
|
40
21
|
# @param [Block] block configuration block
|
41
22
|
def setup(&block)
|
42
23
|
Config.setup(&block)
|
24
|
+
|
25
|
+
DeliveryBoy.logger = self.logger = config.logger
|
26
|
+
|
27
|
+
# Recursive lambda for mapping config down to delivery boy
|
28
|
+
applier = lambda { |db_config, h|
|
29
|
+
h.each do |k, v|
|
30
|
+
applier.call(db_config, v) && next if v.is_a?(Hash)
|
31
|
+
next unless db_config.respond_to?(:"#{k}=")
|
32
|
+
db_config.public_send(:"#{k}=", v)
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
DeliveryBoy.config.tap do |config|
|
37
|
+
config.brokers = Config.config.kafka.seed_brokers
|
38
|
+
applier.call(config, Config.config.to_h)
|
39
|
+
end
|
43
40
|
end
|
44
41
|
|
45
42
|
# @return [WaterDrop::Config] config instance
|
46
43
|
def config
|
47
44
|
Config.config
|
48
45
|
end
|
46
|
+
|
47
|
+
# @return [String] root path of this gem
|
48
|
+
def gem_root
|
49
|
+
Pathname.new(File.expand_path('../..', __FILE__))
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
53
|
+
|
54
|
+
%w[
|
55
|
+
version
|
56
|
+
schemas/message_options
|
57
|
+
schemas/config
|
58
|
+
config
|
59
|
+
errors
|
60
|
+
base_producer
|
61
|
+
sync_producer
|
62
|
+
async_producer
|
63
|
+
].each { |lib| require "#{base_path}/#{lib}" }
|
@@ -0,0 +1,35 @@
|
|
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
|
+
class << self
|
8
|
+
# Delivery boy method name that we use to invoke producer action
|
9
|
+
attr_accessor :method_name
|
10
|
+
|
11
|
+
# Performs message delivery using method_name method
|
12
|
+
# @param message [String] message that we want to send to Kafka
|
13
|
+
# @param options [Hash] options (including topic) for producer
|
14
|
+
# @raise [WaterDrop::Errors::InvalidMessageOptions] raised when message options are
|
15
|
+
# somehow invalid and we cannot perform delivery because of that
|
16
|
+
def call(message, options)
|
17
|
+
validate!(options)
|
18
|
+
return unless WaterDrop.config.deliver
|
19
|
+
DeliveryBoy.public_send(method_name, message, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Runs the message options validations and raises an error if anything is invalid
|
25
|
+
# @param options [Hash] hash that we want to validate
|
26
|
+
# @raise [WaterDrop::Errors::InvalidMessageOptions] raised when message options are
|
27
|
+
# somehow invalid and we cannot perform delivery because of that
|
28
|
+
def validate!(options)
|
29
|
+
validation_result = Schemas::MessageOptions.call(options)
|
30
|
+
return true if validation_result.success?
|
31
|
+
raise Errors::InvalidMessageOptions, validation_result.errors
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
data/lib/water_drop/config.rb
CHANGED
@@ -1,35 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Configuration and descriptions are based on the delivery boy zendesk gem
|
4
|
+
# @see https://github.com/zendesk/delivery_boy
|
3
5
|
module WaterDrop
|
4
6
|
# Configurator for setting up all options required by WaterDrop
|
5
7
|
class Config
|
6
8
|
extend Dry::Configurable
|
7
9
|
|
10
|
+
# WaterDrop options
|
8
11
|
# option client_id [String] identifier of this producer
|
9
12
|
setting :client_id, 'waterdrop'
|
10
|
-
# option
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
setting :
|
15
|
-
# @option raise_on_failure [Boolean] Should raise error when failed to deliver a message
|
16
|
-
setting :raise_on_failure
|
13
|
+
# option [Instance, nil] logger that we want to use or nil to fallback to ruby-kafka logger
|
14
|
+
setting :logger, NullLogger.new
|
15
|
+
# option [Boolean] should we send messages. Setting this to false can be really useful when
|
16
|
+
# testing and or developing because when set to false, won't actually ping Kafka
|
17
|
+
setting :deliver, true
|
17
18
|
|
18
|
-
#
|
19
|
-
setting :connection_pool do
|
20
|
-
# Connection pool size for producers. Note that we take a bigger number because there
|
21
|
-
# are cases when we might have more sidekiq threads than Karafka consumers (small app)
|
22
|
-
# or the opposite for bigger systems
|
23
|
-
setting :size, 2
|
24
|
-
# How long should we wait for a working resource from the pool before rising timeout
|
25
|
-
# With a proper connection pool size, this should never happen
|
26
|
-
setting :timeout, 5
|
27
|
-
end
|
28
|
-
|
29
|
-
# option kafka [Hash] - optional - kafka configuration options (hosts)
|
19
|
+
# Settings directly related to the Kafka driver
|
30
20
|
setting :kafka do
|
31
|
-
#
|
21
|
+
# option [Array<String>] Array that contains Kafka seed broker hosts with ports
|
32
22
|
setting :seed_brokers
|
23
|
+
|
24
|
+
# Network timeouts
|
33
25
|
# option connect_timeout [Integer] Sets the number of seconds to wait while connecting to
|
34
26
|
# a broker for the first time. When ruby-kafka initializes, it needs to connect to at
|
35
27
|
# least one host.
|
@@ -38,15 +30,56 @@ module WaterDrop
|
|
38
30
|
# writing to a socket connection to a broker. After this timeout expires the connection
|
39
31
|
# will be killed. Note that some Kafka operations are by definition long-running, such as
|
40
32
|
# waiting for new messages to arrive in a partition, so don't set this value too low
|
41
|
-
setting :socket_timeout,
|
33
|
+
setting :socket_timeout, 30
|
34
|
+
|
35
|
+
# Buffering for async producer
|
36
|
+
# @option [Integer] The maximum number of bytes allowed in the buffer before new messages
|
37
|
+
# are rejected.
|
38
|
+
setting :max_buffer_bytesize, 10_000_000
|
39
|
+
# @option [Integer] The maximum number of messages allowed in the buffer before new messages
|
40
|
+
# are rejected.
|
41
|
+
setting :max_buffer_size, 1000
|
42
|
+
# @option [Integer] The maximum number of messages allowed in the queue before new messages
|
43
|
+
# are rejected. The queue is used to ferry messages from the foreground threads of your
|
44
|
+
# application to the background thread that buffers and delivers messages.
|
45
|
+
setting :max_queue_size, 1000
|
46
|
+
|
47
|
+
# option [Integer] A timeout executed by a broker when the client is sending messages to it.
|
48
|
+
# It defines the number of seconds the broker should wait for replicas to acknowledge the
|
49
|
+
# write before responding to the client with an error. As such, it relates to the
|
50
|
+
# required_acks setting. It should be set lower than socket_timeout.
|
51
|
+
setting :ack_timeout, 5
|
52
|
+
# option [Integer] The number of seconds between background message
|
53
|
+
# deliveries. Default is 10 seconds. Disable timer-based background deliveries by
|
54
|
+
# setting this to 0.
|
55
|
+
setting :delivery_interval, 10
|
56
|
+
# option [Integer] The number of buffered messages that will trigger a background message
|
57
|
+
# delivery. Default is 100 messages. Disable buffer size based background deliveries by
|
58
|
+
# setting this to 0.
|
59
|
+
setting :delivery_threshold, 100
|
60
|
+
|
61
|
+
# option [Integer] The number of retries when attempting to deliver messages.
|
62
|
+
setting :max_retries, 2
|
63
|
+
# option [Integer]
|
64
|
+
setting :required_acks, -1
|
65
|
+
# option [Integer]
|
66
|
+
setting :retry_backoff, 1
|
67
|
+
|
68
|
+
# option [Integer] The minimum number of messages that must be buffered before compression is
|
69
|
+
# attempted. By default only one message is required. Only relevant if compression_codec
|
70
|
+
# is set.
|
71
|
+
setting :compression_threshold, 1
|
72
|
+
# option [Symbol] The codec used to compress messages. Must be either snappy or gzip.
|
73
|
+
setting :compression_codec, nil
|
74
|
+
|
42
75
|
# SSL authentication related settings
|
43
76
|
# option ca_cert [String] SSL CA certificate
|
44
77
|
setting :ssl_ca_cert, nil
|
45
78
|
# option ssl_ca_cert_file_path [String] SSL CA certificate file path
|
46
79
|
setting :ssl_ca_cert_file_path, nil
|
47
|
-
# option
|
80
|
+
# option ssl_client_cert [String] SSL client certificate
|
48
81
|
setting :ssl_client_cert, nil
|
49
|
-
# option
|
82
|
+
# option ssl_client_cert_key [String] SSL client certificate password
|
50
83
|
setting :ssl_client_cert_key, nil
|
51
84
|
# option sasl_gssapi_principal [String] sasl principal
|
52
85
|
setting :sasl_gssapi_principal, nil
|
@@ -60,6 +93,17 @@ module WaterDrop
|
|
60
93
|
setting :sasl_plain_password, nil
|
61
94
|
end
|
62
95
|
|
96
|
+
# option producer [Hash] - optional - producer configuration options
|
97
|
+
setting :producer do
|
98
|
+
# option compression_codec [Symbol] Sets producer compression codec
|
99
|
+
# More: https://github.com/zendesk/ruby-kafka#compression
|
100
|
+
# More: https://github.com/zendesk/ruby-kafka/blob/master/lib/kafka/compression.rb
|
101
|
+
setting :compression_codec, nil
|
102
|
+
# option compression_codec [Integer] Sets producer compression threshold
|
103
|
+
# More: https://github.com/zendesk/ruby-kafka#compression
|
104
|
+
setting :compression_threshold, 1
|
105
|
+
end
|
106
|
+
|
63
107
|
class << self
|
64
108
|
# Configurating method
|
65
109
|
# @yield Runs a block of code providing a config singleton instance to it
|
@@ -67,8 +111,21 @@ module WaterDrop
|
|
67
111
|
def setup
|
68
112
|
configure do |config|
|
69
113
|
yield(config)
|
114
|
+
validate!(config.to_h)
|
70
115
|
end
|
71
116
|
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
# Validates the configuration and if anything is wrong, will raise an exception
|
121
|
+
# @param config_hash [Hash] config hash with setup details
|
122
|
+
# @raise [WaterDrop::Errors::InvalidConfiguration] raised when something is wrong with
|
123
|
+
# the configuration
|
124
|
+
def validate!(config_hash)
|
125
|
+
validation_result = Schemas::Config.call(config_hash)
|
126
|
+
return true if validation_result.success?
|
127
|
+
raise Errors::InvalidConfiguration, validation_result.errors
|
128
|
+
end
|
72
129
|
end
|
73
130
|
end
|
74
131
|
end
|
@@ -0,0 +1,15 @@
|
|
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 schema
|
10
|
+
InvalidConfiguration = Class.new(BaseError)
|
11
|
+
|
12
|
+
# Raised when we try to send message with invalid optionss
|
13
|
+
InvalidMessageOptions = Class.new(BaseError)
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
# Namespace for all the schemas for config validations
|
5
|
+
module Schemas
|
6
|
+
# Schema with validation rules for WaterDrop configuration details
|
7
|
+
Config = Dry::Validation.Schema do
|
8
|
+
# Valid uri schemas of Kafka broker url
|
9
|
+
URI_SCHEMES = %w[
|
10
|
+
kafka
|
11
|
+
kafka+ssl
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
# All encryption related options keys
|
15
|
+
ENCRYPTION_OPTIONS_KEYS = %i[
|
16
|
+
ssl_ca_cert
|
17
|
+
ssl_ca_cert_file_path
|
18
|
+
ssl_client_cert
|
19
|
+
ssl_client_cert_key
|
20
|
+
sasl_plain_authzid
|
21
|
+
sasl_plain_username
|
22
|
+
sasl_plain_password
|
23
|
+
sasl_gssapi_principal
|
24
|
+
sasl_gssapi_keytab
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
configure do
|
28
|
+
config.messages_file = File.join(
|
29
|
+
WaterDrop.gem_root, 'config', 'errors.yml'
|
30
|
+
)
|
31
|
+
|
32
|
+
# Uri validator to check if uri is in a Kafka acceptable format
|
33
|
+
# @param uri [String] uri we want to validate
|
34
|
+
# @return [Boolean] true if it is a valid uri, otherwise false
|
35
|
+
def broker_schema?(uri)
|
36
|
+
uri = URI.parse(uri)
|
37
|
+
URI_SCHEMES.include?(uri.scheme) && uri.port
|
38
|
+
rescue URI::InvalidURIError
|
39
|
+
false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
required(:client_id).filled(:str?, format?: Schemas::TOPIC_REGEXP)
|
44
|
+
required(:logger).filled
|
45
|
+
required(:deliver).filled(:bool?)
|
46
|
+
|
47
|
+
required(:kafka).schema do
|
48
|
+
required(:seed_brokers).filled { each(:broker_schema?) }
|
49
|
+
required(:connect_timeout).filled { (int? | float?) & gt?(0) }
|
50
|
+
required(:socket_timeout).filled { (int? | float?) & gt?(0) }
|
51
|
+
required(:compression_threshold).filled(:int?, gteq?: 1)
|
52
|
+
optional(:compression_codec).maybe(included_in?: %i[snappy gzip])
|
53
|
+
|
54
|
+
required(:max_buffer_bytesize).filled(:int?, gt?: 0)
|
55
|
+
required(:max_buffer_size).filled(:int?, gt?: 0)
|
56
|
+
required(:max_queue_size).filled(:int?, gt?: 0)
|
57
|
+
|
58
|
+
required(:ack_timeout).filled(:int?, gt?: 0)
|
59
|
+
required(:delivery_interval).filled(:int?, gteq?: 0)
|
60
|
+
required(:delivery_threshold).filled(:int?, gteq?: 0)
|
61
|
+
|
62
|
+
required(:max_retries).filled(:int?, gteq?: 0)
|
63
|
+
required(:retry_backoff).filled(:int?, gteq?: 0)
|
64
|
+
required(:required_acks).filled(included_in?: [1, 0, -1, :all])
|
65
|
+
|
66
|
+
ENCRYPTION_OPTIONS_KEYS.each do |encryption_attribute|
|
67
|
+
optional(encryption_attribute).maybe(:str?)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
module Schemas
|
5
|
+
# Regexp to check that topic has a valid format
|
6
|
+
TOPIC_REGEXP = /\A(\w|\-|\.)+\z/
|
7
|
+
|
8
|
+
# Schema with validation rules for validating that all the message options that
|
9
|
+
# we provide to producer ale valid and usable
|
10
|
+
# @note Does not validate message itself as it is not our concern
|
11
|
+
MessageOptions = Dry::Validation.Schema do
|
12
|
+
required(:topic).filled(:str?, format?: TOPIC_REGEXP)
|
13
|
+
optional(:key).maybe(:str?, :filled?)
|
14
|
+
optional(:partition).filled(:int?, gteq?: 0)
|
15
|
+
optional(:partition_key).maybe(:str?, :filled?)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/water_drop/version.rb
CHANGED
data/waterdrop.gemspec
CHANGED
@@ -9,22 +9,18 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.name = 'waterdrop'
|
10
10
|
spec.version = ::WaterDrop::VERSION
|
11
11
|
spec.platform = Gem::Platform::RUBY
|
12
|
-
spec.authors = ['Maciej Mensfeld'
|
13
|
-
spec.email = %w[maciej@mensfeld.pl
|
12
|
+
spec.authors = ['Maciej Mensfeld']
|
13
|
+
spec.email = %w[maciej@mensfeld.pl]
|
14
14
|
spec.homepage = 'https://github.com/karafka/waterdrop'
|
15
15
|
spec.summary = ' Kafka messaging made easy! '
|
16
16
|
spec.description = spec.summary
|
17
17
|
spec.license = 'MIT'
|
18
18
|
|
19
|
-
spec.add_dependency '
|
20
|
-
spec.add_dependency '
|
21
|
-
spec.add_dependency '
|
22
|
-
spec.add_dependency 'connection_pool', '>= 0'
|
19
|
+
spec.add_dependency 'delivery_boy', '>= 0.2.2'
|
20
|
+
spec.add_dependency 'dry-configurable', '~> 0.7'
|
21
|
+
spec.add_dependency 'dry-validation', '~> 0.11'
|
23
22
|
spec.add_dependency 'null-logger'
|
24
|
-
spec.add_dependency 'dry-configurable', '~> 0.6'
|
25
23
|
|
26
|
-
spec.add_development_dependency 'rspec', '~> 3.6.0'
|
27
|
-
spec.add_development_dependency 'simplecov', '~> 0.14.1'
|
28
24
|
spec.required_ruby_version = '>= 2.2.0'
|
29
25
|
|
30
26
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
metadata
CHANGED
@@ -1,72 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waterdrop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0.alpha1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maciej Mensfeld
|
8
|
-
- Pavlo Vavruk
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2017-
|
11
|
+
date: 2017-10-30 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
14
|
+
name: delivery_boy
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
16
|
requirements:
|
18
17
|
- - ">="
|
19
18
|
- !ruby/object:Gem::Version
|
20
|
-
version:
|
21
|
-
type: :runtime
|
22
|
-
prerelease: false
|
23
|
-
version_requirements: !ruby/object:Gem::Requirement
|
24
|
-
requirements:
|
25
|
-
- - ">="
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
version: '0'
|
28
|
-
- !ruby/object:Gem::Dependency
|
29
|
-
name: rake
|
30
|
-
requirement: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
19
|
+
version: 0.2.2
|
35
20
|
type: :runtime
|
36
21
|
prerelease: false
|
37
22
|
version_requirements: !ruby/object:Gem::Requirement
|
38
23
|
requirements:
|
39
24
|
- - ">="
|
40
25
|
- !ruby/object:Gem::Version
|
41
|
-
version:
|
26
|
+
version: 0.2.2
|
42
27
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
28
|
+
name: dry-configurable
|
44
29
|
requirement: !ruby/object:Gem::Requirement
|
45
30
|
requirements:
|
46
31
|
- - "~>"
|
47
32
|
- !ruby/object:Gem::Version
|
48
|
-
version: '0.
|
33
|
+
version: '0.7'
|
49
34
|
type: :runtime
|
50
35
|
prerelease: false
|
51
36
|
version_requirements: !ruby/object:Gem::Requirement
|
52
37
|
requirements:
|
53
38
|
- - "~>"
|
54
39
|
- !ruby/object:Gem::Version
|
55
|
-
version: '0.
|
40
|
+
version: '0.7'
|
56
41
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
42
|
+
name: dry-validation
|
58
43
|
requirement: !ruby/object:Gem::Requirement
|
59
44
|
requirements:
|
60
|
-
- - "
|
45
|
+
- - "~>"
|
61
46
|
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
47
|
+
version: '0.11'
|
63
48
|
type: :runtime
|
64
49
|
prerelease: false
|
65
50
|
version_requirements: !ruby/object:Gem::Requirement
|
66
51
|
requirements:
|
67
|
-
- - "
|
52
|
+
- - "~>"
|
68
53
|
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
54
|
+
version: '0.11'
|
70
55
|
- !ruby/object:Gem::Dependency
|
71
56
|
name: null-logger
|
72
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,52 +66,9 @@ dependencies:
|
|
81
66
|
- - ">="
|
82
67
|
- !ruby/object:Gem::Version
|
83
68
|
version: '0'
|
84
|
-
- !ruby/object:Gem::Dependency
|
85
|
-
name: dry-configurable
|
86
|
-
requirement: !ruby/object:Gem::Requirement
|
87
|
-
requirements:
|
88
|
-
- - "~>"
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
version: '0.6'
|
91
|
-
type: :runtime
|
92
|
-
prerelease: false
|
93
|
-
version_requirements: !ruby/object:Gem::Requirement
|
94
|
-
requirements:
|
95
|
-
- - "~>"
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
version: '0.6'
|
98
|
-
- !ruby/object:Gem::Dependency
|
99
|
-
name: rspec
|
100
|
-
requirement: !ruby/object:Gem::Requirement
|
101
|
-
requirements:
|
102
|
-
- - "~>"
|
103
|
-
- !ruby/object:Gem::Version
|
104
|
-
version: 3.6.0
|
105
|
-
type: :development
|
106
|
-
prerelease: false
|
107
|
-
version_requirements: !ruby/object:Gem::Requirement
|
108
|
-
requirements:
|
109
|
-
- - "~>"
|
110
|
-
- !ruby/object:Gem::Version
|
111
|
-
version: 3.6.0
|
112
|
-
- !ruby/object:Gem::Dependency
|
113
|
-
name: simplecov
|
114
|
-
requirement: !ruby/object:Gem::Requirement
|
115
|
-
requirements:
|
116
|
-
- - "~>"
|
117
|
-
- !ruby/object:Gem::Version
|
118
|
-
version: 0.14.1
|
119
|
-
type: :development
|
120
|
-
prerelease: false
|
121
|
-
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
requirements:
|
123
|
-
- - "~>"
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: 0.14.1
|
126
69
|
description: Kafka messaging made easy!
|
127
70
|
email:
|
128
71
|
- maciej@mensfeld.pl
|
129
|
-
- pavlo.vavruk@gmail.com
|
130
72
|
executables: []
|
131
73
|
extensions: []
|
132
74
|
extra_rdoc_files: []
|
@@ -141,12 +83,15 @@ files:
|
|
141
83
|
- Gemfile.lock
|
142
84
|
- MIT-LICENCE
|
143
85
|
- README.md
|
144
|
-
-
|
86
|
+
- config/errors.yml
|
145
87
|
- lib/water_drop.rb
|
88
|
+
- lib/water_drop/async_producer.rb
|
89
|
+
- lib/water_drop/base_producer.rb
|
146
90
|
- lib/water_drop/config.rb
|
147
|
-
- lib/water_drop/
|
148
|
-
- lib/water_drop/
|
149
|
-
- lib/water_drop/
|
91
|
+
- lib/water_drop/errors.rb
|
92
|
+
- lib/water_drop/schemas/config.rb
|
93
|
+
- lib/water_drop/schemas/message_options.rb
|
94
|
+
- lib/water_drop/sync_producer.rb
|
150
95
|
- lib/water_drop/version.rb
|
151
96
|
- lib/waterdrop.rb
|
152
97
|
- waterdrop.gemspec
|
@@ -165,12 +110,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
165
110
|
version: 2.2.0
|
166
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
167
112
|
requirements:
|
168
|
-
- - "
|
113
|
+
- - ">"
|
169
114
|
- !ruby/object:Gem::Version
|
170
|
-
version:
|
115
|
+
version: 1.3.1
|
171
116
|
requirements: []
|
172
117
|
rubyforge_project:
|
173
|
-
rubygems_version: 2.6.
|
118
|
+
rubygems_version: 2.6.13
|
174
119
|
signing_key:
|
175
120
|
specification_version: 4
|
176
121
|
summary: Kafka messaging made easy!
|
data/Rakefile
DELETED
data/lib/water_drop/message.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WaterDrop
|
4
|
-
# Message class which encapsulate single Kafka message logic and its delivery
|
5
|
-
class Message
|
6
|
-
attr_reader :topic, :message, :options
|
7
|
-
|
8
|
-
# @param topic [String, Symbol] a topic to which we want to send a message
|
9
|
-
# @param message [Object] any object that can be serialized to a JSON string or
|
10
|
-
# that can be casted to a string
|
11
|
-
# @param options [Hash] (optional) additional options to pass to the Kafka producer
|
12
|
-
# @return [WaterDrop::Message] WaterDrop message instance
|
13
|
-
# @example Creating a new message
|
14
|
-
# WaterDrop::Message.new(topic, message)
|
15
|
-
def initialize(topic, message, options = {})
|
16
|
-
@topic = topic.to_s
|
17
|
-
@message = message
|
18
|
-
@options = options
|
19
|
-
end
|
20
|
-
|
21
|
-
# Sents a current message to Kafka
|
22
|
-
# @note Won't send any messages if send_messages config flag is set to false
|
23
|
-
# @example Set a message
|
24
|
-
# WaterDrop::Message.new(topic, message).send!
|
25
|
-
def send!
|
26
|
-
return true unless ::WaterDrop.config.send_messages
|
27
|
-
|
28
|
-
Pool.with { |producer| producer.send_message(self) }
|
29
|
-
|
30
|
-
::WaterDrop.logger.info("Message #{message} was sent to topic '#{topic}'")
|
31
|
-
rescue StandardError => e
|
32
|
-
# Even if we dont reraise this exception, it should log that it happened
|
33
|
-
::WaterDrop.logger.error(e)
|
34
|
-
# Reraise if we want to raise on failure
|
35
|
-
# Ignore if we dont want to know that something went wrong
|
36
|
-
return unless ::WaterDrop.config.raise_on_failure
|
37
|
-
raise(e)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
data/lib/water_drop/pool.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WaterDrop
|
4
|
-
# Raw Kafka producers connection pool for WaterDrop messages delivery
|
5
|
-
module Pool
|
6
|
-
extend SingleForwardable
|
7
|
-
# Delegate directly to pool
|
8
|
-
def_delegators :pool, :with
|
9
|
-
|
10
|
-
class << self
|
11
|
-
# @return [::ConnectionPool] connection pool instance that we can then use
|
12
|
-
def pool
|
13
|
-
@pool ||= ConnectionPool.new(
|
14
|
-
size: ::WaterDrop.config.connection_pool.size,
|
15
|
-
timeout: ::WaterDrop.config.connection_pool.timeout
|
16
|
-
) do
|
17
|
-
WaterDrop::ProducerProxy.new
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,80 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WaterDrop
|
4
|
-
# Proxy object for a producer (sender) objects that are inside pool
|
5
|
-
# We use it to provide additional timeout monitoring layer
|
6
|
-
# There used to be an issue with Poseidon (previous engine for this lib)
|
7
|
-
# usage of sockets that are old and not used - that's why we just
|
8
|
-
# reinitialize connection if the connection layer is not being used for too long
|
9
|
-
# We keep this logic to avoid problems just in case. If those problems won't occur
|
10
|
-
# with Ruby-Kafka, we will drop it
|
11
|
-
class ProducerProxy
|
12
|
-
# How long should be object considered alive if nothing is being
|
13
|
-
# send using it. After that time, we will recreate the connection
|
14
|
-
LIFE_TIME = 5 * 60 # 5 minute
|
15
|
-
|
16
|
-
# If sending fails - how many times we should try with a new connection
|
17
|
-
MAX_SEND_RETRIES = 1
|
18
|
-
|
19
|
-
# @return [WaterDrop::ProducerProxy] proxy object to Kafka::Producer
|
20
|
-
# @note To ignore @last_usage nil case - we just assume that it is being
|
21
|
-
# first used when we create it
|
22
|
-
def initialize
|
23
|
-
touch
|
24
|
-
@attempts = 0
|
25
|
-
end
|
26
|
-
|
27
|
-
# Sends message to Kafka
|
28
|
-
# @param message [WaterDrop::Message] message that we want to send
|
29
|
-
# @note If something goes wrong it will assume that producer is corrupted and will try to
|
30
|
-
# create a new one
|
31
|
-
# @example Send 1 message
|
32
|
-
# ProducerProxy.new.send_message(WaterDrop::Message.new(topic, message))
|
33
|
-
def send_message(message)
|
34
|
-
touch
|
35
|
-
producer.produce(
|
36
|
-
message.message,
|
37
|
-
{ topic: message.topic }.merge(message.options)
|
38
|
-
)
|
39
|
-
producer.deliver_messages
|
40
|
-
rescue StandardError => e
|
41
|
-
reload!
|
42
|
-
|
43
|
-
retry if (@attempts += 1) <= MAX_SEND_RETRIES
|
44
|
-
|
45
|
-
raise(e)
|
46
|
-
ensure
|
47
|
-
@attempts = 0
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
# Refreshes last usage value with current time
|
53
|
-
def touch
|
54
|
-
@last_usage = Time.now
|
55
|
-
end
|
56
|
-
|
57
|
-
# @return [Kafka::Producer] producer instance to which we can forward method requests
|
58
|
-
def producer
|
59
|
-
reload! if dead?
|
60
|
-
@producer ||= Kafka.new(
|
61
|
-
{
|
62
|
-
client_id: ::WaterDrop.config.client_id,
|
63
|
-
logger: ::WaterDrop.config.logger
|
64
|
-
}.merge(::WaterDrop.config.kafka.to_h)
|
65
|
-
).producer
|
66
|
-
end
|
67
|
-
|
68
|
-
# @return [Boolean] true if we cannot use producer anymore because it was not used for a
|
69
|
-
# long time
|
70
|
-
def dead?
|
71
|
-
@last_usage + LIFE_TIME < Time.now
|
72
|
-
end
|
73
|
-
|
74
|
-
# Resets a producer so a new one will be created once requested
|
75
|
-
def reload!
|
76
|
-
@producer&.shutdown
|
77
|
-
@producer = nil
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|