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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +14 -0
  3. data/.rubocop.yml +210 -0
  4. data/.travis.yml +45 -0
  5. data/CHANGELOG.md +3 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +182 -0
  10. data/Rakefile +69 -0
  11. data/examples/consumer.rb +55 -0
  12. data/examples/producer.rb +46 -0
  13. data/ext/Rakefile +69 -0
  14. data/kafka.gemspec +39 -0
  15. data/lib/kafka/admin.rb +141 -0
  16. data/lib/kafka/config.rb +145 -0
  17. data/lib/kafka/consumer.rb +87 -0
  18. data/lib/kafka/error.rb +44 -0
  19. data/lib/kafka/ffi/admin/admin_options.rb +121 -0
  20. data/lib/kafka/ffi/admin/config_entry.rb +97 -0
  21. data/lib/kafka/ffi/admin/config_resource.rb +101 -0
  22. data/lib/kafka/ffi/admin/delete_topic.rb +19 -0
  23. data/lib/kafka/ffi/admin/new_partitions.rb +77 -0
  24. data/lib/kafka/ffi/admin/new_topic.rb +91 -0
  25. data/lib/kafka/ffi/admin/result.rb +66 -0
  26. data/lib/kafka/ffi/admin/topic_result.rb +32 -0
  27. data/lib/kafka/ffi/admin.rb +16 -0
  28. data/lib/kafka/ffi/broker_metadata.rb +32 -0
  29. data/lib/kafka/ffi/client.rb +640 -0
  30. data/lib/kafka/ffi/config.rb +382 -0
  31. data/lib/kafka/ffi/consumer.rb +342 -0
  32. data/lib/kafka/ffi/error.rb +25 -0
  33. data/lib/kafka/ffi/event.rb +215 -0
  34. data/lib/kafka/ffi/group_info.rb +75 -0
  35. data/lib/kafka/ffi/group_list.rb +27 -0
  36. data/lib/kafka/ffi/group_member_info.rb +52 -0
  37. data/lib/kafka/ffi/message/header.rb +205 -0
  38. data/lib/kafka/ffi/message.rb +205 -0
  39. data/lib/kafka/ffi/metadata.rb +58 -0
  40. data/lib/kafka/ffi/opaque.rb +81 -0
  41. data/lib/kafka/ffi/opaque_pointer.rb +73 -0
  42. data/lib/kafka/ffi/partition_metadata.rb +61 -0
  43. data/lib/kafka/ffi/producer.rb +144 -0
  44. data/lib/kafka/ffi/queue.rb +65 -0
  45. data/lib/kafka/ffi/topic.rb +32 -0
  46. data/lib/kafka/ffi/topic_config.rb +126 -0
  47. data/lib/kafka/ffi/topic_metadata.rb +42 -0
  48. data/lib/kafka/ffi/topic_partition.rb +43 -0
  49. data/lib/kafka/ffi/topic_partition_list.rb +167 -0
  50. data/lib/kafka/ffi.rb +624 -0
  51. data/lib/kafka/poller.rb +28 -0
  52. data/lib/kafka/producer/delivery_report.rb +120 -0
  53. data/lib/kafka/producer.rb +127 -0
  54. data/lib/kafka/version.rb +8 -0
  55. data/lib/kafka.rb +11 -0
  56. metadata +159 -0
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/ffi/opaque_pointer"
4
+
5
+ module Kafka::FFI::Admin
6
+ class ConfigEntry < ::Kafka::FFI::OpaquePointer
7
+ # Returns the configuration property name
8
+ #
9
+ # @return [String] Configuration property name
10
+ def name
11
+ ::Kafka::FFI.rd_kafka_ConfigEntry_name(self)
12
+ end
13
+
14
+ # Returns the configuration value
15
+ #
16
+ # @return [nil] Value is sensitive or unset
17
+ # @return [String] Configuration value
18
+ def value
19
+ ::Kafka::FFI.rd_kafka_ConfigEntry_value(self)
20
+ end
21
+
22
+ # Returns the source of the config
23
+ #
24
+ # @see ffi.rb config_source
25
+ #
26
+ # @return [Symbol] Source of the config
27
+ def source
28
+ ::Kafka::FFI.rd_kafka_ConfigEntry_source(self)
29
+ end
30
+
31
+ # List of synonyms for the config entry
32
+ #
33
+ # @return [nil] ConfigEntry was not returned by DescribeConfigs
34
+ # @return [Array<ConfigEntry>] Set of synonyms for the config
35
+ def synonyms
36
+ count = ::FFI::MemoryPointer.new(:pointer)
37
+
38
+ entries = ::Kafka::FFI.rd_kafka_ConfigEntry_synonyms(self, count)
39
+ if entries.null?
40
+ return nil
41
+ end
42
+
43
+ entries.read_array_of_pointer(count.read(:size_t)).map do |ptr|
44
+ ConfigEntry.new(ptr)
45
+ end
46
+ ensure
47
+ count.free
48
+ end
49
+
50
+ # rubocop:disable Naming/PredicateName
51
+
52
+ # Returns true if the config property is read-only on the broker. Only
53
+ # returns a boolean when called on a ConfigEntry from a DescribeConfigs
54
+ # result.
55
+ #
56
+ # @return [nil] ConfigEntry was not returned by DescribeConfigs
57
+ # @return [Boolean] If the property is read only
58
+ def is_read_only
59
+ val = ::Kafka::FFI.rd_kafka_ConfigEntry_is_read_only(self)
60
+ val == -1 ? nil : val == 1
61
+ end
62
+
63
+ # Returns true if the config property is set to its default. Only returns a
64
+ # boolean when use on a ConfigEntry from a DescribeConfigs result.
65
+ #
66
+ # @return [nil] ConfigEntry was not returned by DescribeConfigs
67
+ # @return [Boolean] If the property is set to default
68
+ def is_default
69
+ val = ::Kafka::FFI.rd_kafka_ConfigEntry_is_default(self)
70
+ val == -1 ? nil : val == 1
71
+ end
72
+
73
+ # Returns true if the config property is sensitive. Only returns a boolean
74
+ # when use on a ConfigEntry from a DescribeConfigs result.
75
+ #
76
+ # @return [nil] ConfigEntry was not returned by DescribeConfigs
77
+ # @return [Boolean] If the property is set to default
78
+ def is_sensitive
79
+ val = ::Kafka::FFI.rd_kafka_ConfigEntry_is_sensitive(self)
80
+ val == -1 ? nil : val == 1
81
+ end
82
+
83
+ # Returns true if the entry is a synonym for another config option.
84
+ #
85
+ # @return [Boolean]
86
+ def is_synonym
87
+ ::Kafka::FFI.rd_kafka_ConfigEntry_is_sensitive(self) == 1
88
+ end
89
+
90
+ # rubocop:enable Naming/PredicateName
91
+
92
+ alias read_only? is_read_only
93
+ alias default? is_default
94
+ alias sensitive? is_sensitive
95
+ alias synonym? is_synonym
96
+ end
97
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/ffi/opaque_pointer"
4
+
5
+ module Kafka::FFI::Admin
6
+ class ConfigResource < ::Kafka::FFI::OpaquePointer
7
+ # Create a new ConfigResource
8
+ #
9
+ # @example Build ConfigResource for a topic
10
+ # ConfigResource.new(:topic, "events")
11
+ #
12
+ # @example Build ConfigResource for a broker
13
+ # ConfigResource.new(:broker, broker_id)
14
+ #
15
+ # @see ffi.rb resource_type enum
16
+ #
17
+ # @param type [:broker, :topic, :group] Type of config resource
18
+ # @param name [String] Name of resource (broker_id, topic name, etc...)
19
+ def self.new(type, name)
20
+ ::Kafka::FFI.rd_kafka_ConfigResource_new(type, name)
21
+ end
22
+
23
+ # Set configuration name and value pair
24
+ #
25
+ # @note This will overwrite the current value
26
+ #
27
+ # @param name [String] Config option name
28
+ # @param value [nil] Revert config to default
29
+ # @param value [String] Value to set config option to
30
+ #
31
+ # @raise [Kafka::ResponseError] Invalid input
32
+ def set_config(name, value)
33
+ err = ::Kafka::FFI.rd_kafka_ConfigResource_set_config(self, name, value)
34
+ if err != :ok
35
+ raise ::Kafka::ResponseError, err
36
+ end
37
+
38
+ nil
39
+ end
40
+
41
+ # Retrieve an array of ConfigEntry from the resource.
42
+ #
43
+ # @return [Array<ConfigEntry>] Config entries for the resource
44
+ def configs
45
+ count = ::FFI::MemoryPointer.new(:pointer)
46
+
47
+ configs = ::Kafka::FFI.rd_kafka_ConfigResource_configs(self, count)
48
+ if configs.null?
49
+ return nil
50
+ end
51
+
52
+ configs = configs.read_array_of_pointer(count.read(:size_t))
53
+ configs.map! { |p| ConfigEntry.from_native(p, nil) }
54
+ ensure
55
+ count.free
56
+ end
57
+
58
+ # Returns the type of the resource
59
+ #
60
+ # @see ffi.rb resource_type enum
61
+ #
62
+ # @return [Symbol] Type of config resource
63
+ def type
64
+ ::Kafka::FFI.rd_kafka_ConfigResource_type(self)
65
+ end
66
+
67
+ # Returns the config option name
68
+ #
69
+ # @return [String] Name of the config
70
+ def name
71
+ ::Kafka::FFI.rd_kafka_ConfigResource_name(self)
72
+ end
73
+
74
+ # Returns the response error received from an AlterConfigs request.
75
+ #
76
+ # @note Only set when ConfigResource was returned from AlterConfigs.
77
+ #
78
+ # @return [nil] No error
79
+ # @return [Kafka::ResponseError] AlterConfig request error
80
+ def error
81
+ err = ::Kafka::FFI.rd_kafka_ConfigResource_error(self)
82
+ err == :ok ? nil : ::Kafka::ResponseError.new(err)
83
+ end
84
+
85
+ # Returns a string describing the error received for this resource during
86
+ # an AlterConfigs request.
87
+ #
88
+ # @note Only set when ConfigResource was returned from AlterConfigs.
89
+ #
90
+ # @return [nil] No error
91
+ # @return [String] AlterConfig request error
92
+ def error_string
93
+ ::Kafka::FFI.rd_kafka_ConfigResource_error_string(self)
94
+ end
95
+
96
+ # Destroy the ConfigResource, returning its resources back to the system.
97
+ def destroy
98
+ ::Kafka::FFI.rd_kafka_ConfigResource_destroy(self)
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/ffi/opaque_pointer"
4
+
5
+ module Kafka::FFI::Admin
6
+ class DeleteTopic < ::Kafka::FFI::OpaquePointer
7
+ def self.new(topic)
8
+ ::Kafka::FFI.rd_kafka_DeleteTopic_new(topic)
9
+ end
10
+
11
+ # Release the resources used by the DeleteTopic. It is the application's
12
+ # responsibility to call #destroy when it is done with the object.
13
+ def destroy
14
+ if !pointer.null?
15
+ ::Kafka::FFI.rd_kafka_DeleteTopic_destroy(self)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/ffi/opaque_pointer"
4
+
5
+ module Kafka::FFI::Admin
6
+ class NewPartitions < ::Kafka::FFI::OpaquePointer
7
+ # Allocates a new NewPartitions request for passing to CreatePartitions to
8
+ # increase the number of partitions for an existing topic.
9
+ #
10
+ # @param topic [String] Name of the topic to adjust
11
+ # @param partition_count [Integer] Increase the number of partitions to
12
+ # this value.
13
+ #
14
+ # @raise [ArgumentError] Invalid topic or partition_count
15
+ def self.new(topic, partition_count)
16
+ error = ::FFI::MemoryPointer.new(:char, 512)
17
+
18
+ if topic.nil? || topic.empty?
19
+ # Check in Ruby as nil will cause a segfault as of 1.3.0
20
+ raise ArgumentError, "topic name is required"
21
+ end
22
+
23
+ req = ::Kafka::FFI.rd_kafka_NewPartitions_new(topic, partition_count, error, error.size)
24
+ if req.nil?
25
+ raise ArgumentError, error.read_string
26
+ end
27
+
28
+ req
29
+ ensure
30
+ error.free
31
+ end
32
+
33
+ # Assign the partition by index, relative to existing partition count, to
34
+ # be replicated on the set of brokers specified by broker_ids. If called,
35
+ # this method must be called consecutively for each new partition being
36
+ # created starting with an index of 0.
37
+ #
38
+ # @note This MUST either be called for all new partitions or not at all.
39
+ #
40
+ # @example Assigning broker assignments for two new partitions
41
+ # # Topic already has 3 partitions and replication factor of 2.
42
+ # request = NewPartitions("topic", 5)
43
+ # request.set_replica_assignment(0, [1001, 1003])
44
+ # request.set_replica_assignment(1, [1002, 1001])
45
+ #
46
+ # @param partition_index [Integer] Index of the new partition being
47
+ # created.
48
+ # @param broker_ids [Array<Integer>] Broker IDs to be assigned a replica of
49
+ # the topic. Number of broker ids should match the topic replication
50
+ # factor.
51
+ #
52
+ # @raise [Kafka::ResponseError] Arguments were invalid or partition_index
53
+ # was not called consecutively.
54
+ def set_replica_assignment(partition_index, broker_ids)
55
+ error = ::FFI::MemoryPointer.new(:char, 512)
56
+
57
+ broker_list = ::FFI::MemoryPointer.new(:int32, broker_ids.length)
58
+ broker_list.write_array_of_int32(broker_ids)
59
+
60
+ resp = ::Kafka::FFI.rd_kafka_NewPartitions_set_replica_assignment(self, partition_index, broker_list, broker_ids.length, error, error.size)
61
+ if resp != :ok
62
+ raise ::Kafka::ResponseError.new(resp, error.read_string)
63
+ end
64
+
65
+ nil
66
+ ensure
67
+ error.free
68
+ end
69
+
70
+ # Destroy and free the NewPartitions, releasing it's resources back to the
71
+ # system.
72
+ def destroy
73
+ ::Kafka::FFI.rd_kafka_NewPartitions_destroy(self)
74
+ end
75
+ end
76
+ end
77
+
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/ffi/error"
4
+ require "kafka/ffi/opaque_pointer"
5
+
6
+ module Kafka::FFI::Admin
7
+ class NewTopic < ::Kafka::FFI::OpaquePointer
8
+ # Create a new NewTopic for passing to Admin::Client#create_topics. It is
9
+ # the application's responsiblity to call #destroy when done with the
10
+ # object.
11
+ #
12
+ # @param name [String] Name of the topic to create
13
+ # @param partitions [Integer] Number of partitions in the topic
14
+ #
15
+ # @param replication_factor [Integer] Default replication factor for the
16
+ # topic's partitions.
17
+ # @param replication_factor [-1] Value from #set_replica_assignment will be
18
+ # used.
19
+ #
20
+ # @raise [ArgumentError] Parameters were invalid
21
+ #
22
+ # @return [NewTopic]
23
+ def self.new(name, partitions, replication_factor)
24
+ # Allocate memory for the error message
25
+ error = ::FFI::MemoryPointer.new(:char, 512)
26
+
27
+ if name.nil? || name.empty?
28
+ raise ArgumentError, " name is required and cannot be blank"
29
+ end
30
+
31
+ obj = ::Kafka::FFI.rd_kafka_NewTopic_new(name, partitions, replication_factor, error, error.size)
32
+ if obj.nil?
33
+ raise ArgumentError, error.read_string
34
+ end
35
+
36
+ obj
37
+ ensure
38
+ error.free
39
+ end
40
+
41
+ # Set the broker assignment for partition to the replica set in broker_ids.
42
+ #
43
+ # @note If called, must be call consecutively for each partition, starting
44
+ # at 0.
45
+ # @note new must have been called with replication_factor of -1
46
+ #
47
+ # @param partition [Integer] Partition to assign to the brokers
48
+ # @param broker_ids [Integer, Array<Integer>] Brokers that will be assigned
49
+ # the partition for the topic.
50
+ #
51
+ # @raise [Kafka::ResponseError] Error occurred setting config
52
+ def set_replica_assignment(partition, broker_ids)
53
+ broker_ids = Array(broker_ids)
54
+
55
+ brokers = ::FFI::MemoryPointer.new(:int32, broker_ids.size)
56
+ error = ::FFI::MemoryPointer.new(:char, 512)
57
+
58
+ brokers.write_array_of_int32(broker_ids)
59
+
60
+ err = ::Kafka::FFI.rd_kafka_NewTopic_set_replica_assignment(self, partition, brokers, broker_ids.size, error, error.size)
61
+ if err != :ok
62
+ raise ::Kafka::ResponseError, err
63
+ end
64
+
65
+ nil
66
+ ensure
67
+ error.free
68
+ brokers.free
69
+ end
70
+
71
+ # Set the broker side topic configuration name/value pair.
72
+ #
73
+ # @raise [Kafka::ResponseError] Arguments were invalid
74
+ def set_config(name, value)
75
+ err = ::Kafka::FFI.rd_kafka_NewTopic_set_config(self, name, value)
76
+ if err != :ok
77
+ raise ::Kafka::ResponseError, err
78
+ end
79
+
80
+ nil
81
+ end
82
+
83
+ # Release the memory held by NewTopic back to the system. This must be
84
+ # called by the application when it is done with the object.
85
+ def destroy
86
+ if !pointer.null?
87
+ ::Kafka::FFI.rd_kafka_NewTopic_destroy(self)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kafka::FFI::Admin
4
+ class Result < Array
5
+ def initialize(event)
6
+ @event = event
7
+
8
+ super(get_results(event))
9
+ end
10
+
11
+ def get_results(event)
12
+ count = ::FFI::MemoryPointer.new(:size_t)
13
+
14
+ klass, results =
15
+ case event.type
16
+ when :create_topics
17
+ [
18
+ TopicResult,
19
+ ::Kafka::FFI.rd_kafka_CreateTopics_result_topics(event, count),
20
+ ]
21
+ when :delete_topics
22
+ [
23
+ TopicResult,
24
+ ::Kafka::FFI.rd_kafka_DeleteTopics_result_topics(event, count),
25
+ ]
26
+ when :create_partitions
27
+ [
28
+ TopicResult,
29
+ ::Kafka::FFI.rd_kafka_CreateTopics_result_topics(event, count),
30
+ ]
31
+ when :alter_configs
32
+ [
33
+ ConfigResource,
34
+ ::Kafka::FFI.rd_kafka_AlterConfigs_result_resources(event, count),
35
+ ]
36
+ when :describe_configs
37
+ [
38
+ ConfigResource,
39
+ ::Kafka::FFI.rd_kafka_DescribeConfigs_result_resources(event, count),
40
+ ]
41
+ else
42
+ raise ArgumentError, "unable to map #{event.class} to result type"
43
+ end
44
+
45
+ if results.null?
46
+ return []
47
+ end
48
+
49
+ results = results.read_array_of_pointer(count.read(:size_t))
50
+ results.map! { |p| klass.from_native(p, nil) }
51
+ ensure
52
+ count.free
53
+ end
54
+
55
+ def destroy
56
+ if @event
57
+ @event.destroy
58
+ @event = nil
59
+ end
60
+
61
+ clear
62
+
63
+ nil
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ffi"
4
+ require "kafka/ffi/opaque_pointer"
5
+
6
+ module Kafka::FFI::Admin
7
+ class TopicResult < ::Kafka::FFI::OpaquePointer
8
+ # Returns the name of the topic the result is for.
9
+ #
10
+ # @return [String] Topic name
11
+ def name
12
+ ::Kafka::FFI.rd_kafka_topic_result_name(self)
13
+ end
14
+ alias topic name
15
+
16
+ # Returns either nil for success or an error with details about why the
17
+ # topic operation failed.
18
+ #
19
+ # @return [nil] Topic operation was successful for this topic.
20
+ # @return [Kafka::ResponseError] Error performing the operation for this
21
+ # topic.
22
+ def error
23
+ err = ::Kafka::FFI.rd_kafka_topic_result_error(self)
24
+ if err != :ok
25
+ ::Kafka::ResponseError.new(
26
+ err,
27
+ ::Kafka::FFI.rd_kafka_topic_result_error_string(self),
28
+ )
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "kafka/ffi"
4
+
5
+ module Kafka::FFI
6
+ module Admin
7
+ require "kafka/ffi/admin/result"
8
+ require "kafka/ffi/admin/new_topic"
9
+ require "kafka/ffi/admin/delete_topic"
10
+ require "kafka/ffi/admin/topic_result"
11
+ require "kafka/ffi/admin/admin_options"
12
+ require "kafka/ffi/admin/config_entry"
13
+ require "kafka/ffi/admin/new_partitions"
14
+ require "kafka/ffi/admin/config_resource"
15
+ end
16
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kafka::FFI
4
+ class BrokerMetadata < ::FFI::Struct
5
+ layout(
6
+ :id, :int32,
7
+ :host, :string,
8
+ :port, :int
9
+ )
10
+
11
+ # Returns the Broker's cluster ID
12
+ #
13
+ # @return [Integer] Broker ID
14
+ def id
15
+ self[:id]
16
+ end
17
+
18
+ # Returns the hostname of the Broker
19
+ #
20
+ # @return [String] Broker hostname
21
+ def host
22
+ self[:host]
23
+ end
24
+
25
+ # Returns the port used to connect to the Broker
26
+ #
27
+ # @return [Integer] Broker port
28
+ def port
29
+ self[:port]
30
+ end
31
+ end
32
+ end