kafka 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +210 -0
- data/.travis.yml +45 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +182 -0
- data/Rakefile +69 -0
- data/examples/consumer.rb +55 -0
- data/examples/producer.rb +46 -0
- data/ext/Rakefile +69 -0
- data/kafka.gemspec +39 -0
- data/lib/kafka/admin.rb +141 -0
- data/lib/kafka/config.rb +145 -0
- data/lib/kafka/consumer.rb +87 -0
- data/lib/kafka/error.rb +44 -0
- data/lib/kafka/ffi/admin/admin_options.rb +121 -0
- data/lib/kafka/ffi/admin/config_entry.rb +97 -0
- data/lib/kafka/ffi/admin/config_resource.rb +101 -0
- data/lib/kafka/ffi/admin/delete_topic.rb +19 -0
- data/lib/kafka/ffi/admin/new_partitions.rb +77 -0
- data/lib/kafka/ffi/admin/new_topic.rb +91 -0
- data/lib/kafka/ffi/admin/result.rb +66 -0
- data/lib/kafka/ffi/admin/topic_result.rb +32 -0
- data/lib/kafka/ffi/admin.rb +16 -0
- data/lib/kafka/ffi/broker_metadata.rb +32 -0
- data/lib/kafka/ffi/client.rb +640 -0
- data/lib/kafka/ffi/config.rb +382 -0
- data/lib/kafka/ffi/consumer.rb +342 -0
- data/lib/kafka/ffi/error.rb +25 -0
- data/lib/kafka/ffi/event.rb +215 -0
- data/lib/kafka/ffi/group_info.rb +75 -0
- data/lib/kafka/ffi/group_list.rb +27 -0
- data/lib/kafka/ffi/group_member_info.rb +52 -0
- data/lib/kafka/ffi/message/header.rb +205 -0
- data/lib/kafka/ffi/message.rb +205 -0
- data/lib/kafka/ffi/metadata.rb +58 -0
- data/lib/kafka/ffi/opaque.rb +81 -0
- data/lib/kafka/ffi/opaque_pointer.rb +73 -0
- data/lib/kafka/ffi/partition_metadata.rb +61 -0
- data/lib/kafka/ffi/producer.rb +144 -0
- data/lib/kafka/ffi/queue.rb +65 -0
- data/lib/kafka/ffi/topic.rb +32 -0
- data/lib/kafka/ffi/topic_config.rb +126 -0
- data/lib/kafka/ffi/topic_metadata.rb +42 -0
- data/lib/kafka/ffi/topic_partition.rb +43 -0
- data/lib/kafka/ffi/topic_partition_list.rb +167 -0
- data/lib/kafka/ffi.rb +624 -0
- data/lib/kafka/poller.rb +28 -0
- data/lib/kafka/producer/delivery_report.rb +120 -0
- data/lib/kafka/producer.rb +127 -0
- data/lib/kafka/version.rb +8 -0
- data/lib/kafka.rb +11 -0
- metadata +159 -0
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Kafka::Producer
|
4
|
+
class DeliveryReport
|
5
|
+
# @return [nil] Delivery was successful or report has not been received
|
6
|
+
# yet.
|
7
|
+
# @return [Kafka::ResponseError] Error delivering the Message.
|
8
|
+
attr_reader :error
|
9
|
+
|
10
|
+
# @return [nil] Report has not been received yet
|
11
|
+
# @return [String] Name of the topic Message was delivered to.
|
12
|
+
attr_reader :topic
|
13
|
+
|
14
|
+
# @return [nil] Report has not been received yet
|
15
|
+
# @return [Integer] Offset for the message on partition.
|
16
|
+
attr_reader :offset
|
17
|
+
|
18
|
+
# @return [nil] Report has not been received yet
|
19
|
+
# @return [Integer] Partition the message was delivered to.
|
20
|
+
attr_reader :partition
|
21
|
+
|
22
|
+
# Initializes a new DeliveryReport
|
23
|
+
#
|
24
|
+
# @param block [Proc] Callback to call with the DeliveryReport when it is
|
25
|
+
# received from the cluster.
|
26
|
+
def initialize(&block)
|
27
|
+
@mutex = Mutex.new
|
28
|
+
@waiter = ConditionVariable.new
|
29
|
+
|
30
|
+
@error = nil
|
31
|
+
@topic = nil
|
32
|
+
@offset = nil
|
33
|
+
@partition = nil
|
34
|
+
@callback = block
|
35
|
+
|
36
|
+
# Will be set to true by a call to #done. Fast out for any callers to
|
37
|
+
# #wait that may come in after done has already been called.
|
38
|
+
@done = false
|
39
|
+
end
|
40
|
+
|
41
|
+
# Returns true when the report has been received back from the kafka
|
42
|
+
# cluster.
|
43
|
+
#
|
44
|
+
# @return [Boolean] True when the server has reported back on the
|
45
|
+
# delivery.
|
46
|
+
def received?
|
47
|
+
@done
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [Boolean] Is the report for an error?
|
51
|
+
def error?
|
52
|
+
error.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns if the delivery was successful
|
56
|
+
#
|
57
|
+
# @return [Boolean] True when the report was delivered to the cluster
|
58
|
+
# successfully.
|
59
|
+
def successful?
|
60
|
+
!error
|
61
|
+
end
|
62
|
+
|
63
|
+
# @private
|
64
|
+
#
|
65
|
+
# Set the response based on the message and notify anyone waiting on the
|
66
|
+
# result.
|
67
|
+
#
|
68
|
+
# @param message [Kafka::FFI::Message]
|
69
|
+
def done(message)
|
70
|
+
@mutex.synchronize do
|
71
|
+
@error = message.error
|
72
|
+
|
73
|
+
@offset = message.offset
|
74
|
+
@topic = message.topic
|
75
|
+
@partition = message.partition
|
76
|
+
|
77
|
+
@done = true
|
78
|
+
@waiter.broadcast
|
79
|
+
|
80
|
+
remove_instance_variable(:@mutex)
|
81
|
+
remove_instance_variable(:@waiter)
|
82
|
+
end
|
83
|
+
|
84
|
+
if @callback
|
85
|
+
@callback.call(self)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Wait for a report to be received for the delivery from the cluster.
|
90
|
+
#
|
91
|
+
# @param timeout [Integer] Maximum time to wait in milliseconds.
|
92
|
+
#
|
93
|
+
# @raise [Kafka::ResponseError<RD_KAFKA_RESP_ERR__TIMED_OUT>] No report
|
94
|
+
# received before timeout.
|
95
|
+
def wait(timeout: 5000)
|
96
|
+
# Fast out since the delivery report has already been reported back from
|
97
|
+
# the cluster.
|
98
|
+
if @done
|
99
|
+
return
|
100
|
+
end
|
101
|
+
|
102
|
+
@mutex.synchronize do
|
103
|
+
# Convert from milliseconds to seconds to match Ruby's API. Takes
|
104
|
+
# milliseconds to be consistent with librdkafka APIs.
|
105
|
+
if timeout
|
106
|
+
timeout /= 1000.0
|
107
|
+
end
|
108
|
+
|
109
|
+
@waiter.wait(@mutex, timeout)
|
110
|
+
|
111
|
+
# No report was received for the message before we timed out waiting.
|
112
|
+
if !@done
|
113
|
+
raise ::Kafka::ResponseError, ::Kafka::FFI::RD_KAFKA_RESP_ERR__TIMED_OUT
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
nil
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "kafka/poller"
|
4
|
+
|
5
|
+
module Kafka
|
6
|
+
class Producer
|
7
|
+
require "kafka/producer/delivery_report"
|
8
|
+
|
9
|
+
# Returns the backing Kafka::FFI::Producer.
|
10
|
+
#
|
11
|
+
# @DANGER Using the backing Producer means being aware of memory management
|
12
|
+
# and could leave the producer in a bad state. Make sure you know what
|
13
|
+
# you're doing.
|
14
|
+
#
|
15
|
+
# @return [Kafka::FFI::Producer]
|
16
|
+
attr_reader :client
|
17
|
+
|
18
|
+
# Initialize a new Producer for the configured cluster.
|
19
|
+
#
|
20
|
+
# @param config [Config]
|
21
|
+
def initialize(config)
|
22
|
+
config = config.dup
|
23
|
+
|
24
|
+
# Configure callbacks
|
25
|
+
config.on_delivery_report(&method(:on_delivery_report))
|
26
|
+
|
27
|
+
@client = ::Kafka::FFI::Producer.new(config)
|
28
|
+
|
29
|
+
# Periodically call poll on the client to ensure callbacks are fired.
|
30
|
+
@poller = Poller.new(@client)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Produce and publish a message to the Kafka cluster.
|
34
|
+
#
|
35
|
+
# @param topic [String] Topic to publish the message to
|
36
|
+
# @param payload [String] Message's payload
|
37
|
+
# @param key [String] Optional partitioning key Kafka can use to determine
|
38
|
+
# the correct partition.
|
39
|
+
# @param partition [-1, nil] Kafka will determine the partition
|
40
|
+
# automatically based on the `partitioner` config option.
|
41
|
+
# @param partition [Integer] Specifiy partition to publish the message to.
|
42
|
+
# @param headers [Hash{String => String}] Set of headers to attach to the
|
43
|
+
# message.
|
44
|
+
# @param timestamp [nil] Let Kafka automatically assign Message timestamp
|
45
|
+
# @param timestamp [Time] Timestamp for the message
|
46
|
+
# @param timestamp [Integer] Timestamp as milliseconds since unix epoch
|
47
|
+
#
|
48
|
+
# @param block [Proc] Optional asyncronous callback to be called when the
|
49
|
+
# delivery status of the message is reported back from librdkafka. The
|
50
|
+
# callback MUST avoid expensive or long running processing as that may
|
51
|
+
# causes issues inside librdkafka.
|
52
|
+
#
|
53
|
+
# @yield [report] Called asyncronously when the report is received from
|
54
|
+
# Kafka on the success or failure of the delivery.
|
55
|
+
# @yieldparam report [DeliveryReport] Delivery status of the message.
|
56
|
+
#
|
57
|
+
# @return [DeliveryReport] Report of the success or failure of the
|
58
|
+
# delivery. Call #wait to block until the report is received.
|
59
|
+
def produce(topic, payload, key: nil, partition: nil, headers: nil, timestamp: nil, &block)
|
60
|
+
report = DeliveryReport.new(&block)
|
61
|
+
|
62
|
+
# Allocate a pointer to a small chunk of memory. We will use the pointer
|
63
|
+
# (not the value it points to) as a key for looking up the DeliveryReport
|
64
|
+
# in the callback.
|
65
|
+
#
|
66
|
+
# Using a MemoryPointer as a key also ensures we have a reference to the
|
67
|
+
# Pointer so it doesn't get garbage collected away and it can be freed it
|
68
|
+
# in the callback since the raw FFI::Pointer disallows #free as FFI
|
69
|
+
# doesn't believe we allocated it.
|
70
|
+
opaque = Kafka::FFI::Opaque.new(report)
|
71
|
+
|
72
|
+
@client.produce(topic, payload, key: key, partition: partition, headers: headers, timestamp: timestamp, opaque: opaque)
|
73
|
+
|
74
|
+
report
|
75
|
+
rescue
|
76
|
+
opaque.free
|
77
|
+
|
78
|
+
raise
|
79
|
+
end
|
80
|
+
|
81
|
+
# Wait until all outstanding produce requests are completed.
|
82
|
+
#
|
83
|
+
# @raise [Kafka::ResponseError] Timeout was reached before all
|
84
|
+
# outstanding requests were completed.
|
85
|
+
def flush(timeout: 1000)
|
86
|
+
@client.flush(timeout: timeout)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Gracefully shutdown the Producer, flushing any pending deliveries, and
|
90
|
+
# finally releasing an memory back to the system.
|
91
|
+
#
|
92
|
+
# @note Once #close is call it is no longer safe to call any other method
|
93
|
+
# on the Producer.
|
94
|
+
#
|
95
|
+
# @param timeout [Integer] Maximum time to wait in milliseconds for
|
96
|
+
# messages to be flushed.
|
97
|
+
def close(timeout: 30000)
|
98
|
+
# @see https://github.com/edenhill/librdkafka/blob/master/INTRODUCTION.md#producer
|
99
|
+
@poller.stop
|
100
|
+
|
101
|
+
@client.flush(timeout: timeout)
|
102
|
+
@client.poll
|
103
|
+
|
104
|
+
# Client handles destroying cached Topics
|
105
|
+
@client.destroy
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# @param client [Kafka::FFI::Producer]
|
111
|
+
# @param message [Kafka::FFI::Message]
|
112
|
+
# @param opaque [FFI::Pointer]
|
113
|
+
def on_delivery_report(_client, message, _opaque)
|
114
|
+
op = message.opaque
|
115
|
+
if op.nil?
|
116
|
+
return
|
117
|
+
end
|
118
|
+
|
119
|
+
begin
|
120
|
+
report = op.value
|
121
|
+
report.done(message)
|
122
|
+
ensure
|
123
|
+
op.free
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/lib/kafka.rb
ADDED
metadata
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kafka
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Gaffney
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mini_portile2
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: |
|
70
|
+
Kafka provides binding to librdafka as well as a default producer and
|
71
|
+
consumer implementation.
|
72
|
+
email:
|
73
|
+
- gaffneyc@gmail.com
|
74
|
+
executables: []
|
75
|
+
extensions:
|
76
|
+
- ext/Rakefile
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- ".gitignore"
|
80
|
+
- ".rubocop.yml"
|
81
|
+
- ".travis.yml"
|
82
|
+
- CHANGELOG.md
|
83
|
+
- CODE_OF_CONDUCT.md
|
84
|
+
- Gemfile
|
85
|
+
- LICENSE.txt
|
86
|
+
- README.md
|
87
|
+
- Rakefile
|
88
|
+
- examples/consumer.rb
|
89
|
+
- examples/producer.rb
|
90
|
+
- ext/Rakefile
|
91
|
+
- kafka.gemspec
|
92
|
+
- lib/kafka.rb
|
93
|
+
- lib/kafka/admin.rb
|
94
|
+
- lib/kafka/config.rb
|
95
|
+
- lib/kafka/consumer.rb
|
96
|
+
- lib/kafka/error.rb
|
97
|
+
- lib/kafka/ffi.rb
|
98
|
+
- lib/kafka/ffi/admin.rb
|
99
|
+
- lib/kafka/ffi/admin/admin_options.rb
|
100
|
+
- lib/kafka/ffi/admin/config_entry.rb
|
101
|
+
- lib/kafka/ffi/admin/config_resource.rb
|
102
|
+
- lib/kafka/ffi/admin/delete_topic.rb
|
103
|
+
- lib/kafka/ffi/admin/new_partitions.rb
|
104
|
+
- lib/kafka/ffi/admin/new_topic.rb
|
105
|
+
- lib/kafka/ffi/admin/result.rb
|
106
|
+
- lib/kafka/ffi/admin/topic_result.rb
|
107
|
+
- lib/kafka/ffi/broker_metadata.rb
|
108
|
+
- lib/kafka/ffi/client.rb
|
109
|
+
- lib/kafka/ffi/config.rb
|
110
|
+
- lib/kafka/ffi/consumer.rb
|
111
|
+
- lib/kafka/ffi/error.rb
|
112
|
+
- lib/kafka/ffi/event.rb
|
113
|
+
- lib/kafka/ffi/group_info.rb
|
114
|
+
- lib/kafka/ffi/group_list.rb
|
115
|
+
- lib/kafka/ffi/group_member_info.rb
|
116
|
+
- lib/kafka/ffi/message.rb
|
117
|
+
- lib/kafka/ffi/message/header.rb
|
118
|
+
- lib/kafka/ffi/metadata.rb
|
119
|
+
- lib/kafka/ffi/opaque.rb
|
120
|
+
- lib/kafka/ffi/opaque_pointer.rb
|
121
|
+
- lib/kafka/ffi/partition_metadata.rb
|
122
|
+
- lib/kafka/ffi/producer.rb
|
123
|
+
- lib/kafka/ffi/queue.rb
|
124
|
+
- lib/kafka/ffi/topic.rb
|
125
|
+
- lib/kafka/ffi/topic_config.rb
|
126
|
+
- lib/kafka/ffi/topic_metadata.rb
|
127
|
+
- lib/kafka/ffi/topic_partition.rb
|
128
|
+
- lib/kafka/ffi/topic_partition_list.rb
|
129
|
+
- lib/kafka/poller.rb
|
130
|
+
- lib/kafka/producer.rb
|
131
|
+
- lib/kafka/producer/delivery_report.rb
|
132
|
+
- lib/kafka/version.rb
|
133
|
+
homepage: http://github.com/deadmanssnitch/kafka
|
134
|
+
licenses:
|
135
|
+
- MIT
|
136
|
+
metadata:
|
137
|
+
homepage_uri: http://github.com/deadmanssnitch/kafka
|
138
|
+
source_code_uri: http://github.com/deadmanssnitch/kafka
|
139
|
+
changelog_uri: https://github.com/deadmanssnitch/kafka/blob/master/CHANGELOG.md
|
140
|
+
post_install_message:
|
141
|
+
rdoc_options: []
|
142
|
+
require_paths:
|
143
|
+
- lib
|
144
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
145
|
+
requirements:
|
146
|
+
- - ">="
|
147
|
+
- !ruby/object:Gem::Version
|
148
|
+
version: 2.5.0
|
149
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
150
|
+
requirements:
|
151
|
+
- - ">="
|
152
|
+
- !ruby/object:Gem::Version
|
153
|
+
version: '0'
|
154
|
+
requirements: []
|
155
|
+
rubygems_version: 3.1.2
|
156
|
+
signing_key:
|
157
|
+
specification_version: 4
|
158
|
+
summary: Kafka client bindings to librdkafka
|
159
|
+
test_files: []
|