delivery_boy 0.2.8.beta1 → 0.2.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +33 -0
- data/.gitignore +1 -1
- data/CHANGELOG +3 -0
- data/README.md +37 -6
- data/delivery_boy.gemspec +1 -1
- data/lib/delivery_boy.rb +38 -1
- data/lib/delivery_boy/config.rb +3 -0
- data/lib/delivery_boy/fake.rb +36 -5
- data/lib/delivery_boy/instance.rb +11 -0
- data/lib/delivery_boy/version.rb +1 -1
- metadata +8 -9
- data/circle.yml +0 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7d1d10c8e6cf173eeca342252bf3e47327f08200fd6e34207e803d6ef490ead7
|
4
|
+
data.tar.gz: 4e7d841926323cc46aefec6cdf93812796ae2a454a562e4ad0c6d02eb79b229e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6388bf8cadefd13a20ceabb7e887171e0f6c8dc42cb5f31a5370da7578bb5e5913192a20161d0370af43cc3a88798f74417886657f887e636fec2586dddfd27a
|
7
|
+
data.tar.gz: e1cbea934e040fcea51b9730e0340376aef0f59ede9c8d82199bba3b0adc60c309ef87a86ade3b7a6bb1d9f7136403dfb6d64408693974f4832f85a04ce24500
|
@@ -0,0 +1,33 @@
|
|
1
|
+
version: 2
|
2
|
+
jobs:
|
3
|
+
build:
|
4
|
+
docker:
|
5
|
+
- image: circleci/ruby:2.5.1-node
|
6
|
+
environment:
|
7
|
+
LOG_LEVEL: DEBUG
|
8
|
+
- image: wurstmeister/zookeeper
|
9
|
+
- image: wurstmeister/kafka:2.11-2.0.0
|
10
|
+
environment:
|
11
|
+
KAFKA_ADVERTISED_HOST_NAME: localhost
|
12
|
+
KAFKA_ADVERTISED_PORT: 9092
|
13
|
+
KAFKA_PORT: 9092
|
14
|
+
KAFKA_ZOOKEEPER_CONNECT: localhost:2181
|
15
|
+
KAFKA_DELETE_TOPIC_ENABLE: true
|
16
|
+
- image: wurstmeister/kafka:2.11-2.0.0
|
17
|
+
environment:
|
18
|
+
KAFKA_ADVERTISED_HOST_NAME: localhost
|
19
|
+
KAFKA_ADVERTISED_PORT: 9093
|
20
|
+
KAFKA_PORT: 9093
|
21
|
+
KAFKA_ZOOKEEPER_CONNECT: localhost:2181
|
22
|
+
KAFKA_DELETE_TOPIC_ENABLE: true
|
23
|
+
- image: wurstmeister/kafka:2.11-2.0.0
|
24
|
+
environment:
|
25
|
+
KAFKA_ADVERTISED_HOST_NAME: localhost
|
26
|
+
KAFKA_ADVERTISED_PORT: 9094
|
27
|
+
KAFKA_PORT: 9094
|
28
|
+
KAFKA_ZOOKEEPER_CONNECT: localhost:2181
|
29
|
+
KAFKA_DELETE_TOPIC_ENABLE: true
|
30
|
+
steps:
|
31
|
+
- checkout
|
32
|
+
- run: bundle install --path vendor/bundle
|
33
|
+
- run: bundle exec rspec
|
data/.gitignore
CHANGED
data/CHANGELOG
CHANGED
data/README.md
CHANGED
@@ -59,7 +59,34 @@ end
|
|
59
59
|
|
60
60
|
In addition to improving response time, delivering messages asynchronously also protects your application against Kafka availability issues -- if messages cannot be delivered, they'll be buffered for later and retried automatically.
|
61
61
|
|
62
|
-
|
62
|
+
A third method is to produce messages first (without delivering the messages to Kafka yet), and deliver them synchronously later.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
# app/controllers/comments_controller.rb
|
66
|
+
class CommentsController < ApplicationController
|
67
|
+
def create
|
68
|
+
@comment = Comment.create!(params)
|
69
|
+
|
70
|
+
event = {
|
71
|
+
name: "comment_created",
|
72
|
+
data: {
|
73
|
+
comment_id: @comment.id
|
74
|
+
user_id: current_user.id
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
# This will queue the two messages in the internal buffer.
|
79
|
+
DeliveryBoy.produce(comment.to_json, topic: "comments")
|
80
|
+
DeliveryBoy.produce(event.to_json, topic: "activity")
|
81
|
+
|
82
|
+
# This will deliver all messages in the buffer to Kafka.
|
83
|
+
# This call is blocking.
|
84
|
+
DeliveryBoy.deliver_messages
|
85
|
+
end
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
The methods `deliver`, `deliver_async` and `produce` take the following options:
|
63
90
|
|
64
91
|
* `topic` – the Kafka topic that should be written to (required).
|
65
92
|
* `key` – the key that should be set on the Kafka message (optional).
|
@@ -103,6 +130,10 @@ A list of Kafka brokers that should be used to initialize the client. Defaults t
|
|
103
130
|
|
104
131
|
This is how the client will identify itself to the Kafka brokers. Default is `delivery_boy`.
|
105
132
|
|
133
|
+
##### `log_level`
|
134
|
+
|
135
|
+
The log level for the logger.
|
136
|
+
|
106
137
|
#### Message delivery
|
107
138
|
|
108
139
|
##### `delivery_interval`
|
@@ -231,17 +262,17 @@ describe PostsController do
|
|
231
262
|
describe "#show" do
|
232
263
|
it "emits an event to Kafka" do
|
233
264
|
post = Post.create!(body: "hello")
|
234
|
-
|
265
|
+
|
235
266
|
get :show, id: post.id
|
236
|
-
|
267
|
+
|
237
268
|
# Use this API to extract all messages written to a Kafka topic.
|
238
269
|
messages = DeliveryBoy.testing.messages_for("post_views")
|
239
|
-
|
270
|
+
|
240
271
|
expect(messages.count).to eq 1
|
241
|
-
|
272
|
+
|
242
273
|
# In addition to #value, you can also pull out #key and #partition_key.
|
243
274
|
event = JSON.parse(messages.first.value)
|
244
|
-
|
275
|
+
|
245
276
|
expect(event["post_id"]).to eq post.id
|
246
277
|
end
|
247
278
|
end
|
data/delivery_boy.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_runtime_dependency "ruby-kafka", "~> 0.
|
23
|
+
spec.add_runtime_dependency "ruby-kafka", "~> 0.7.8"
|
24
24
|
spec.add_runtime_dependency "king_konf", "~> 0.3"
|
25
25
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.15"
|
data/lib/delivery_boy.rb
CHANGED
@@ -47,6 +47,39 @@ module DeliveryBoy
|
|
47
47
|
instance.deliver_async!(value, topic: topic, **options)
|
48
48
|
end
|
49
49
|
|
50
|
+
# Like {.produce!}, but handles +Kafka::BufferOverflow+ errors
|
51
|
+
# by logging them and just going on with normal business.
|
52
|
+
#
|
53
|
+
# @return [nil]
|
54
|
+
def produce(value, topic:, **options)
|
55
|
+
produce!(value, topic: topic, **options)
|
56
|
+
rescue Kafka::BufferOverflow
|
57
|
+
logger.error "Message for `#{topic}` dropped due to buffer overflow"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Appends the given message to the producer buffer but does not send it until {.deliver_messages} is called.
|
61
|
+
#
|
62
|
+
# @param value [String] the message value.
|
63
|
+
# @param topic [String] the topic that the message should be written to.
|
64
|
+
# @param key [String, nil] the message key.
|
65
|
+
# @param partition [Integer, nil] the topic partition that the message should
|
66
|
+
# be written to.
|
67
|
+
# @param partition_key [String, nil] a key used to deterministically assign
|
68
|
+
# a partition to the message.
|
69
|
+
# @return [nil]
|
70
|
+
# @raise [Kafka::BufferOverflow] if the producer's buffer is full.
|
71
|
+
def produce!(value, topic:, **options)
|
72
|
+
instance.produce(value, topic: topic, **options)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Delivers the items currently in the producer buffer.
|
76
|
+
#
|
77
|
+
# @return [nil]
|
78
|
+
# @raise [Kafka::DeliveryFailed] if delivery failed for some reason.
|
79
|
+
def deliver_messages
|
80
|
+
instance.deliver_messages
|
81
|
+
end
|
82
|
+
|
50
83
|
# Shut down DeliveryBoy.
|
51
84
|
#
|
52
85
|
# Automatically called when the process exits.
|
@@ -60,7 +93,11 @@ module DeliveryBoy
|
|
60
93
|
#
|
61
94
|
# @return [Logger]
|
62
95
|
def logger
|
63
|
-
@logger ||= Logger.new($stdout)
|
96
|
+
@logger ||= Logger.new($stdout).tap do |logger|
|
97
|
+
if config.log_level
|
98
|
+
logger.level = Object.const_get("Logger::#{config.log_level.upcase}")
|
99
|
+
end
|
100
|
+
end
|
64
101
|
end
|
65
102
|
|
66
103
|
attr_writer :logger
|
data/lib/delivery_boy/config.rb
CHANGED
@@ -7,6 +7,7 @@ module DeliveryBoy
|
|
7
7
|
# Basic
|
8
8
|
list :brokers, items: :string, sep: ",", default: ["localhost:9092"]
|
9
9
|
string :client_id, default: "delivery_boy"
|
10
|
+
string :log_level, default: nil
|
10
11
|
|
11
12
|
# Buffering
|
12
13
|
integer :max_buffer_bytesize, default: 10_000_000
|
@@ -35,6 +36,7 @@ module DeliveryBoy
|
|
35
36
|
string :ssl_client_cert, default: nil
|
36
37
|
string :ssl_client_cert_key, default: nil
|
37
38
|
boolean :ssl_ca_certs_from_system, default: false
|
39
|
+
boolean :ssl_verify_hostname, default: true
|
38
40
|
|
39
41
|
# SASL authentication
|
40
42
|
string :sasl_gssapi_principal
|
@@ -45,6 +47,7 @@ module DeliveryBoy
|
|
45
47
|
string :sasl_scram_username
|
46
48
|
string :sasl_scram_password
|
47
49
|
string :sasl_scram_mechanism
|
50
|
+
boolean :sasl_over_ssl, default: true
|
48
51
|
|
49
52
|
# Datadog monitoring
|
50
53
|
boolean :datadog_enabled
|
data/lib/delivery_boy/fake.rb
CHANGED
@@ -10,31 +10,62 @@ module DeliveryBoy
|
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@messages = Hash.new {|h, k| h[k] = [] }
|
13
|
+
@buffer = Hash.new {|h, k| h[k] = [] }
|
14
|
+
@delivery_lock = Mutex.new
|
13
15
|
end
|
14
16
|
|
15
17
|
def deliver(value, topic:, key: nil, partition: nil, partition_key: nil, create_time: Time.now)
|
16
|
-
|
17
|
-
|
18
|
+
@delivery_lock.synchronize do
|
19
|
+
offset = @messages[topic].count
|
20
|
+
message = FakeMessage.new(value, topic, key, offset, partition, partition_key, create_time)
|
18
21
|
|
19
|
-
|
22
|
+
@messages[topic] << message
|
23
|
+
end
|
20
24
|
|
21
25
|
nil
|
22
26
|
end
|
23
27
|
|
24
28
|
alias deliver_async! deliver
|
25
29
|
|
30
|
+
def produce(value, topic:, key: nil, partition: nil, partition_key: nil, create_time: Time.now)
|
31
|
+
@delivery_lock.synchronize do
|
32
|
+
offset = @buffer[topic].count
|
33
|
+
message = FakeMessage.new(value, topic, key, offset, partition, partition_key, create_time)
|
34
|
+
|
35
|
+
@buffer[topic] << message
|
36
|
+
end
|
37
|
+
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def deliver_messages
|
42
|
+
@delivery_lock.synchronize do
|
43
|
+
@buffer.each do |topic, messages|
|
44
|
+
@messages[topic].push(*messages)
|
45
|
+
end
|
46
|
+
@buffer.clear
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
26
50
|
def shutdown
|
27
51
|
clear
|
28
52
|
end
|
29
53
|
|
30
54
|
# Clear all messages stored in memory.
|
31
55
|
def clear
|
32
|
-
@
|
56
|
+
@delivery_lock.synchronize do
|
57
|
+
@messages.clear
|
58
|
+
@buffer.clear
|
59
|
+
end
|
33
60
|
end
|
34
61
|
|
35
62
|
# Return all messages written to the specified topic.
|
36
63
|
def messages_for(topic)
|
37
|
-
@
|
64
|
+
@delivery_lock.synchronize do
|
65
|
+
# Return a clone so that the list of messages can be traversed
|
66
|
+
# without worrying about a concurrent modification
|
67
|
+
@messages[topic].clone
|
68
|
+
end
|
38
69
|
end
|
39
70
|
end
|
40
71
|
end
|
@@ -6,6 +6,7 @@ module DeliveryBoy
|
|
6
6
|
def initialize(config, logger)
|
7
7
|
@config = config
|
8
8
|
@logger = logger
|
9
|
+
@async_producer = nil
|
9
10
|
end
|
10
11
|
|
11
12
|
def deliver(value, topic:, **options)
|
@@ -27,6 +28,14 @@ module DeliveryBoy
|
|
27
28
|
async_producer.shutdown if async_producer?
|
28
29
|
end
|
29
30
|
|
31
|
+
def produce(value, topic:, **options)
|
32
|
+
sync_producer.produce(value, topic: topic, **options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def deliver_messages
|
36
|
+
sync_producer.deliver_messages
|
37
|
+
end
|
38
|
+
|
30
39
|
private
|
31
40
|
|
32
41
|
attr_reader :config, :logger
|
@@ -68,6 +77,7 @@ module DeliveryBoy
|
|
68
77
|
ssl_client_cert: config.ssl_client_cert,
|
69
78
|
ssl_client_cert_key: config.ssl_client_cert_key,
|
70
79
|
ssl_ca_certs_from_system: config.ssl_ca_certs_from_system,
|
80
|
+
ssl_verify_hostname: config.ssl_verify_hostname,
|
71
81
|
sasl_gssapi_principal: config.sasl_gssapi_principal,
|
72
82
|
sasl_gssapi_keytab: config.sasl_gssapi_keytab,
|
73
83
|
sasl_plain_authzid: config.sasl_plain_authzid,
|
@@ -76,6 +86,7 @@ module DeliveryBoy
|
|
76
86
|
sasl_scram_username: config.sasl_scram_username,
|
77
87
|
sasl_scram_password: config.sasl_scram_password,
|
78
88
|
sasl_scram_mechanism: config.sasl_scram_mechanism,
|
89
|
+
sasl_over_ssl: config.sasl_over_ssl
|
79
90
|
)
|
80
91
|
end
|
81
92
|
|
data/lib/delivery_boy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delivery_boy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.8
|
4
|
+
version: 0.2.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Schierbeck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-kafka
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 0.7.8
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 0.7.8
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: king_konf
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,6 +87,7 @@ executables: []
|
|
87
87
|
extensions: []
|
88
88
|
extra_rdoc_files: []
|
89
89
|
files:
|
90
|
+
- ".circleci/config.yml"
|
90
91
|
- ".gitignore"
|
91
92
|
- ".rspec"
|
92
93
|
- ".travis.yml"
|
@@ -97,7 +98,6 @@ files:
|
|
97
98
|
- Rakefile
|
98
99
|
- bin/console
|
99
100
|
- bin/setup
|
100
|
-
- circle.yml
|
101
101
|
- delivery_boy.gemspec
|
102
102
|
- examples/async.rb
|
103
103
|
- examples/sync.rb
|
@@ -126,12 +126,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
126
|
version: '0'
|
127
127
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: '0'
|
132
132
|
requirements: []
|
133
|
-
|
134
|
-
rubygems_version: 2.7.6
|
133
|
+
rubygems_version: 3.0.3
|
135
134
|
signing_key:
|
136
135
|
specification_version: 4
|
137
136
|
summary: A simple way to produce messages to Kafka from Ruby applications
|
data/circle.yml
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
machine:
|
2
|
-
pre:
|
3
|
-
- curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.0
|
4
|
-
services:
|
5
|
-
- docker
|
6
|
-
ruby:
|
7
|
-
version: 2.4.1
|
8
|
-
|
9
|
-
dependencies:
|
10
|
-
pre:
|
11
|
-
- docker -v
|
12
|
-
- docker run -p 2181:2181 -p 9092:9092 --env ADVERTISED_HOST=localhost --env ADVERTISED_PORT=9092 -d spotify/kafka
|
13
|
-
|
14
|
-
test:
|
15
|
-
override:
|
16
|
-
- bundle exec rspec
|