sorceror_poseidon_cluster 0.4.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 +7 -0
- data/.coveralls.yml +1 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +85 -0
- data/README.md +95 -0
- data/Rakefile +21 -0
- data/examples/consumer_group.rb +33 -0
- data/lib/poseidon/cluster.rb +28 -0
- data/lib/poseidon/consumer_group.rb +467 -0
- data/lib/poseidon_cluster.rb +1 -0
- data/scenario/.gitignore +1 -0
- data/scenario/consumer.rb +17 -0
- data/scenario/producer.rb +23 -0
- data/scenario/run.rb +35 -0
- data/scenario/scenario.rb +134 -0
- data/sorceror_poseidon_cluster.gemspec +27 -0
- data/spec/lib/poseidon/cluster_spec.rb +19 -0
- data/spec/lib/poseidon/consumer_group_spec.rb +313 -0
- data/spec/spec_helper.rb +14 -0
- metadata +184 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.required_ruby_version = '>= 2.0.0'
|
3
|
+
s.required_rubygems_version = ">= 1.8.0"
|
4
|
+
|
5
|
+
s.name = File.basename(__FILE__, '.gemspec')
|
6
|
+
s.summary = "Poseidon cluster extensions"
|
7
|
+
s.description = "Cluster extensions for Poseidon tweaked for Sorceror, a producer and consumer implementation for Kafka >= 0.8"
|
8
|
+
s.version = "0.4.0"
|
9
|
+
|
10
|
+
s.authors = ["Black Square Media", "Kareem Kouddous"]
|
11
|
+
s.email = "kareemknyc@gmail.com"
|
12
|
+
s.homepage = "https://github.com/kemoko/poseidon_cluster"
|
13
|
+
|
14
|
+
s.require_path = 'lib'
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features,scenario}/*`.split("\n")
|
17
|
+
|
18
|
+
s.add_dependency "poseidon", ">= 0.0.5.pre1"
|
19
|
+
s.add_dependency "zk"
|
20
|
+
|
21
|
+
s.add_development_dependency "rake"
|
22
|
+
s.add_development_dependency "bundler"
|
23
|
+
s.add_development_dependency "rspec"
|
24
|
+
s.add_development_dependency "rspec-its"
|
25
|
+
s.add_development_dependency "yard"
|
26
|
+
s.add_development_dependency "coveralls"
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Poseidon::Cluster do
|
4
|
+
|
5
|
+
it 'should generate incremented numbers (atomically)' do
|
6
|
+
num = described_class.inc!
|
7
|
+
(described_class.inc! - num).should == 1
|
8
|
+
|
9
|
+
(0...5).map do
|
10
|
+
Thread.new { 100.times { described_class.inc! }}
|
11
|
+
end.each &:join
|
12
|
+
(described_class.inc! - num).should == 502
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should generate GUIDs' do
|
16
|
+
described_class.guid.should match(/\A[\w\-\.]+?\-\d{1,5}\-\d{10}\-\d{1,3}\z/)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Poseidon::ConsumerGroup do
|
4
|
+
|
5
|
+
def fetch_response(n)
|
6
|
+
set = Poseidon::MessageSet.new
|
7
|
+
n.times {|i| set << Poseidon::Message.new(value: "value", key: "key", offset: i) }
|
8
|
+
pfr = Poseidon::Protocol::PartitionFetchResponse.new(0, 0, 100, set)
|
9
|
+
tfr = Poseidon::Protocol::TopicFetchResponse.new("mytopic", [pfr])
|
10
|
+
Poseidon::Protocol::FetchResponse.new(nil, [tfr])
|
11
|
+
end
|
12
|
+
|
13
|
+
let :brokers do
|
14
|
+
[ Poseidon::Protocol::Broker.new(1, "localhost", 29092), # id,host,port
|
15
|
+
Poseidon::Protocol::Broker.new(2, "localhost", 29091), ]
|
16
|
+
end
|
17
|
+
|
18
|
+
let :partitions do
|
19
|
+
[ Poseidon::Protocol::PartitionMetadata.new(0, 0, 1, [1,2], []), # err,id,leader,replicas,isr
|
20
|
+
Poseidon::Protocol::PartitionMetadata.new(0, 1, 2, [1,2], []), ]
|
21
|
+
end
|
22
|
+
|
23
|
+
let :topics do
|
24
|
+
[ Poseidon::TopicMetadata.new(Poseidon::Protocol::TopicMetadataStruct.new(0, "mytopic", partitions)) ]
|
25
|
+
end
|
26
|
+
|
27
|
+
let :metadata do
|
28
|
+
Poseidon::Protocol::MetadataResponse.new nil, brokers.dup, topics.dup
|
29
|
+
end
|
30
|
+
|
31
|
+
let :zk_client do
|
32
|
+
double "ZK", mkdir_p: nil, get: nil, set: nil, delete: nil, create: "/path", register: nil, close: nil
|
33
|
+
end
|
34
|
+
before do
|
35
|
+
allow(zk_client).to receive(:children).and_return(nil, ["my-group-UNIQUEID"])
|
36
|
+
end
|
37
|
+
|
38
|
+
let(:group) { described_class.new "my-group", ["localhost:29092", "localhost:29091"], ["localhost:22181"], "mytopic" }
|
39
|
+
subject { group }
|
40
|
+
|
41
|
+
before do
|
42
|
+
allow(ZK).to receive_messages(new: zk_client)
|
43
|
+
allow(Poseidon::Cluster).to receive_messages(guid: "UNIQUEID")
|
44
|
+
allow_any_instance_of(Poseidon::ConsumerGroup).to receive(:sleep)
|
45
|
+
allow_any_instance_of(Poseidon::PartitionConsumer).to receive_messages(resolve_offset_if_necessary: 0)
|
46
|
+
allow_any_instance_of(Poseidon::BrokerPool).to receive_messages(fetch_metadata_from_broker: metadata)
|
47
|
+
|
48
|
+
allow_any_instance_of(Poseidon::Connection).to receive(:fetch).with(10000, 1, ->req { req[0].partition_fetches[0].partition == 0 }).and_return(fetch_response(10))
|
49
|
+
allow_any_instance_of(Poseidon::Connection).to receive(:fetch).with(10000, 1, ->req { req[0].partition_fetches[0].partition == 1 }).and_return(fetch_response(5))
|
50
|
+
end
|
51
|
+
|
52
|
+
it { should be_registered }
|
53
|
+
its(:name) { should == "my-group" }
|
54
|
+
its(:topic) { should == "mytopic" }
|
55
|
+
its(:pool) { should be_instance_of(Poseidon::BrokerPool) }
|
56
|
+
its(:id) { should == "my-group-UNIQUEID" }
|
57
|
+
its(:zk) { should be(zk_client) }
|
58
|
+
|
59
|
+
its(:claimed) { should == [0, 1] }
|
60
|
+
its(:metadata) { should be_instance_of(Poseidon::ClusterMetadata) }
|
61
|
+
its(:topic_metadata) { should be_instance_of(Poseidon::TopicMetadata) }
|
62
|
+
its(:registries) { should == {
|
63
|
+
consumer: "/consumers/my-group/ids",
|
64
|
+
owner: "/consumers/my-group/owners/mytopic",
|
65
|
+
offset: "/consumers/my-group/offsets/mytopic",
|
66
|
+
}}
|
67
|
+
|
68
|
+
its("metadata.brokers.keys") { should =~ [1,2] }
|
69
|
+
its("topic_metadata.partition_count") { should == 2 }
|
70
|
+
|
71
|
+
it "should register with zookeeper and rebalance" do
|
72
|
+
zk_client.should_receive(:mkdir_p).with("/consumers/my-group/ids")
|
73
|
+
zk_client.should_receive(:mkdir_p).with("/consumers/my-group/owners/mytopic")
|
74
|
+
zk_client.should_receive(:mkdir_p).with("/consumers/my-group/offsets/mytopic")
|
75
|
+
zk_client.should_receive(:create).with("/consumers/my-group/ids/my-group-UNIQUEID", "{}", ephemeral: true)
|
76
|
+
zk_client.should_receive(:register).with("/consumers/my-group/ids")
|
77
|
+
described_class.any_instance.should_receive :rebalance!
|
78
|
+
|
79
|
+
subject
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should sort partitions by leader address" do
|
83
|
+
subject.partitions.map(&:id).should == [1, 0]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should not fail if topic doesn't exist" do
|
87
|
+
no_topics = Poseidon::Protocol::MetadataResponse.new nil, brokers.dup, []
|
88
|
+
Poseidon::BrokerPool.any_instance.stub(:fetch_metadata_from_broker).and_return(no_topics)
|
89
|
+
|
90
|
+
subject.partitions.should == []
|
91
|
+
subject.claimed.should == []
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should return the offset for each partition" do
|
95
|
+
zk_client.should_receive(:get).with("/consumers/my-group/offsets/mytopic/0", ignore: :no_node).and_return([nil])
|
96
|
+
subject.offset(0).should == 0
|
97
|
+
|
98
|
+
zk_client.should_receive(:get).with("/consumers/my-group/offsets/mytopic/1", ignore: :no_node).and_return(["21", nil])
|
99
|
+
subject.offset(1).should == 21
|
100
|
+
|
101
|
+
zk_client.should_receive(:get).with("/consumers/my-group/offsets/mytopic/2", ignore: :no_node).and_return(["0", nil])
|
102
|
+
subject.offset(2).should == 0
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should return the leader for a partition" do
|
106
|
+
subject.leader(0).should == brokers[0]
|
107
|
+
subject.leader(1).should == brokers[1]
|
108
|
+
subject.leader(2).should be_nil
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should checkout individual partition consumers (atomically)" do
|
112
|
+
subject.checkout {|c| c.partition.should == 1 }.should be_truthy
|
113
|
+
subject.checkout {|c| c.partition.should == 0 }.should be_truthy
|
114
|
+
|
115
|
+
n = 0
|
116
|
+
a = Thread.new do
|
117
|
+
100.times { subject.checkout {|_| n+=1 } }
|
118
|
+
Thread.pass
|
119
|
+
100.times { subject.checkout {|_| n+=1 } }
|
120
|
+
end
|
121
|
+
b = Thread.new do
|
122
|
+
100.times { subject.checkout {|_| n+=1 } }
|
123
|
+
Thread.pass
|
124
|
+
100.times { subject.checkout {|_| n+=1 } }
|
125
|
+
end
|
126
|
+
[a, b].each &:join
|
127
|
+
n.should == 400
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "consumer" do
|
131
|
+
subject { described_class::Consumer.new group, 1 }
|
132
|
+
before { group.stub(:offset).with(1).and_return(432) }
|
133
|
+
|
134
|
+
it { should be_a(Poseidon::PartitionConsumer) }
|
135
|
+
its(:offset) { should == 432 }
|
136
|
+
|
137
|
+
it 'should start with the earliest offset if none stored' do
|
138
|
+
group.unstub(:offset)
|
139
|
+
subject.offset.should == :earliest_offset
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should start with the latest offset if none stored and in trailing mode' do
|
143
|
+
group.unstub(:offset)
|
144
|
+
trailing_consumer = described_class::Consumer.new group, 1, {trail: true}
|
145
|
+
trailing_consumer.offset.should == :latest_offset
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
describe "rebalance" do
|
151
|
+
|
152
|
+
it "should watch out for new consumers joining/leaving" do
|
153
|
+
described_class.any_instance.should_receive(:rebalance!)
|
154
|
+
subject
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should distribute available partitions between consumers" do
|
158
|
+
subject.claimed.should == [0, 1]
|
159
|
+
zk_client.stub children: ["my-group-UNIQUEID", "my-group-OTHERID"]
|
160
|
+
-> { subject.send :rebalance! }.should change { subject.claimed }.to([0])
|
161
|
+
zk_client.stub children: ["my-group-UNIQUEID", "my-group-OTHERID", "my-group-THIRDID"]
|
162
|
+
-> { subject.send :rebalance! }.should change { subject.claimed }.to([])
|
163
|
+
end
|
164
|
+
|
165
|
+
it "should allocate partitions correctly" do
|
166
|
+
subject.claimed.should == [0, 1]
|
167
|
+
|
168
|
+
zk_client.stub children: ["my-group-UNIQUEID", "my-group-ZID"]
|
169
|
+
zk_client.should_receive(:delete).with("/consumers/my-group/owners/mytopic/1", ignore: :no_node)
|
170
|
+
-> { subject.send :rebalance! }.should change { subject.claimed }.to([1])
|
171
|
+
|
172
|
+
zk_client.stub children: ["my-group-UNIQUEID", "my-group-ZID", "my-group-AID"]
|
173
|
+
-> { subject.send :rebalance! }.should change { subject.claimed }.to([0])
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should wait for consumer to commit manually all messages before rebalancing" do
|
177
|
+
consumer = nil
|
178
|
+
subject.checkout(commit: false) { |c| consumer = c }
|
179
|
+
|
180
|
+
t = Thread.new { subject.send :rebalance! }
|
181
|
+
|
182
|
+
sleep 0.1
|
183
|
+
t.alive?.should == true
|
184
|
+
|
185
|
+
payloads = consumer.fetch
|
186
|
+
subject.commit(consumer.partition, payloads.shift.offset + 1)
|
187
|
+
|
188
|
+
sleep 0.1
|
189
|
+
t.alive?.should == true
|
190
|
+
|
191
|
+
payloads.each do |payload|
|
192
|
+
subject.commit(consumer.partition, payload.offset + 1)
|
193
|
+
end
|
194
|
+
|
195
|
+
sleep 0.1
|
196
|
+
t.alive?.should == false
|
197
|
+
|
198
|
+
t.join
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "fetch" do
|
203
|
+
|
204
|
+
it "should return messages from claimed partitions" do
|
205
|
+
subject.fetch do |n, msg|
|
206
|
+
n.should == 1
|
207
|
+
msg.size.should == 5
|
208
|
+
end.should be_truthy
|
209
|
+
|
210
|
+
subject.fetch do |n, msg|
|
211
|
+
n.should == 0
|
212
|
+
msg.size.should == 10
|
213
|
+
end.should be_truthy
|
214
|
+
|
215
|
+
subject.fetch do |n, msg|
|
216
|
+
n.should == 1
|
217
|
+
msg.size.should == 5
|
218
|
+
end.should be_truthy
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should auto-commit fetched offset" do
|
222
|
+
zk_client.should_receive(:set).with("/consumers/my-group/offsets/mytopic/1", "5")
|
223
|
+
subject.fetch {|n, _| n.should == 1 }
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should skip auto-commits if requested" do
|
227
|
+
zk_client.should_not_receive(:set)
|
228
|
+
subject.fetch(commit: false) {|n, _| n.should == 1 }
|
229
|
+
end
|
230
|
+
|
231
|
+
it "should skip auto-commits if block results in false" do
|
232
|
+
zk_client.should_not_receive(:set)
|
233
|
+
subject.fetch {|n, _| n.should == 1; false }
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should return false when trying to fetch messages without a claim" do
|
237
|
+
no_topics = Poseidon::Protocol::MetadataResponse.new nil, brokers.dup, []
|
238
|
+
Poseidon::BrokerPool.any_instance.stub fetch_metadata_from_broker: no_topics
|
239
|
+
|
240
|
+
subject.claimed.should == []
|
241
|
+
subject.fetch {|*| }.should be_falsey
|
242
|
+
end
|
243
|
+
|
244
|
+
it "should return true even when no messages were fetched" do
|
245
|
+
Poseidon::Connection.any_instance.stub fetch: fetch_response(0)
|
246
|
+
subject.fetch {|*| }.should be_truthy
|
247
|
+
end
|
248
|
+
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "fetch_loop" do
|
252
|
+
|
253
|
+
it "should fetch indefinitely" do
|
254
|
+
total, cycles = 0, 0
|
255
|
+
subject.fetch_loop do |_, m|
|
256
|
+
total += m.size
|
257
|
+
break if (cycles+=1) > 2
|
258
|
+
end
|
259
|
+
total.should == 20
|
260
|
+
cycles.should == 3
|
261
|
+
end
|
262
|
+
|
263
|
+
it "should delay fetch was unsuccessful" do
|
264
|
+
subject.stub fetch: false
|
265
|
+
|
266
|
+
cycles = 0
|
267
|
+
subject.should_receive(:sleep).with(1)
|
268
|
+
subject.fetch_loop do |n, m|
|
269
|
+
n.should == -1
|
270
|
+
m.should == []
|
271
|
+
break if (cycles+=1) > 1
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should delay fetch didn't yield any results" do
|
276
|
+
subject.stub(:fetch).and_yield(3, []).and_return(true)
|
277
|
+
|
278
|
+
cycles = 0
|
279
|
+
subject.should_receive(:sleep).with(1)
|
280
|
+
subject.fetch_loop do |n, m|
|
281
|
+
n.should == 3
|
282
|
+
m.should == []
|
283
|
+
break if (cycles+=1) > 1
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
describe "pick" do
|
290
|
+
|
291
|
+
{ [3, ["N1", "N2", "N3"], "N1"] => (0..0),
|
292
|
+
[3, ["N1", "N2", "N3"], "N2"] => (1..1),
|
293
|
+
[3, ["N1", "N2", "N3"], "N3"] => (2..2),
|
294
|
+
[4, ["N2", "N4", "N3", "N1"], "N3"] => (2..2),
|
295
|
+
[3, ["N1", "N2", "N3"], "N4"] => nil,
|
296
|
+
[5, ["N1", "N2", "N3"], "N1"] => (0..1),
|
297
|
+
[5, ["N1", "N2", "N3"], "N2"] => (2..3),
|
298
|
+
[5, ["N1", "N2", "N3"], "N3"] => (4..4),
|
299
|
+
[5, ["N1", "N2", "N3"], "N4"] => nil,
|
300
|
+
[2, ["N1", "N2"], "N9"] => nil,
|
301
|
+
[1, ["N1", "N2", "N3"], "N1"] => (0..0),
|
302
|
+
[1, ["N1", "N2", "N3"], "N2"] => nil,
|
303
|
+
[1, ["N1", "N2", "N3"], "N3"] => nil,
|
304
|
+
[5, ["N1", "N2"], "N1"] => (0..2),
|
305
|
+
[5, ["N1", "N2"], "N2"] => (3..4),
|
306
|
+
}.each do |args, expected|
|
307
|
+
it "should pick #{expected.inspect} from #{args.inspect}" do
|
308
|
+
described_class.pick(*args).should == expected
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
end
|
313
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'poseidon_cluster'
|
2
|
+
require 'rspec'
|
3
|
+
require 'rspec/its'
|
4
|
+
require 'coveralls'
|
5
|
+
Coveralls.wear_merged!
|
6
|
+
|
7
|
+
RSpec.configure do |c|
|
8
|
+
c.expect_with :rspec do |c|
|
9
|
+
c.syntax = [:expect, :should]
|
10
|
+
end
|
11
|
+
c.mock_with :rspec do |c|
|
12
|
+
c.syntax = [:expect, :should]
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sorceror_poseidon_cluster
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Black Square Media
|
8
|
+
- Kareem Kouddous
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2015-05-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: poseidon
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.0.5.pre1
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.0.5.pre1
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: zk
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: bundler
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec-its
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: yard
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: coveralls
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - ">="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: Cluster extensions for Poseidon tweaked for Sorceror, a producer and
|
127
|
+
consumer implementation for Kafka >= 0.8
|
128
|
+
email: kareemknyc@gmail.com
|
129
|
+
executables: []
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files: []
|
132
|
+
files:
|
133
|
+
- ".coveralls.yml"
|
134
|
+
- ".gitignore"
|
135
|
+
- ".travis.yml"
|
136
|
+
- Gemfile
|
137
|
+
- Gemfile.lock
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- examples/consumer_group.rb
|
141
|
+
- lib/poseidon/cluster.rb
|
142
|
+
- lib/poseidon/consumer_group.rb
|
143
|
+
- lib/poseidon_cluster.rb
|
144
|
+
- scenario/.gitignore
|
145
|
+
- scenario/consumer.rb
|
146
|
+
- scenario/producer.rb
|
147
|
+
- scenario/run.rb
|
148
|
+
- scenario/scenario.rb
|
149
|
+
- sorceror_poseidon_cluster.gemspec
|
150
|
+
- spec/lib/poseidon/cluster_spec.rb
|
151
|
+
- spec/lib/poseidon/consumer_group_spec.rb
|
152
|
+
- spec/spec_helper.rb
|
153
|
+
homepage: https://github.com/kemoko/poseidon_cluster
|
154
|
+
licenses: []
|
155
|
+
metadata: {}
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ">="
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: 2.0.0
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ">="
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: 1.8.0
|
170
|
+
requirements: []
|
171
|
+
rubyforge_project:
|
172
|
+
rubygems_version: 2.4.6
|
173
|
+
signing_key:
|
174
|
+
specification_version: 4
|
175
|
+
summary: Poseidon cluster extensions
|
176
|
+
test_files:
|
177
|
+
- scenario/consumer.rb
|
178
|
+
- scenario/producer.rb
|
179
|
+
- scenario/run.rb
|
180
|
+
- scenario/scenario.rb
|
181
|
+
- spec/lib/poseidon/cluster_spec.rb
|
182
|
+
- spec/lib/poseidon/consumer_group_spec.rb
|
183
|
+
- spec/spec_helper.rb
|
184
|
+
has_rdoc:
|