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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.semaphore/semaphore.yml +23 -0
  3. data/CHANGELOG.md +27 -0
  4. data/README.md +9 -9
  5. data/docker-compose.yml +17 -11
  6. data/ext/README.md +10 -15
  7. data/ext/Rakefile +24 -3
  8. data/lib/rdkafka.rb +8 -0
  9. data/lib/rdkafka/abstract_handle.rb +82 -0
  10. data/lib/rdkafka/admin.rb +155 -0
  11. data/lib/rdkafka/admin/create_topic_handle.rb +27 -0
  12. data/lib/rdkafka/admin/create_topic_report.rb +22 -0
  13. data/lib/rdkafka/admin/delete_topic_handle.rb +27 -0
  14. data/lib/rdkafka/admin/delete_topic_report.rb +22 -0
  15. data/lib/rdkafka/bindings.rb +64 -18
  16. data/lib/rdkafka/callbacks.rb +106 -0
  17. data/lib/rdkafka/config.rb +38 -9
  18. data/lib/rdkafka/consumer.rb +221 -46
  19. data/lib/rdkafka/consumer/headers.rb +7 -5
  20. data/lib/rdkafka/consumer/partition.rb +1 -1
  21. data/lib/rdkafka/consumer/topic_partition_list.rb +6 -16
  22. data/lib/rdkafka/error.rb +35 -4
  23. data/lib/rdkafka/metadata.rb +92 -0
  24. data/lib/rdkafka/producer.rb +50 -24
  25. data/lib/rdkafka/producer/delivery_handle.rb +7 -49
  26. data/lib/rdkafka/producer/delivery_report.rb +7 -2
  27. data/lib/rdkafka/version.rb +3 -3
  28. data/rdkafka.gemspec +3 -3
  29. data/spec/rdkafka/abstract_handle_spec.rb +114 -0
  30. data/spec/rdkafka/admin/create_topic_handle_spec.rb +52 -0
  31. data/spec/rdkafka/admin/create_topic_report_spec.rb +16 -0
  32. data/spec/rdkafka/admin/delete_topic_handle_spec.rb +52 -0
  33. data/spec/rdkafka/admin/delete_topic_report_spec.rb +16 -0
  34. data/spec/rdkafka/admin_spec.rb +203 -0
  35. data/spec/rdkafka/bindings_spec.rb +28 -10
  36. data/spec/rdkafka/callbacks_spec.rb +20 -0
  37. data/spec/rdkafka/config_spec.rb +51 -9
  38. data/spec/rdkafka/consumer/message_spec.rb +6 -1
  39. data/spec/rdkafka/consumer_spec.rb +287 -20
  40. data/spec/rdkafka/error_spec.rb +7 -3
  41. data/spec/rdkafka/metadata_spec.rb +78 -0
  42. data/spec/rdkafka/producer/delivery_handle_spec.rb +3 -43
  43. data/spec/rdkafka/producer/delivery_report_spec.rb +5 -1
  44. data/spec/rdkafka/producer_spec.rb +220 -100
  45. data/spec/spec_helper.rb +34 -6
  46. metadata +37 -13
  47. data/.travis.yml +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0b248aee9a260de3d8bb56efba9255f21d3bfcb6c269a1ab15beac7ae2521b6c
4
- data.tar.gz: 2d1199d5717a832674f659ac1d5e69d2a659845e9c8e5509428270a4a8390ae1
3
+ metadata.gz: 22bda74a815bebd487179ac5ec3faa4566752f2c87945ca5c461c427633daf1f
4
+ data.tar.gz: '0802ca4118ed248fa11226a2c1b2140d073d230c02e3696a03e462fe59cb129e'
5
5
  SHA512:
6
- metadata.gz: 62afc350fa40f5e2117e7791927fe5ad7fcb3cb7ba13cdbba2f450dc6537d41e37668a5997fccbb260abe3fbb09e7a73c0cb5d5059b94837d5097f373c27eb75
7
- data.tar.gz: 1723633bcce6569992a2368df38c2d59b217dd94af9e8afab9932ca085e9ae0fbc3661ee8fbc370d31f3985204b38b3bbe15bbdfa11ca2be61c69b4c938a40f3
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
- [![Build Status](https://travis-ci.org/appsignal/rdkafka-ruby.svg?branch=master)](https://travis-ci.org/appsignal/rdkafka-ruby)
3
+ [![Build Status](https://appsignal.semaphoreci.com/badges/rdkafka-ruby/branches/master.svg?style=shields)](https://appsignal.semaphoreci.com/projects/rdkafka-ruby)
4
4
  [![Gem Version](https://badge.fury.io/rb/rdkafka.svg)](https://badge.fury.io/rb/rdkafka)
5
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/ecb1765f81571cccdb0e/maintainability)](https://codeclimate.com/github/appsignal/rdkafka-ruby/maintainability)
6
- [![Test Coverage](https://api.codeclimate.com/v1/badges/ecb1765f81571cccdb0e/test_coverage)](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.3+.
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
- ## Known issues
72
-
73
- When using forked process such as when using Unicorn you currently need
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: wurstmeister/zookeeper
5
- ports:
6
- - "2181:2181"
7
+ image: confluentinc/cp-zookeeper:latest
8
+ environment:
9
+ ZOOKEEPER_CLIENT_PORT: 2181
10
+ ZOOKEEPER_TICK_TIME: 2000
11
+
7
12
  kafka:
8
- image: wurstmeister/kafka:1.0.1
13
+ image: confluentinc/cp-kafka:latest
14
+ depends_on:
15
+ - zookeeper
9
16
  ports:
10
- - "9092:9092"
17
+ - 9092:9092
11
18
  environment:
12
- KAFKA_ADVERTISED_HOST_NAME: localhost
13
- KAFKA_ADVERTISED_PORT: 9092
19
+ KAFKA_BROKER_ID: 1
14
20
  KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
15
- KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'false'
16
- KAFKA_CREATE_TOPICS: "consume_test_topic:3:1,empty_test_topic:3:1,load_test_topic:3:1,produce_test_topic:3:1,rake_test_topic:3:1,watermarks_test_topic:3:1"
17
- volumes:
18
- - /var/run/docker.sock:/var/run/docker.sock
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
- * Download the new version `tar.gz` from https://github.com/edenhill/librdkafka/
9
- * Generate a `sha256` with (if using MacOS) `shasum -a 256 <file>`
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
- ## Disclaimer
14
-
15
- Currently the `librdkafka` project does not provide
16
- checksums of releases. The checksum provided here is generated on a best
17
- effort basis. If the CDN would be compromised at the time of download the
18
- checksum could be incorrect.
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
- if recipe.host.include?("darwin") && Dir.exists?("/usr/local/opt/openssl")
27
- ENV["CPPFLAGS"] = "-I/usr/local/opt/openssl/include"
28
- ENV["LDFLAGS"] = "-L/usr/local/opt/openssl/lib"
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