promiscuous-poseidon_cluster 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +4 -7
- data/lib/poseidon/cluster.rb +14 -9
- data/lib/poseidon/consumer_group.rb +41 -40
- data/poseidon_cluster.gemspec +3 -4
- data/spec/lib/poseidon/cluster_spec.rb +2 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b88185138e6a8e8de7ce363a550b4630f6e95c8f
|
4
|
+
data.tar.gz: b234eaee3e59a7441458f4506a09fd5c5e929e2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 167be7e9484234a3fd4d282567c1230d1d231463be6ed38bb0adfbddfcc91d198de5af2d636caa0a2b588044afbd9e0c43948a6b10d4ffdd0f581104ac334318
|
7
|
+
data.tar.gz: 3952e0a94d4efe8ef020812ba0dd6b9379b4bdbe530cfbcb61da532a2286eaff5a79c6574fe772e5168cd11605bdc80b9b77a2313a83f49cc263e7bf365d445a
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
poseidon_cluster (0.
|
4
|
+
poseidon_cluster (0.4.0)
|
5
5
|
poseidon (>= 0.0.5.pre1)
|
6
6
|
zk
|
7
7
|
|
@@ -22,7 +22,7 @@ GEM
|
|
22
22
|
multi_json (>= 1.8.4)
|
23
23
|
mime-types (2.3)
|
24
24
|
multi_json (1.10.1)
|
25
|
-
poseidon (0.0.5
|
25
|
+
poseidon (0.0.5)
|
26
26
|
rake (10.3.2)
|
27
27
|
rest-client (1.6.7)
|
28
28
|
mime-types (>= 1.16)
|
@@ -53,13 +53,10 @@ GEM
|
|
53
53
|
thor (0.19.1)
|
54
54
|
tins (1.3.0)
|
55
55
|
yard (0.8.7.4)
|
56
|
-
zk (1.9.
|
56
|
+
zk (1.9.5)
|
57
57
|
logging (~> 1.8.2)
|
58
58
|
zookeeper (~> 1.4.0)
|
59
|
-
zookeeper (1.4.
|
60
|
-
zookeeper (1.4.9-java)
|
61
|
-
slyphon-log4j (= 1.2.15)
|
62
|
-
slyphon-zookeeper_jar (= 3.3.5)
|
59
|
+
zookeeper (1.4.10)
|
63
60
|
|
64
61
|
PLATFORMS
|
65
62
|
java
|
data/lib/poseidon/cluster.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'socket'
|
2
|
-
require 'timeout'
|
3
|
-
require 'zk'
|
4
|
-
require 'poseidon'
|
5
2
|
require 'thread'
|
6
3
|
|
4
|
+
require 'poseidon'
|
5
|
+
require 'zk'
|
6
|
+
|
7
7
|
module Poseidon::Cluster
|
8
8
|
MAX_INT32 = 0x7fffffff
|
9
9
|
@@sem = Mutex.new
|
@@ -12,17 +12,22 @@ module Poseidon::Cluster
|
|
12
12
|
# @return [Integer] an incremented number
|
13
13
|
# @api private
|
14
14
|
def self.inc!
|
15
|
-
@@sem.synchronize
|
15
|
+
@@sem.synchronize do
|
16
|
+
@@inc += 1;
|
17
|
+
|
18
|
+
if @@inc > MAX_INT32
|
19
|
+
@@inc = 1
|
20
|
+
end
|
21
|
+
|
22
|
+
@@inc
|
23
|
+
end
|
16
24
|
end
|
17
25
|
|
18
26
|
# @return [String] an globally unique identifier
|
19
27
|
# @api private
|
20
28
|
def self.guid
|
21
|
-
[::Socket.gethostname, ::Process.pid, ::Time.now.
|
29
|
+
[::Socket.gethostname, ::Process.pid, ::Time.now.nsec, inc!].join('-')
|
22
30
|
end
|
23
|
-
|
24
31
|
end
|
25
32
|
|
26
|
-
|
27
|
-
require "poseidon/#{name}"
|
28
|
-
end
|
33
|
+
require 'poseidon/consumer_group'
|
@@ -21,7 +21,7 @@
|
|
21
21
|
#
|
22
22
|
# @api public
|
23
23
|
class Poseidon::ConsumerGroup
|
24
|
-
DEFAULT_CLAIM_TIMEOUT =
|
24
|
+
DEFAULT_CLAIM_TIMEOUT = 5
|
25
25
|
DEFAULT_LOOP_DELAY = 1
|
26
26
|
|
27
27
|
# Poseidon::ConsumerGroup::Consumer is internally used by Poseidon::ConsumerGroup.
|
@@ -41,7 +41,6 @@ class Poseidon::ConsumerGroup
|
|
41
41
|
options.delete(:trail)
|
42
42
|
super group.id, broker.host, broker.port, group.topic, partition, offset, options
|
43
43
|
end
|
44
|
-
|
45
44
|
end
|
46
45
|
|
47
46
|
# @param [Integer] pnum number of partitions size
|
@@ -97,7 +96,7 @@ class Poseidon::ConsumerGroup
|
|
97
96
|
def initialize(name, brokers, zookeepers, topic, options = {})
|
98
97
|
@name = name
|
99
98
|
@topic = topic
|
100
|
-
@zk = ::ZK.new(zookeepers.join(
|
99
|
+
@zk = ::ZK.new(zookeepers.join(','), {:thread => :per_callback})
|
101
100
|
# Poseidon::BrokerPool doesn't provide default value for this option
|
102
101
|
# Configuring default value like this isn't beautiful, though.. by kssminus
|
103
102
|
options[:socket_timeout_ms] ||= 10000
|
@@ -147,8 +146,8 @@ class Poseidon::ConsumerGroup
|
|
147
146
|
registries.each do |_, path|
|
148
147
|
zk.mkdir_p(path)
|
149
148
|
end
|
150
|
-
zk.create(consumer_path, "{}", ephemeral
|
151
|
-
zk.register(registries[:consumer]) {
|
149
|
+
zk.create(consumer_path, "{}", {:ephemeral => true})
|
150
|
+
zk.register(registries[:consumer]) { rebalance! }
|
152
151
|
|
153
152
|
# Rebalance
|
154
153
|
rebalance!
|
@@ -156,10 +155,12 @@ class Poseidon::ConsumerGroup
|
|
156
155
|
end
|
157
156
|
|
158
157
|
# Reloads metadata/broker/partition information
|
159
|
-
def
|
160
|
-
@metadata = @topic_metadata = nil
|
158
|
+
def reload_metadata
|
159
|
+
@metadata = nil; @topic_metadata = nil
|
161
160
|
metadata
|
162
|
-
|
161
|
+
topic_metadata
|
162
|
+
|
163
|
+
true
|
163
164
|
end
|
164
165
|
|
165
166
|
# Closes the consumer group gracefully, only really useful in tests
|
@@ -178,7 +179,7 @@ class Poseidon::ConsumerGroup
|
|
178
179
|
# @param [Integer] partition
|
179
180
|
# @return [Integer] the latest stored offset for the given partition
|
180
181
|
def offset(partition)
|
181
|
-
data, _ = zk.get
|
182
|
+
data, _ = zk.get(offset_path(partition), {:ignore => :no_node})
|
182
183
|
data.to_i
|
183
184
|
end
|
184
185
|
|
@@ -186,9 +187,9 @@ class Poseidon::ConsumerGroup
|
|
186
187
|
# @param [Integer] partition
|
187
188
|
# @param [Integer] offset
|
188
189
|
def commit(partition, offset)
|
189
|
-
zk.set
|
190
|
+
zk.set(offset_path(partition), offset.to_s)
|
190
191
|
rescue ZK::Exceptions::NoNode
|
191
|
-
zk.create
|
192
|
+
zk.create(offset_path(partition), offset.to_s, {:ignore => :node_exists})
|
192
193
|
end
|
193
194
|
|
194
195
|
# Sorted partitions by broker address (so partitions on the same broker are clustered together)
|
@@ -198,7 +199,7 @@ class Poseidon::ConsumerGroup
|
|
198
199
|
|
199
200
|
topic_metadata.available_partitions.sort_by do |part|
|
200
201
|
broker = metadata.brokers[part.leader]
|
201
|
-
[broker.host, broker.port].join(
|
202
|
+
[broker.host, broker.port].join(':')
|
202
203
|
end
|
203
204
|
end
|
204
205
|
|
@@ -239,6 +240,7 @@ class Poseidon::ConsumerGroup
|
|
239
240
|
unless opts[:commit] == false || commit == false
|
240
241
|
commit consumer.partition, consumer.offset
|
241
242
|
end
|
243
|
+
|
242
244
|
true
|
243
245
|
end
|
244
246
|
|
@@ -345,7 +347,7 @@ class Poseidon::ConsumerGroup
|
|
345
347
|
# Yield over an empty array if nothing claimed,
|
346
348
|
# to allow user to e.g. break out of the loop
|
347
349
|
unless ok
|
348
|
-
yield
|
350
|
+
yield(-1, [])
|
349
351
|
end
|
350
352
|
|
351
353
|
# Sleep if either not claimes or nothing returned
|
@@ -366,28 +368,21 @@ class Poseidon::ConsumerGroup
|
|
366
368
|
# * let POS be our index position in CG and let N = size(PT)/size(CG)
|
367
369
|
# * assign partitions from POS*N to (POS+1)*N-1
|
368
370
|
def rebalance!
|
369
|
-
return if @pending
|
370
|
-
|
371
|
-
@pending = true
|
372
371
|
@mutex.synchronize do
|
373
|
-
@pending = nil
|
374
|
-
|
375
372
|
release_all!
|
376
|
-
|
373
|
+
reload_metadata
|
377
374
|
|
378
|
-
|
379
|
-
|
380
|
-
|
375
|
+
consumer_ids = zk.children(registries[:consumer], {:watch => true}).sort
|
376
|
+
partition_list = partitions
|
377
|
+
responsible_for = self.class.pick(partition_list.size, consumer_ids, id)
|
381
378
|
|
382
|
-
|
383
|
-
if @pending
|
384
|
-
release_all!
|
385
|
-
break
|
386
|
-
end
|
379
|
+
return if responsible_for.nil?
|
387
380
|
|
388
|
-
|
389
|
-
|
390
|
-
|
381
|
+
partition_list[responsible_for].each do |partition|
|
382
|
+
if consumer = claim!(partition.id)
|
383
|
+
@consumers.push(consumer)
|
384
|
+
end
|
385
|
+
end
|
391
386
|
end
|
392
387
|
end
|
393
388
|
|
@@ -395,26 +390,33 @@ class Poseidon::ConsumerGroup
|
|
395
390
|
def release_all!
|
396
391
|
@consumers.each {|c| release!(c.partition) }
|
397
392
|
@consumers.clear
|
393
|
+
|
394
|
+
true
|
398
395
|
end
|
399
396
|
|
400
397
|
private
|
401
398
|
|
402
399
|
# Claim the ownership of the partition for this consumer
|
403
|
-
# @raise [Timeout::Error]
|
404
400
|
def claim!(partition)
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
401
|
+
sleep_duration = 0.1
|
402
|
+
num_tries = ((options[:claim_timout] || DEFAULT_CLAIM_TIMEOUT)/sleep_duration).floor.to_i
|
403
|
+
created = nil
|
404
|
+
|
405
|
+
num_tries.times do
|
406
|
+
created = zk.create(claim_path(partition), id, {:ephemeral => true, :ignore => :node_exists})
|
407
|
+
break if created
|
408
|
+
|
409
|
+
sleep(sleep_duration)
|
411
410
|
end
|
412
|
-
|
411
|
+
|
412
|
+
return nil unless created
|
413
|
+
|
414
|
+
Consumer.new(self, partition, options.dup)
|
413
415
|
end
|
414
416
|
|
415
417
|
# Release ownership of the partition
|
416
418
|
def release!(partition)
|
417
|
-
zk.delete
|
419
|
+
zk.delete(claim_path(partition), {:ignore => :no_node})
|
418
420
|
end
|
419
421
|
|
420
422
|
# @return [String] zookeeper ownership claim path
|
@@ -431,5 +433,4 @@ class Poseidon::ConsumerGroup
|
|
431
433
|
def consumer_path
|
432
434
|
"#{registries[:consumer]}/#{id}"
|
433
435
|
end
|
434
|
-
|
435
436
|
end
|
data/poseidon_cluster.gemspec
CHANGED
@@ -5,10 +5,10 @@ Gem::Specification.new do |s|
|
|
5
5
|
s.name = File.basename(__FILE__, '.gemspec')
|
6
6
|
s.summary = "Poseidon cluster extensions"
|
7
7
|
s.description = "Cluster extensions for Poseidon, a producer and consumer implementation for Kafka >= 0.8"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
|
-
s.authors = ["Black Square Media"]
|
11
|
-
s.email = "
|
10
|
+
s.authors = ["Black Square Media", "Crowdtap Inc."]
|
11
|
+
s.email = "ops@crowdtap.com"
|
12
12
|
s.homepage = "https://github.com/promiscuous-io/poseidon_cluster"
|
13
13
|
|
14
14
|
s.require_path = 'lib'
|
@@ -24,5 +24,4 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.add_development_dependency "rspec-its"
|
25
25
|
s.add_development_dependency "yard"
|
26
26
|
s.add_development_dependency "coveralls"
|
27
|
-
|
28
27
|
end
|
@@ -8,12 +8,12 @@ describe Poseidon::Cluster do
|
|
8
8
|
|
9
9
|
(0...5).map do
|
10
10
|
Thread.new { 100.times { described_class.inc! }}
|
11
|
-
end.each
|
11
|
+
end.each(&:join)
|
12
12
|
(described_class.inc! - num).should == 502
|
13
13
|
end
|
14
14
|
|
15
15
|
it 'should generate GUIDs' do
|
16
|
-
described_class.guid.should match(/\A[\w\-\.]+?\-\d{1,5}\-\d{
|
16
|
+
described_class.guid.should match(/\A[\w\-\.]+?\-\d{1,5}\-\d{9}\-\d{1,3}\z/)
|
17
17
|
end
|
18
18
|
|
19
19
|
end
|
metadata
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: promiscuous-poseidon_cluster
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Black Square Media
|
8
|
+
- Crowdtap Inc.
|
8
9
|
autorequire:
|
9
10
|
bindir: bin
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
+
date: 2015-04-09 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
13
14
|
- !ruby/object:Gem::Dependency
|
14
15
|
name: poseidon
|
@@ -124,7 +125,7 @@ dependencies:
|
|
124
125
|
version: '0'
|
125
126
|
description: Cluster extensions for Poseidon, a producer and consumer implementation
|
126
127
|
for Kafka >= 0.8
|
127
|
-
email:
|
128
|
+
email: ops@crowdtap.com
|
128
129
|
executables: []
|
129
130
|
extensions: []
|
130
131
|
extra_rdoc_files: []
|