kafka 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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