waterdrop 0.4.0 → 1.0.0.alpha1
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
- 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
|
[](https://travis-ci.org/karafka/waterdrop)
|
|
4
|
-
[](https://codeclimate.com/github/karafka/waterdrop)
|
|
5
|
-
[](http://badge.fury.io/rb/waterdrop)
|
|
6
4
|
[](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
|
+
[](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
|