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