kazoo-ruby 0.2.0 → 0.3.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/lib/kazoo/cli.rb +11 -146
- data/lib/kazoo/cli/cluster.rb +28 -0
- data/lib/kazoo/cli/common.rb +32 -0
- data/lib/kazoo/cli/consumers.rb +94 -0
- data/lib/kazoo/cli/topics.rb +43 -0
- data/lib/kazoo/consumergroup.rb +86 -14
- data/lib/kazoo/partition.rb +4 -0
- data/lib/kazoo/topic.rb +13 -2
- data/lib/kazoo/version.rb +1 -1
- data/test/functional/functional_consumergroup_test.rb +29 -0
- metadata +5 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 129a9f807dc2c4a376d8941c64f25c652427fac1
|
4
|
+
data.tar.gz: 1f3ea8b05527feaef6b5b6fce8d7ff8eff19f987
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb6a831e906561296e5c8cb07116a470f4a612ad16b4d92d21d7b64bc3b58d01abf05f10486a4f8d2983b9afd4c1f340fc763bbdd3849dcd935f05675a3702d0
|
7
|
+
data.tar.gz: 560c455ec3ae69a0d2cfcb7f955fec90e4a26eaef79f636632fa40784745537368347ada6f028074a30585760a29bcaf14d3b80d1fce20503846d318820e4f6c
|
data/lib/kazoo/cli.rb
CHANGED
@@ -1,155 +1,20 @@
|
|
1
1
|
require 'kazoo'
|
2
2
|
require 'thor'
|
3
3
|
|
4
|
+
require 'kazoo/cli/common'
|
5
|
+
require 'kazoo/cli/cluster'
|
6
|
+
require 'kazoo/cli/consumers'
|
7
|
+
require 'kazoo/cli/topics'
|
8
|
+
|
4
9
|
module Kazoo
|
5
10
|
class CLI < Thor
|
6
|
-
|
7
|
-
|
8
|
-
desc "cluster", "Describes the Kafka cluster as registered in Zookeeper"
|
9
|
-
def cluster
|
10
|
-
validate_class_options!
|
11
|
-
|
12
|
-
kafka_cluster.brokers.values.sort_by(&:id).each do |broker|
|
13
|
-
$stdout.puts "#{broker.id}:\t#{broker.addr}\t(hosts #{broker.replicated_partitions.length} partitions, leads #{broker.led_partitions.length})"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
desc "topics", "Lists all topics in the cluster"
|
18
|
-
def topics
|
19
|
-
validate_class_options!
|
20
|
-
|
21
|
-
kafka_cluster.topics.values.sort_by(&:name).each do |topic|
|
22
|
-
$stdout.puts topic.name
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
desc "create_topic", "Creates a new topic"
|
27
|
-
option :name, type: :string, required: true
|
28
|
-
option :partitions, type: :numeric, required: true
|
29
|
-
option :replication_factor, type: :numeric, required: true
|
30
|
-
def create_topic
|
31
|
-
validate_class_options!
|
32
|
-
|
33
|
-
kafka_cluster.create_topic(options[:name], partitions: options[:partitions], replication_factor: options[:replication_factor])
|
34
|
-
end
|
35
|
-
|
36
|
-
desc "delete_topic", "Removes a topic"
|
37
|
-
option :name, type: :string, required: true
|
38
|
-
def delete_topic
|
39
|
-
validate_class_options!
|
40
|
-
|
41
|
-
kafka_cluster.topics.fetch(options[:name]).destroy
|
42
|
-
end
|
43
|
-
|
44
|
-
option :topic, type: :string
|
45
|
-
desc "partitions", "Lists partitions"
|
46
|
-
def partitions
|
47
|
-
validate_class_options!
|
48
|
-
|
49
|
-
topics = kafka_cluster.topics.values
|
50
|
-
topics.select! { |t| t.name == options[:topic] } if options[:topic]
|
51
|
-
topics.sort_by!(&:name)
|
52
|
-
|
53
|
-
topics.each do |topic|
|
54
|
-
topic.partitions.each do |partition|
|
55
|
-
$stdout.puts "#{partition.topic.name}/#{partition.id}\tReplicas: #{partition.replicas.map(&:id).join(",")}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
option :replicas, type: :numeric, default: 1
|
61
|
-
desc "critical <broker>", "Determine whether a broker is critical"
|
62
|
-
def critical(broker_name)
|
63
|
-
validate_class_options!
|
64
|
-
|
65
|
-
if broker(broker_name).critical?(replicas: options[:replicas])
|
66
|
-
raise Thor::Error, "WARNING: broker #{broker_name} is critical and cannot be stopped safely!"
|
67
|
-
else
|
68
|
-
$stdout.puts "Broker #{broker_name} is non-critical and can be stopped safely."
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
desc "consumergroups", "Lists the consumergroups registered for this Kafka cluster"
|
74
|
-
def consumergroups
|
75
|
-
validate_class_options!
|
76
|
-
|
77
|
-
kafka_cluster.consumergroups.each do |group|
|
78
|
-
instances = group.instances
|
79
|
-
if instances.length == 0
|
80
|
-
puts "- #{group.name}: inactive"
|
81
|
-
else
|
82
|
-
puts "- #{group.name}: #{instances.length} running instances"
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
desc "consumergroup", "Prints information about a consumer group"
|
88
|
-
option :name, type: :string, required: true
|
89
|
-
def consumergroup
|
90
|
-
validate_class_options!
|
91
|
-
|
92
|
-
cg = kafka_cluster.consumergroup(options[:name])
|
93
|
-
raise Kazoo::Error, "Consumergroup #{options[:name]} is not registered in Zookeeper" unless cg.exists?
|
94
|
-
|
95
|
-
puts "Consumer group: #{cg.name}\n"
|
96
|
-
|
97
|
-
if cg.active?
|
98
|
-
puts "Running instances:"
|
99
|
-
cg.instances.each do |instance|
|
100
|
-
puts "- #{instance.id}"
|
101
|
-
end
|
102
|
-
else
|
103
|
-
puts "This consumer group is inactive."
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
desc "delete_consumergroup", "Removes a consumer group from Zookeeper"
|
108
|
-
option :name, type: :string, required: true
|
109
|
-
def delete_consumergroup
|
110
|
-
validate_class_options!
|
111
|
-
|
112
|
-
cg = kafka_cluster.consumergroup(options[:name])
|
113
|
-
raise Kazoo::Error, "Consumergroup #{options[:name]} is not registered in Zookeeper" unless cg.exists?
|
114
|
-
raise Kazoo::Error, "Cannot remove consumergroup #{cg.name} because it's still active" if cg.active?
|
115
|
-
|
116
|
-
cg.destroy
|
117
|
-
end
|
118
|
-
|
119
|
-
desc "reset_consumergroup", "Resets all the offsets stored for a consumergroup"
|
120
|
-
option :name, type: :string, required: true
|
121
|
-
def reset_consumergroup
|
122
|
-
validate_class_options!
|
123
|
-
|
124
|
-
cg = kafka_cluster.consumergroup(options[:name])
|
125
|
-
raise Kazoo::Error, "Consumergroup #{options[:name]} is not registered in Zookeeper" unless cg.exists?
|
126
|
-
raise Kazoo::Error, "Cannot remove consumergroup #{cg.name} because it's still active" if cg.active?
|
127
|
-
|
128
|
-
cg.reset_all_offsets
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
private
|
133
|
-
|
134
|
-
def validate_class_options!
|
135
|
-
if options[:zookeeper].nil? || options[:zookeeper] == ''
|
136
|
-
raise Thor::InvocationError, "Please supply --zookeeper argument, or set the ZOOKEEPER environment variable"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def broker(name_or_id)
|
141
|
-
broker = if name_or_id =~ /\A\d+\z/
|
142
|
-
kafka_cluster.brokers[name_or_id.to_i]
|
143
|
-
else
|
144
|
-
kafka_cluster.brokers.values.detect { |b| b.addr == name_or_id } || cluster.brokers.values.detect { |b| b.host == name_or_id }
|
145
|
-
end
|
11
|
+
desc "cluster SUBCOMMAND ...ARGS", "Inspect the Kafka cluster"
|
12
|
+
subcommand "cluster", Kazoo::CLI::Cluster
|
146
13
|
|
147
|
-
|
148
|
-
|
149
|
-
end
|
14
|
+
desc "topics SUBCOMMAND ...ARGS", "Manage consumer groups"
|
15
|
+
subcommand "topics", Kazoo::CLI::Topics
|
150
16
|
|
151
|
-
|
152
|
-
|
153
|
-
end
|
17
|
+
desc "consumers SUBCOMMAND ...ARGS", "Manage consumer groups"
|
18
|
+
subcommand "consumers", Kazoo::CLI::Consumers
|
154
19
|
end
|
155
20
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Kazoo
|
2
|
+
class CLI < Thor
|
3
|
+
class Cluster < Thor
|
4
|
+
include Kazoo::CLI::Common
|
5
|
+
|
6
|
+
desc "brokers", "Describes the brokers in the cluster as registered in Zookeeper"
|
7
|
+
def brokers
|
8
|
+
validate_class_options!
|
9
|
+
|
10
|
+
kafka_cluster.brokers.values.sort_by(&:id).each do |broker|
|
11
|
+
$stdout.puts "#{broker.id}:\t#{broker.addr}\t(hosts #{broker.replicated_partitions.length} partitions, leads #{broker.led_partitions.length})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
option :replicas, type: :numeric, default: 1
|
16
|
+
desc "critical BROKER", "Determine whether a broker is critical"
|
17
|
+
def critical(broker_name)
|
18
|
+
validate_class_options!
|
19
|
+
|
20
|
+
if broker(broker_name).critical?(replicas: options[:replicas])
|
21
|
+
raise Thor::Error, "WARNING: broker #{broker_name} is critical and cannot be stopped safely!"
|
22
|
+
else
|
23
|
+
$stdout.puts "Broker #{broker_name} is non-critical and can be stopped safely."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Kazoo
|
2
|
+
class CLI < Thor
|
3
|
+
module Common
|
4
|
+
def self.included(base)
|
5
|
+
base.class_option :zookeeper, type: :string, default: ENV['ZOOKEEPER']
|
6
|
+
end
|
7
|
+
|
8
|
+
private
|
9
|
+
|
10
|
+
def validate_class_options!
|
11
|
+
if options[:zookeeper].nil? || options[:zookeeper] == ''
|
12
|
+
raise Thor::InvocationError, "Please supply --zookeeper argument, or set the ZOOKEEPER environment variable"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def broker(name_or_id)
|
17
|
+
broker = if name_or_id =~ /\A\d+\z/
|
18
|
+
kafka_cluster.brokers[name_or_id.to_i]
|
19
|
+
else
|
20
|
+
kafka_cluster.brokers.values.detect { |b| b.addr == name_or_id } || kafka_cluster.brokers.values.detect { |b| b.host == name_or_id }
|
21
|
+
end
|
22
|
+
|
23
|
+
raise Thor::InvocationError, "Broker #{name_or_id.inspect} not found!" if broker.nil?
|
24
|
+
broker
|
25
|
+
end
|
26
|
+
|
27
|
+
def kafka_cluster
|
28
|
+
@kafka_cluster ||= Kazoo::Cluster.new(options[:zookeeper])
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Kazoo
|
2
|
+
class CLI < Thor
|
3
|
+
class Consumers < Thor
|
4
|
+
include Kazoo::CLI::Common
|
5
|
+
|
6
|
+
desc "list", "Lists the consumergroups registered for this Kafka cluster"
|
7
|
+
def list
|
8
|
+
validate_class_options!
|
9
|
+
|
10
|
+
kafka_cluster.consumergroups.sort_by(&:name).each do |group|
|
11
|
+
instances = group.instances
|
12
|
+
if instances.length == 0
|
13
|
+
puts "- #{group.name}: inactive"
|
14
|
+
else
|
15
|
+
puts "- #{group.name}: #{instances.length} running instances"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
desc "show NAME", "Prints information about a consumer group"
|
21
|
+
def show(name)
|
22
|
+
validate_class_options!
|
23
|
+
|
24
|
+
cg = kafka_cluster.consumergroup(name)
|
25
|
+
raise Kazoo::Error, "Consumergroup #{cg.name} is not registered in Zookeeper" unless cg.exists?
|
26
|
+
|
27
|
+
topics = cg.topics.sort_by(&:name)
|
28
|
+
|
29
|
+
puts "Consumer name: #{cg.name}"
|
30
|
+
puts "Created on: #{cg.created_at}"
|
31
|
+
puts "Topics (#{topics.length}): #{topics.map(&:name).join(', ')}"
|
32
|
+
|
33
|
+
instances = cg.instances
|
34
|
+
if instances.length > 0
|
35
|
+
|
36
|
+
puts
|
37
|
+
puts "Running instances (#{instances.length}):"
|
38
|
+
instances.each do |instance|
|
39
|
+
puts "- #{instance.id}\t(created on #{instance.created_at})"
|
40
|
+
end
|
41
|
+
|
42
|
+
partition_claims = cg.partition_claims
|
43
|
+
if partition_claims.length > 0
|
44
|
+
partitions = partition_claims.keys.sort_by { |p| [p.topic.name, p.id] }
|
45
|
+
|
46
|
+
puts
|
47
|
+
puts "Partition claims (#{partition_claims.length}):"
|
48
|
+
partitions.each do |partition|
|
49
|
+
instance = partition_claims[partition]
|
50
|
+
puts "- #{partition.key}: #{instance.id}"
|
51
|
+
end
|
52
|
+
else
|
53
|
+
puts
|
54
|
+
puts "WARNING: this consumer group is active but hasn't claimed any partitions"
|
55
|
+
end
|
56
|
+
|
57
|
+
unclaimed_partitions = (cg.partitions - partition_claims.keys).sort_by { |p| [p.topic.name, p.id] }
|
58
|
+
|
59
|
+
if unclaimed_partitions.length > 0
|
60
|
+
puts
|
61
|
+
puts "WARNING: this consumergroup has #{unclaimed_partitions.length} unclaimed partitions:"
|
62
|
+
unclaimed_partitions.each do |partition|
|
63
|
+
puts "- #{partition.key}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
else
|
67
|
+
puts "This consumer group is inactive."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "delete NAME", "Removes a consumer group from Zookeeper"
|
72
|
+
def delete(name)
|
73
|
+
validate_class_options!
|
74
|
+
|
75
|
+
cg = kafka_cluster.consumergroup(name)
|
76
|
+
raise Kazoo::Error, "Consumergroup #{cg.name} is not registered in Zookeeper" unless cg.exists?
|
77
|
+
raise Kazoo::Error, "Cannot remove consumergroup #{cg.name} because it's still active" if cg.active?
|
78
|
+
|
79
|
+
cg.destroy
|
80
|
+
end
|
81
|
+
|
82
|
+
desc "reset [NAME]", "Resets all the offsets stored for a consumergroup"
|
83
|
+
def reset(name)
|
84
|
+
validate_class_options!
|
85
|
+
|
86
|
+
cg = kafka_cluster.consumergroup(name)
|
87
|
+
raise Kazoo::Error, "Consumergroup #{cg.name} is not registered in Zookeeper" unless cg.exists?
|
88
|
+
raise Kazoo::Error, "Cannot remove consumergroup #{cg.name} because it's still active" if cg.active?
|
89
|
+
|
90
|
+
cg.reset_all_offsets
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Kazoo
|
2
|
+
class CLI < Thor
|
3
|
+
class Topics < Thor
|
4
|
+
include Kazoo::CLI::Common
|
5
|
+
|
6
|
+
desc "list", "Lists all topics in the cluster"
|
7
|
+
def list
|
8
|
+
validate_class_options!
|
9
|
+
|
10
|
+
kafka_cluster.topics.values.sort_by(&:name).each do |topic|
|
11
|
+
$stdout.puts topic.name
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "create TOPIC", "Creates a new topic"
|
16
|
+
option :partitions, type: :numeric, required: true
|
17
|
+
option :replication_factor, type: :numeric, required: true
|
18
|
+
def create(name)
|
19
|
+
validate_class_options!
|
20
|
+
|
21
|
+
kafka_cluster.create_topic(name, partitions: options[:partitions], replication_factor: options[:replication_factor])
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "delete TOPIC", "Removes a topic"
|
25
|
+
def delete(name)
|
26
|
+
validate_class_options!
|
27
|
+
|
28
|
+
kafka_cluster.topics.fetch(name).destroy
|
29
|
+
end
|
30
|
+
|
31
|
+
option :topic, type: :string
|
32
|
+
desc "partitions TOPIC", "Lists partitions for a topic"
|
33
|
+
def partitions(topic)
|
34
|
+
validate_class_options!
|
35
|
+
|
36
|
+
topic = kafka_cluster.topics.fetch(topic)
|
37
|
+
topic.partitions.each do |partition|
|
38
|
+
puts "#{partition.key}\tReplicas: #{partition.replicas.map(&:id).join(",")}\tISR: #{partition.isr.map(&:id).join(",")}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/kazoo/consumergroup.rb
CHANGED
@@ -20,6 +20,13 @@ module Kazoo
|
|
20
20
|
stat.fetch(:stat).exists?
|
21
21
|
end
|
22
22
|
|
23
|
+
def created_at
|
24
|
+
result = cluster.zk.stat(path: "/consumers/#{name}")
|
25
|
+
raise Kazoo::Error, "Failed to get consumer details. Error code: #{result.fetch(:rc)}" if result.fetch(:rc) != Zookeeper::Constants::ZOK
|
26
|
+
|
27
|
+
Time.at(result.fetch(:stat).mtime / 1000.0)
|
28
|
+
end
|
29
|
+
|
23
30
|
|
24
31
|
def instantiate(id: nil)
|
25
32
|
Instance.new(self, id: id)
|
@@ -69,31 +76,88 @@ module Kazoo
|
|
69
76
|
end
|
70
77
|
end
|
71
78
|
|
79
|
+
def topics
|
80
|
+
topic_result = cluster.zk.get_children(path: "/consumers/#{name}/owners")
|
81
|
+
case topic_result.fetch(:rc)
|
82
|
+
when Zookeeper::Constants::ZOK
|
83
|
+
topic_result.fetch(:children).map { |topic_name| cluster.topic(topic_name) }
|
84
|
+
when Zookeeper::Constants::ZNONODE
|
85
|
+
[]
|
86
|
+
else
|
87
|
+
raise Kazoo::Error, "Failed to get subscribed topics. Result code: #{topic_result.fetch(:rc)}"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def partitions
|
92
|
+
partitions, threads, mutex = [], [], Mutex.new
|
93
|
+
topics.each do |topic|
|
94
|
+
threads << Thread.new do
|
95
|
+
topic_partitions = topic.partitions
|
96
|
+
mutex.synchronize { partitions.concat(topic_partitions) }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
threads.each(&:join)
|
101
|
+
return partitions
|
102
|
+
end
|
103
|
+
|
104
|
+
def unclaimed_partitions
|
105
|
+
partitions - partition_claims.keys
|
106
|
+
end
|
107
|
+
|
108
|
+
def partition_claims
|
109
|
+
topic_result = cluster.zk.get_children(path: "/consumers/#{name}/owners")
|
110
|
+
case topic_result.fetch(:rc)
|
111
|
+
when Zookeeper::Constants::ZOK; # continue
|
112
|
+
when Zookeeper::Constants::ZNONODE; return {}
|
113
|
+
else raise Kazoo::Error, "Failed to get partition claims. Result code: #{topic_result.fetch(:rc)}"
|
114
|
+
end
|
115
|
+
|
116
|
+
partition_claims, threads, mutex = {}, [], Mutex.new
|
117
|
+
topic_result.fetch(:children).each do |topic_name|
|
118
|
+
threads << Thread.new do
|
119
|
+
topic = cluster.topic(topic_name)
|
120
|
+
|
121
|
+
partition_result = cluster.zk.get_children(path: "/consumers/#{name}/owners/#{topic.name}")
|
122
|
+
raise Kazoo::Error, "Failed to get partition claims. Result code: #{partition_result.fetch(:rc)}" if partition_result.fetch(:rc) != Zookeeper::Constants::ZOK
|
123
|
+
|
124
|
+
partition_threads = []
|
125
|
+
partition_result.fetch(:children).each do |partition_id|
|
126
|
+
partition_threads << Thread.new do
|
127
|
+
partition = topic.partition(partition_id.to_i)
|
128
|
+
|
129
|
+
claim_result =cluster.zk.get(path: "/consumers/#{name}/owners/#{topic.name}/#{partition.id}")
|
130
|
+
raise Kazoo::Error, "Failed to get partition claims. Result code: #{claim_result.fetch(:rc)}" if claim_result.fetch(:rc) != Zookeeper::Constants::ZOK
|
131
|
+
|
132
|
+
mutex.synchronize { partition_claims[partition] = instantiate(id: claim_result.fetch(:data)) }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
partition_threads.each(&:join)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
threads.each(&:join)
|
140
|
+
return partition_claims
|
141
|
+
end
|
142
|
+
|
72
143
|
def retrieve_offset(partition)
|
73
144
|
result = cluster.zk.get(path: "/consumers/#{name}/offsets/#{partition.topic.name}/#{partition.id}")
|
74
145
|
case result.fetch(:rc)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
nil
|
79
|
-
else
|
80
|
-
raise Kazoo::Error, "Failed to retrieve offset for partition #{partition.topic.name}/#{partition.id}. Error code: #{result.fetch(:rc)}"
|
146
|
+
when Zookeeper::Constants::ZOK; result.fetch(:data).to_i
|
147
|
+
when Zookeeper::Constants::ZNONODE; nil
|
148
|
+
else raise Kazoo::Error, "Failed to retrieve offset for partition #{partition.topic.name}/#{partition.id}. Error code: #{result.fetch(:rc)}"
|
81
149
|
end
|
82
150
|
end
|
83
151
|
|
84
152
|
def retrieve_all_offsets
|
85
|
-
offsets, threads, mutex = {}, [], Mutex.new
|
86
|
-
|
87
153
|
topic_result = cluster.zk.get_children(path: "/consumers/#{name}/offsets")
|
88
154
|
case topic_result.fetch(:rc)
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
return offsets
|
93
|
-
else
|
94
|
-
raise Kazoo::Error, "Failed to retrieve offset for partition #{partition.topic.name}/#{partition.id}. Error code: #{topic_result.fetch(:rc)}"
|
155
|
+
when Zookeeper::Constants::ZOK; # continue
|
156
|
+
when Zookeeper::Constants::ZNONODE; return {}
|
157
|
+
else raise Kazoo::Error, "Failed to retrieve offset for partition #{partition.topic.name}/#{partition.id}. Error code: #{topic_result.fetch(:rc)}"
|
95
158
|
end
|
96
159
|
|
160
|
+
offsets, threads, mutex = {}, [], Mutex.new
|
97
161
|
topic_result.fetch(:children).each do |topic_name|
|
98
162
|
threads << Thread.new do
|
99
163
|
Thread.abort_on_exception = true
|
@@ -200,6 +264,14 @@ module Kazoo
|
|
200
264
|
end
|
201
265
|
end
|
202
266
|
|
267
|
+
def created_at
|
268
|
+
result = cluster.zk.stat(path: "/consumers/#{group.name}/ids/#{id}")
|
269
|
+
raise Kazoo::Error, "Failed to get instance details. Error code: #{result.fetch(:rc)}" if result.fetch(:rc) != Zookeeper::Constants::ZOK
|
270
|
+
|
271
|
+
Time.at(result.fetch(:stat).mtime / 1000.0)
|
272
|
+
end
|
273
|
+
|
274
|
+
|
203
275
|
def deregister
|
204
276
|
cluster.zk.delete(path: "/consumers/#{group.name}/ids/#{id}")
|
205
277
|
end
|
data/lib/kazoo/partition.rb
CHANGED
data/lib/kazoo/topic.rb
CHANGED
@@ -4,11 +4,10 @@ module Kazoo
|
|
4
4
|
BLACKLISTED_TOPIC_NAMES = %r{\A\.\.?\z}
|
5
5
|
|
6
6
|
attr_reader :cluster, :name
|
7
|
-
|
7
|
+
attr_writer :partitions
|
8
8
|
|
9
9
|
def initialize(cluster, name)
|
10
10
|
@cluster, @name = cluster, name
|
11
|
-
@partitions = []
|
12
11
|
end
|
13
12
|
|
14
13
|
def self.from_json(cluster, name, json)
|
@@ -20,6 +19,18 @@ module Kazoo
|
|
20
19
|
return topic
|
21
20
|
end
|
22
21
|
|
22
|
+
def partitions
|
23
|
+
@partitions ||= begin
|
24
|
+
result = cluster.zk.get(path: "/brokers/topics/#{name}")
|
25
|
+
raise Kazoo::Error, "Failed to get list of partitions for #{name}. Result code: #{result.fetch(:rc)}" if result.fetch(:rc) != Zookeeper::Constants::ZOK
|
26
|
+
|
27
|
+
partition_json = JSON.parse(result.fetch(:data))
|
28
|
+
partition_json.fetch('partitions').map do |(id, replicas)|
|
29
|
+
partition(id.to_i, replicas: replicas.map { |b| cluster.brokers[b] })
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
23
34
|
def partition(*args)
|
24
35
|
Kazoo::Partition.new(self, *args)
|
25
36
|
end
|
data/lib/kazoo/version.rb
CHANGED
@@ -6,9 +6,13 @@ class FunctionalConsumergroupTest < Minitest::Test
|
|
6
6
|
@cluster = Kazoo.connect(zookeeper)
|
7
7
|
@cg = Kazoo::Consumergroup.new(@cluster, 'test.kazoo')
|
8
8
|
@cg.create
|
9
|
+
|
10
|
+
@topic = @cluster.create_topic("test.4", partitions: 4, replication_factor: 1)
|
9
11
|
end
|
10
12
|
|
11
13
|
def teardown
|
14
|
+
@topic.destroy
|
15
|
+
|
12
16
|
cg = Kazoo::Consumergroup.new(@cluster, 'test.kazoo')
|
13
17
|
cg.destroy if cg.exists?
|
14
18
|
|
@@ -40,6 +44,31 @@ class FunctionalConsumergroupTest < Minitest::Test
|
|
40
44
|
assert_nil @cg.retrieve_offset(partition)
|
41
45
|
end
|
42
46
|
|
47
|
+
def test_unclaimed_partitions
|
48
|
+
partition40 = @topic.partition(0)
|
49
|
+
partition41 = @topic.partition(1)
|
50
|
+
partition42 = @topic.partition(2)
|
51
|
+
partition43 = @topic.partition(3)
|
52
|
+
|
53
|
+
refute @cg.active?
|
54
|
+
|
55
|
+
instance1 = @cg.instantiate
|
56
|
+
instance1.register([@topic])
|
57
|
+
instance2 = @cg.instantiate
|
58
|
+
instance2.register([@topic])
|
59
|
+
|
60
|
+
assert @cg.active?
|
61
|
+
|
62
|
+
instance1.claim_partition(partition40)
|
63
|
+
instance2.claim_partition(partition41)
|
64
|
+
|
65
|
+
assert_equal 2, @cg.partition_claims.length
|
66
|
+
assert_equal [@topic], @cg.topics
|
67
|
+
assert_equal @topic.partitions, @cg.partitions
|
68
|
+
|
69
|
+
assert_equal Set[partition42, partition43], Set.new(@cg.unclaimed_partitions)
|
70
|
+
end
|
71
|
+
|
43
72
|
def test_retrieve_all_offsets
|
44
73
|
topic1 = Kazoo::Topic.new(@cluster, 'test.1')
|
45
74
|
partition10 = Kazoo::Partition.new(topic1, 0)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kazoo-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Willem van Bergen
|
@@ -114,6 +114,10 @@ files:
|
|
114
114
|
- lib/kazoo.rb
|
115
115
|
- lib/kazoo/broker.rb
|
116
116
|
- lib/kazoo/cli.rb
|
117
|
+
- lib/kazoo/cli/cluster.rb
|
118
|
+
- lib/kazoo/cli/common.rb
|
119
|
+
- lib/kazoo/cli/consumers.rb
|
120
|
+
- lib/kazoo/cli/topics.rb
|
117
121
|
- lib/kazoo/cluster.rb
|
118
122
|
- lib/kazoo/consumergroup.rb
|
119
123
|
- lib/kazoo/partition.rb
|