rdkafka 0.6.0 → 0.9.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 +4 -4
- data/.semaphore/semaphore.yml +23 -0
- data/CHANGELOG.md +27 -0
- data/README.md +9 -9
- data/docker-compose.yml +17 -11
- data/ext/README.md +10 -15
- data/ext/Rakefile +24 -3
- data/lib/rdkafka.rb +8 -0
- data/lib/rdkafka/abstract_handle.rb +82 -0
- data/lib/rdkafka/admin.rb +155 -0
- data/lib/rdkafka/admin/create_topic_handle.rb +27 -0
- data/lib/rdkafka/admin/create_topic_report.rb +22 -0
- data/lib/rdkafka/admin/delete_topic_handle.rb +27 -0
- data/lib/rdkafka/admin/delete_topic_report.rb +22 -0
- data/lib/rdkafka/bindings.rb +64 -18
- data/lib/rdkafka/callbacks.rb +106 -0
- data/lib/rdkafka/config.rb +38 -9
- data/lib/rdkafka/consumer.rb +221 -46
- data/lib/rdkafka/consumer/headers.rb +7 -5
- data/lib/rdkafka/consumer/partition.rb +1 -1
- data/lib/rdkafka/consumer/topic_partition_list.rb +6 -16
- data/lib/rdkafka/error.rb +35 -4
- data/lib/rdkafka/metadata.rb +92 -0
- data/lib/rdkafka/producer.rb +50 -24
- data/lib/rdkafka/producer/delivery_handle.rb +7 -49
- data/lib/rdkafka/producer/delivery_report.rb +7 -2
- data/lib/rdkafka/version.rb +3 -3
- data/rdkafka.gemspec +3 -3
- data/spec/rdkafka/abstract_handle_spec.rb +114 -0
- data/spec/rdkafka/admin/create_topic_handle_spec.rb +52 -0
- data/spec/rdkafka/admin/create_topic_report_spec.rb +16 -0
- data/spec/rdkafka/admin/delete_topic_handle_spec.rb +52 -0
- data/spec/rdkafka/admin/delete_topic_report_spec.rb +16 -0
- data/spec/rdkafka/admin_spec.rb +203 -0
- data/spec/rdkafka/bindings_spec.rb +28 -10
- data/spec/rdkafka/callbacks_spec.rb +20 -0
- data/spec/rdkafka/config_spec.rb +51 -9
- data/spec/rdkafka/consumer/message_spec.rb +6 -1
- data/spec/rdkafka/consumer_spec.rb +287 -20
- data/spec/rdkafka/error_spec.rb +7 -3
- data/spec/rdkafka/metadata_spec.rb +78 -0
- data/spec/rdkafka/producer/delivery_handle_spec.rb +3 -43
- data/spec/rdkafka/producer/delivery_report_spec.rb +5 -1
- data/spec/rdkafka/producer_spec.rb +220 -100
- data/spec/spec_helper.rb +34 -6
- metadata +37 -13
- data/.travis.yml +0 -34
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 22bda74a815bebd487179ac5ec3faa4566752f2c87945ca5c461c427633daf1f
|
|
4
|
+
data.tar.gz: '0802ca4118ed248fa11226a2c1b2140d073d230c02e3696a03e462fe59cb129e'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4d3b7042fa2290386d4feaaa01c4289bc858042d66806157066257f282c84db459e80e32a2613efd3452a46c63faaff7fad9f110fb153c3385cef00fb6017f27
|
|
7
|
+
data.tar.gz: e431a320535df8293e099e1b25bf0e6da68caf9a641621faf847dd206093dd9973b41f03f95412c43a740b50a0ef70e7caad418ff20c07218f19342d1cb3826a
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: v1.0
|
|
2
|
+
name: Rdkafka Ruby
|
|
3
|
+
|
|
4
|
+
agent:
|
|
5
|
+
machine:
|
|
6
|
+
type: e1-standard-4
|
|
7
|
+
os_image: ubuntu1804
|
|
8
|
+
|
|
9
|
+
blocks:
|
|
10
|
+
- name: Run specs
|
|
11
|
+
task:
|
|
12
|
+
jobs:
|
|
13
|
+
- name: bundle exec rspec
|
|
14
|
+
matrix:
|
|
15
|
+
- env_var: RUBY_VERSION
|
|
16
|
+
values: [ "2.5.8", "2.6.6", "2.7.2", "3.0.0", "jruby-9.2.13.0" ]
|
|
17
|
+
commands:
|
|
18
|
+
- sem-version ruby $RUBY_VERSION
|
|
19
|
+
- checkout
|
|
20
|
+
- bundle install --path vendor/bundle
|
|
21
|
+
- cd ext && bundle exec rake && cd ..
|
|
22
|
+
- docker-compose up -d --no-recreate
|
|
23
|
+
- bundle exec rspec
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,29 @@
|
|
|
1
|
+
# 0.9.0
|
|
2
|
+
* Fixes for Ruby 3.0
|
|
3
|
+
* Allow any callable object for callbacks (gremerritt)
|
|
4
|
+
* Reduce memory allocations in Rdkafka::Producer#produce (jturkel)
|
|
5
|
+
* Use queue as log callback to avoid unsafe calls from trap context (breunigs)
|
|
6
|
+
* Allow passing in topic configuration on create_topic (dezka)
|
|
7
|
+
* Add each_batch method to consumer (mgrosso)
|
|
8
|
+
|
|
9
|
+
# 0.8.1
|
|
10
|
+
* Fix topic_flag behaviour and add tests for Metadata (geoff2k)
|
|
11
|
+
* Add topic admin interface (geoff2k)
|
|
12
|
+
* Raise an exception if @native_kafka is nil (geoff2k)
|
|
13
|
+
* Option to use zstd compression (jasonmartens)
|
|
14
|
+
|
|
15
|
+
# 0.8.0
|
|
16
|
+
* Upgrade librdkafka to 1.4.0
|
|
17
|
+
* Integrate librdkafka metadata API and add partition_key (by Adithya-copart)
|
|
18
|
+
* Ruby 2.7 compatibility fix (by Geoff Thé)A
|
|
19
|
+
* Add error to delivery report (by Alex Stanovsky)
|
|
20
|
+
* Don't override CPPFLAGS and LDFLAGS if already set on Mac (by Hiroshi Hatake)
|
|
21
|
+
* Allow use of Rake 13.x and up (by Tomasz Pajor)
|
|
22
|
+
|
|
23
|
+
# 0.7.0
|
|
24
|
+
* Bump librdkafka to 1.2.0 (by rob-as)
|
|
25
|
+
* Allow customizing the wait time for delivery report availability (by mensfeld)
|
|
26
|
+
|
|
1
27
|
# 0.6.0
|
|
2
28
|
* Bump librdkafka to 1.1.0 (by Chris Gaffney)
|
|
3
29
|
* Implement seek (by breunigs)
|
|
@@ -7,6 +33,7 @@
|
|
|
7
33
|
* Add cluster and member information (by dmexe)
|
|
8
34
|
* Support message headers for consumer & producer (by dmexe)
|
|
9
35
|
* Add consumer rebalance listener (by dmexe)
|
|
36
|
+
* Implement pause/resume partitions (by dmexe)
|
|
10
37
|
|
|
11
38
|
# 0.4.2
|
|
12
39
|
* Delivery callback for producer
|
data/README.md
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
# Rdkafka
|
|
2
2
|
|
|
3
|
-
[](https://appsignal.semaphoreci.com/projects/rdkafka-ruby)
|
|
4
4
|
[](https://badge.fury.io/rb/rdkafka)
|
|
5
5
|
[](https://codeclimate.com/github/appsignal/rdkafka-ruby/maintainability)
|
|
6
|
-
[](https://codeclimate.com/github/appsignal/rdkafka-ruby/test_coverage)
|
|
7
6
|
|
|
8
7
|
The `rdkafka` gem is a modern Kafka client library for Ruby based on
|
|
9
8
|
[librdkafka](https://github.com/edenhill/librdkafka/).
|
|
10
9
|
It wraps the production-ready C client using the [ffi](https://github.com/ffi/ffi)
|
|
11
|
-
gem and targets Kafka 1.0+ and Ruby 2.
|
|
10
|
+
gem and targets Kafka 1.0+ and Ruby 2.4+.
|
|
11
|
+
|
|
12
|
+
`rdkafka` was written because we needed a reliable Ruby client for
|
|
13
|
+
Kafka that supports modern Kafka at [AppSignal](https://appsignal.com).
|
|
14
|
+
We run it in production on very high traffic systems.
|
|
12
15
|
|
|
13
16
|
This gem only provides a high-level Kafka consumer. If you are running
|
|
14
17
|
an older version of Kafka and/or need the legacy simple consumer we
|
|
@@ -68,12 +71,9 @@ end
|
|
|
68
71
|
delivery_handles.each(&:wait)
|
|
69
72
|
```
|
|
70
73
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
to make sure that you create rdkafka instances after forking. Otherwise
|
|
75
|
-
they will not work and crash your Ruby process when they are garbage
|
|
76
|
-
collected. See https://github.com/appsignal/rdkafka-ruby/issues/19
|
|
74
|
+
Note that creating a producer consumes some resources that will not be
|
|
75
|
+
released until it `#close` is explicitly called, so be sure to call
|
|
76
|
+
`Config#producer` only as necessary.
|
|
77
77
|
|
|
78
78
|
## Development
|
|
79
79
|
|
data/docker-compose.yml
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
1
3
|
version: '2'
|
|
4
|
+
|
|
2
5
|
services:
|
|
3
6
|
zookeeper:
|
|
4
|
-
image:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
image: confluentinc/cp-zookeeper:latest
|
|
8
|
+
environment:
|
|
9
|
+
ZOOKEEPER_CLIENT_PORT: 2181
|
|
10
|
+
ZOOKEEPER_TICK_TIME: 2000
|
|
11
|
+
|
|
7
12
|
kafka:
|
|
8
|
-
image:
|
|
13
|
+
image: confluentinc/cp-kafka:latest
|
|
14
|
+
depends_on:
|
|
15
|
+
- zookeeper
|
|
9
16
|
ports:
|
|
10
|
-
-
|
|
17
|
+
- 9092:9092
|
|
11
18
|
environment:
|
|
12
|
-
|
|
13
|
-
KAFKA_ADVERTISED_PORT: 9092
|
|
19
|
+
KAFKA_BROKER_ID: 1
|
|
14
20
|
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
|
|
22
|
+
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
|
|
23
|
+
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
|
|
24
|
+
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
|
data/ext/README.md
CHANGED
|
@@ -5,19 +5,14 @@ this gem is installed.
|
|
|
5
5
|
|
|
6
6
|
To update the `librdkafka` version follow the following steps:
|
|
7
7
|
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
* Change the `sha256` in `lib/rdkafka/version.rb`
|
|
8
|
+
* Go to https://github.com/edenhill/librdkafka/releases to get the new
|
|
9
|
+
version number and asset checksum for `tar.gz`.
|
|
11
10
|
* Change the version in `lib/rdkafka/version.rb`
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Do your own verification if you rely on this behaviour.
|
|
21
|
-
|
|
22
|
-
Once https://github.com/appsignal/rdkafka-ruby/issues/44 is implemented
|
|
23
|
-
we will change this process.
|
|
11
|
+
* Change the `sha256` in `lib/rdkafka/version.rb`
|
|
12
|
+
* Run `bundle exec rake` in the `ext` directory to download and build
|
|
13
|
+
the new version
|
|
14
|
+
* Run `docker-compose pull` in the main gem directory to ensure the docker
|
|
15
|
+
images used by the tests and run `docker-compose up`
|
|
16
|
+
* Finally, run `bundle exec rspec` in the main gem directory to execute
|
|
17
|
+
the test suite to detect any regressions that may have been introduced
|
|
18
|
+
by the update
|
data/ext/Rakefile
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require File.expand_path('../../lib/rdkafka/version', __FILE__)
|
|
2
2
|
require "mini_portile2"
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "open-uri"
|
|
4
5
|
|
|
5
6
|
task :default => :clean do
|
|
6
7
|
# MiniPortile#download_file_http is a monkey patch that removes the download
|
|
@@ -23,9 +24,10 @@ task :default => :clean do
|
|
|
23
24
|
recipe = MiniPortile.new("librdkafka", Rdkafka::LIBRDKAFKA_VERSION)
|
|
24
25
|
|
|
25
26
|
# Use default homebrew openssl if we're on mac and the directory exists
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
ENV["
|
|
27
|
+
# and each of flags is not empty
|
|
28
|
+
if recipe.host&.include?("darwin") && Dir.exist?("/usr/local/opt/openssl")
|
|
29
|
+
ENV["CPPFLAGS"] = "-I/usr/local/opt/openssl/include" unless ENV["CPPFLAGS"]
|
|
30
|
+
ENV["LDFLAGS"] = "-L/usr/local/opt/openssl/lib" unless ENV["LDFLAGS"]
|
|
29
31
|
end
|
|
30
32
|
|
|
31
33
|
recipe.files << {
|
|
@@ -55,3 +57,22 @@ task :clean do
|
|
|
55
57
|
FileUtils.rm_rf File.join(File.dirname(__FILE__), "ports")
|
|
56
58
|
FileUtils.rm_rf File.join(File.dirname(__FILE__), "tmp")
|
|
57
59
|
end
|
|
60
|
+
|
|
61
|
+
namespace :build do
|
|
62
|
+
desc "Build librdkafka at the given git sha or tag"
|
|
63
|
+
task :git, [:ref] do |task, args|
|
|
64
|
+
ref = args[:ref]
|
|
65
|
+
version = "git-#{ref}"
|
|
66
|
+
|
|
67
|
+
recipe = MiniPortile.new("librdkafka", version)
|
|
68
|
+
recipe.files << "https://github.com/edenhill/librdkafka/archive/#{ref}.tar.gz"
|
|
69
|
+
recipe.configure_options = ["--host=#{recipe.host}","--enable-static", "--enable-zstd"]
|
|
70
|
+
recipe.cook
|
|
71
|
+
|
|
72
|
+
ext = recipe.host.include?("darwin") ? "dylib" : "so"
|
|
73
|
+
lib = File.expand_path("ports/#{recipe.host}/librdkafka/#{version}/lib/librdkafka.#{ext}", __dir__)
|
|
74
|
+
|
|
75
|
+
# Copy will copy the content, following any symlinks
|
|
76
|
+
FileUtils.cp(lib, __dir__)
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/rdkafka.rb
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
require "rdkafka/version"
|
|
2
2
|
|
|
3
|
+
require "rdkafka/abstract_handle"
|
|
4
|
+
require "rdkafka/admin"
|
|
5
|
+
require "rdkafka/admin/create_topic_handle"
|
|
6
|
+
require "rdkafka/admin/create_topic_report"
|
|
7
|
+
require "rdkafka/admin/delete_topic_handle"
|
|
8
|
+
require "rdkafka/admin/delete_topic_report"
|
|
3
9
|
require "rdkafka/bindings"
|
|
10
|
+
require "rdkafka/callbacks"
|
|
4
11
|
require "rdkafka/config"
|
|
5
12
|
require "rdkafka/consumer"
|
|
6
13
|
require "rdkafka/consumer/headers"
|
|
@@ -8,6 +15,7 @@ require "rdkafka/consumer/message"
|
|
|
8
15
|
require "rdkafka/consumer/partition"
|
|
9
16
|
require "rdkafka/consumer/topic_partition_list"
|
|
10
17
|
require "rdkafka/error"
|
|
18
|
+
require "rdkafka/metadata"
|
|
11
19
|
require "rdkafka/producer"
|
|
12
20
|
require "rdkafka/producer/delivery_handle"
|
|
13
21
|
require "rdkafka/producer/delivery_report"
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require "ffi"
|
|
2
|
+
|
|
3
|
+
module Rdkafka
|
|
4
|
+
class AbstractHandle < FFI::Struct
|
|
5
|
+
# Subclasses must define their own layout, and the layout must start with:
|
|
6
|
+
#
|
|
7
|
+
# layout :pending, :bool,
|
|
8
|
+
# :response, :int
|
|
9
|
+
|
|
10
|
+
REGISTRY = {}
|
|
11
|
+
|
|
12
|
+
CURRENT_TIME = -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }.freeze
|
|
13
|
+
|
|
14
|
+
private_constant :CURRENT_TIME
|
|
15
|
+
|
|
16
|
+
def self.register(handle)
|
|
17
|
+
address = handle.to_ptr.address
|
|
18
|
+
REGISTRY[address] = handle
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.remove(address)
|
|
22
|
+
REGISTRY.delete(address)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Whether the handle is still pending.
|
|
26
|
+
#
|
|
27
|
+
# @return [Boolean]
|
|
28
|
+
def pending?
|
|
29
|
+
self[:pending]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Wait for the operation to complete or raise an error if this takes longer than the timeout.
|
|
33
|
+
# If there is a timeout this does not mean the operation failed, rdkafka might still be working on the operation.
|
|
34
|
+
# In this case it is possible to call wait again.
|
|
35
|
+
#
|
|
36
|
+
# @param max_wait_timeout [Numeric, nil] Amount of time to wait before timing out. If this is nil it does not time out.
|
|
37
|
+
# @param wait_timeout [Numeric] Amount of time we should wait before we recheck if the operation has completed
|
|
38
|
+
#
|
|
39
|
+
# @raise [RdkafkaError] When the operation failed
|
|
40
|
+
# @raise [WaitTimeoutError] When the timeout has been reached and the handle is still pending
|
|
41
|
+
#
|
|
42
|
+
# @return [Object] Operation-specific result
|
|
43
|
+
def wait(max_wait_timeout: 60, wait_timeout: 0.1)
|
|
44
|
+
timeout = if max_wait_timeout
|
|
45
|
+
CURRENT_TIME.call + max_wait_timeout
|
|
46
|
+
else
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
loop do
|
|
50
|
+
if pending?
|
|
51
|
+
if timeout && timeout <= CURRENT_TIME.call
|
|
52
|
+
raise WaitTimeoutError.new("Waiting for #{operation_name} timed out after #{max_wait_timeout} seconds")
|
|
53
|
+
end
|
|
54
|
+
sleep wait_timeout
|
|
55
|
+
elsif self[:response] != 0
|
|
56
|
+
raise_error
|
|
57
|
+
else
|
|
58
|
+
return create_result
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [String] the name of the operation (e.g. "delivery")
|
|
64
|
+
def operation_name
|
|
65
|
+
raise "Must be implemented by subclass!"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# @return [Object] operation-specific result
|
|
69
|
+
def create_result
|
|
70
|
+
raise "Must be implemented by subclass!"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Allow subclasses to override
|
|
74
|
+
def raise_error
|
|
75
|
+
raise RdkafkaError.new(self[:response])
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Error that is raised when waiting for the handle to complete
|
|
79
|
+
# takes longer than the specified timeout.
|
|
80
|
+
class WaitTimeoutError < RuntimeError; end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
module Rdkafka
|
|
2
|
+
class Admin
|
|
3
|
+
# @private
|
|
4
|
+
def initialize(native_kafka)
|
|
5
|
+
@native_kafka = native_kafka
|
|
6
|
+
@closing = false
|
|
7
|
+
|
|
8
|
+
# Start thread to poll client for callbacks
|
|
9
|
+
@polling_thread = Thread.new do
|
|
10
|
+
loop do
|
|
11
|
+
Rdkafka::Bindings.rd_kafka_poll(@native_kafka, 250)
|
|
12
|
+
# Exit thread if closing and the poll queue is empty
|
|
13
|
+
if @closing && Rdkafka::Bindings.rd_kafka_outq_len(@native_kafka) == 0
|
|
14
|
+
break
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
@polling_thread.abort_on_exception = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Close this admin instance
|
|
22
|
+
def close
|
|
23
|
+
return unless @native_kafka
|
|
24
|
+
|
|
25
|
+
# Indicate to polling thread that we're closing
|
|
26
|
+
@closing = true
|
|
27
|
+
# Wait for the polling thread to finish up
|
|
28
|
+
@polling_thread.join
|
|
29
|
+
Rdkafka::Bindings.rd_kafka_destroy(@native_kafka)
|
|
30
|
+
@native_kafka = nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Create a topic with the given partition count and replication factor
|
|
34
|
+
#
|
|
35
|
+
# @raise [ConfigError] When the partition count or replication factor are out of valid range
|
|
36
|
+
# @raise [RdkafkaError] When the topic name is invalid or the topic already exists
|
|
37
|
+
# @raise [RdkafkaError] When the topic configuration is invalid
|
|
38
|
+
#
|
|
39
|
+
# @return [CreateTopicHandle] Create topic handle that can be used to wait for the result of creating the topic
|
|
40
|
+
def create_topic(topic_name, partition_count, replication_factor, topic_config={})
|
|
41
|
+
|
|
42
|
+
# Create a rd_kafka_NewTopic_t representing the new topic
|
|
43
|
+
error_buffer = FFI::MemoryPointer.from_string(" " * 256)
|
|
44
|
+
new_topic_ptr = Rdkafka::Bindings.rd_kafka_NewTopic_new(
|
|
45
|
+
FFI::MemoryPointer.from_string(topic_name),
|
|
46
|
+
partition_count,
|
|
47
|
+
replication_factor,
|
|
48
|
+
error_buffer,
|
|
49
|
+
256
|
|
50
|
+
)
|
|
51
|
+
if new_topic_ptr.null?
|
|
52
|
+
raise Rdkafka::Config::ConfigError.new(error_buffer.read_string)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
unless topic_config.nil?
|
|
56
|
+
topic_config.each do |key, value|
|
|
57
|
+
Rdkafka::Bindings.rd_kafka_NewTopic_set_config(
|
|
58
|
+
new_topic_ptr,
|
|
59
|
+
key.to_s,
|
|
60
|
+
value.to_s
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Note that rd_kafka_CreateTopics can create more than one topic at a time
|
|
66
|
+
pointer_array = [new_topic_ptr]
|
|
67
|
+
topics_array_ptr = FFI::MemoryPointer.new(:pointer)
|
|
68
|
+
topics_array_ptr.write_array_of_pointer(pointer_array)
|
|
69
|
+
|
|
70
|
+
# Get a pointer to the queue that our request will be enqueued on
|
|
71
|
+
queue_ptr = Rdkafka::Bindings.rd_kafka_queue_get_background(@native_kafka)
|
|
72
|
+
if queue_ptr.null?
|
|
73
|
+
Rdkafka::Bindings.rd_kafka_NewTopic_destroy(new_topic_ptr)
|
|
74
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Create and register the handle we will return to the caller
|
|
78
|
+
create_topic_handle = CreateTopicHandle.new
|
|
79
|
+
create_topic_handle[:pending] = true
|
|
80
|
+
create_topic_handle[:response] = -1
|
|
81
|
+
CreateTopicHandle.register(create_topic_handle)
|
|
82
|
+
admin_options_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_new(@native_kafka, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_CREATETOPICS)
|
|
83
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, create_topic_handle.to_ptr)
|
|
84
|
+
|
|
85
|
+
begin
|
|
86
|
+
Rdkafka::Bindings.rd_kafka_CreateTopics(
|
|
87
|
+
@native_kafka,
|
|
88
|
+
topics_array_ptr,
|
|
89
|
+
1,
|
|
90
|
+
admin_options_ptr,
|
|
91
|
+
queue_ptr
|
|
92
|
+
)
|
|
93
|
+
rescue Exception => err
|
|
94
|
+
CreateTopicHandle.remove(create_topic_handle.to_ptr.address)
|
|
95
|
+
raise
|
|
96
|
+
ensure
|
|
97
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
|
|
98
|
+
Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
|
|
99
|
+
Rdkafka::Bindings.rd_kafka_NewTopic_destroy(new_topic_ptr)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
create_topic_handle
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Delete the named topic
|
|
106
|
+
#
|
|
107
|
+
# @raise [RdkafkaError] When the topic name is invalid or the topic does not exist
|
|
108
|
+
#
|
|
109
|
+
# @return [DeleteTopicHandle] Delete topic handle that can be used to wait for the result of deleting the topic
|
|
110
|
+
def delete_topic(topic_name)
|
|
111
|
+
|
|
112
|
+
# Create a rd_kafka_DeleteTopic_t representing the topic to be deleted
|
|
113
|
+
delete_topic_ptr = Rdkafka::Bindings.rd_kafka_DeleteTopic_new(FFI::MemoryPointer.from_string(topic_name))
|
|
114
|
+
|
|
115
|
+
# Note that rd_kafka_DeleteTopics can create more than one topic at a time
|
|
116
|
+
pointer_array = [delete_topic_ptr]
|
|
117
|
+
topics_array_ptr = FFI::MemoryPointer.new(:pointer)
|
|
118
|
+
topics_array_ptr.write_array_of_pointer(pointer_array)
|
|
119
|
+
|
|
120
|
+
# Get a pointer to the queue that our request will be enqueued on
|
|
121
|
+
queue_ptr = Rdkafka::Bindings.rd_kafka_queue_get_background(@native_kafka)
|
|
122
|
+
if queue_ptr.null?
|
|
123
|
+
Rdkafka::Bindings.rd_kafka_DeleteTopic_destroy(delete_topic_ptr)
|
|
124
|
+
raise Rdkafka::Config::ConfigError.new("rd_kafka_queue_get_background was NULL")
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Create and register the handle we will return to the caller
|
|
128
|
+
delete_topic_handle = DeleteTopicHandle.new
|
|
129
|
+
delete_topic_handle[:pending] = true
|
|
130
|
+
delete_topic_handle[:response] = -1
|
|
131
|
+
DeleteTopicHandle.register(delete_topic_handle)
|
|
132
|
+
admin_options_ptr = Rdkafka::Bindings.rd_kafka_AdminOptions_new(@native_kafka, Rdkafka::Bindings::RD_KAFKA_ADMIN_OP_DELETETOPICS)
|
|
133
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_set_opaque(admin_options_ptr, delete_topic_handle.to_ptr)
|
|
134
|
+
|
|
135
|
+
begin
|
|
136
|
+
Rdkafka::Bindings.rd_kafka_DeleteTopics(
|
|
137
|
+
@native_kafka,
|
|
138
|
+
topics_array_ptr,
|
|
139
|
+
1,
|
|
140
|
+
admin_options_ptr,
|
|
141
|
+
queue_ptr
|
|
142
|
+
)
|
|
143
|
+
rescue Exception => err
|
|
144
|
+
DeleteTopicHandle.remove(delete_topic_handle.to_ptr.address)
|
|
145
|
+
raise
|
|
146
|
+
ensure
|
|
147
|
+
Rdkafka::Bindings.rd_kafka_AdminOptions_destroy(admin_options_ptr)
|
|
148
|
+
Rdkafka::Bindings.rd_kafka_queue_destroy(queue_ptr)
|
|
149
|
+
Rdkafka::Bindings.rd_kafka_DeleteTopic_destroy(delete_topic_ptr)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
delete_topic_handle
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|