poseidon 0.0.1

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.
Files changed (68) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +12 -0
  4. data/.yardopts +8 -0
  5. data/Gemfile +13 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +71 -0
  8. data/Rakefile +17 -0
  9. data/TODO.md +27 -0
  10. data/examples/consumer.rb +18 -0
  11. data/examples/producer.rb +9 -0
  12. data/lib/poseidon/broker_pool.rb +72 -0
  13. data/lib/poseidon/cluster_metadata.rb +63 -0
  14. data/lib/poseidon/compressed_value.rb +23 -0
  15. data/lib/poseidon/compression/gzip_codec.rb +23 -0
  16. data/lib/poseidon/compression/snappy_codec.rb +17 -0
  17. data/lib/poseidon/compression.rb +30 -0
  18. data/lib/poseidon/connection.rb +138 -0
  19. data/lib/poseidon/fetched_message.rb +37 -0
  20. data/lib/poseidon/message.rb +151 -0
  21. data/lib/poseidon/message_conductor.rb +84 -0
  22. data/lib/poseidon/message_set.rb +80 -0
  23. data/lib/poseidon/message_to_send.rb +33 -0
  24. data/lib/poseidon/messages_for_broker.rb +39 -0
  25. data/lib/poseidon/messages_to_send.rb +47 -0
  26. data/lib/poseidon/messages_to_send_batch.rb +27 -0
  27. data/lib/poseidon/partition_consumer.rb +154 -0
  28. data/lib/poseidon/producer.rb +193 -0
  29. data/lib/poseidon/producer_compression_config.rb +36 -0
  30. data/lib/poseidon/protocol/protocol_struct.rb +238 -0
  31. data/lib/poseidon/protocol/request_buffer.rb +78 -0
  32. data/lib/poseidon/protocol/response_buffer.rb +72 -0
  33. data/lib/poseidon/protocol.rb +122 -0
  34. data/lib/poseidon/sync_producer.rb +117 -0
  35. data/lib/poseidon/topic_metadata.rb +65 -0
  36. data/lib/poseidon/version.rb +4 -0
  37. data/lib/poseidon.rb +102 -0
  38. data/poseidon.gemspec +24 -0
  39. data/spec/bin/kafka-run-class.sh +65 -0
  40. data/spec/integration/multiple_brokers/round_robin_spec.rb +39 -0
  41. data/spec/integration/multiple_brokers/spec_helper.rb +34 -0
  42. data/spec/integration/simple/compression_spec.rb +20 -0
  43. data/spec/integration/simple/connection_spec.rb +33 -0
  44. data/spec/integration/simple/multiple_brokers_spec.rb +8 -0
  45. data/spec/integration/simple/simple_producer_and_consumer_spec.rb +97 -0
  46. data/spec/integration/simple/spec_helper.rb +17 -0
  47. data/spec/integration/simple/unavailable_broker_spec.rb +77 -0
  48. data/spec/spec_helper.rb +32 -0
  49. data/spec/test_cluster.rb +205 -0
  50. data/spec/unit/broker_pool_spec.rb +77 -0
  51. data/spec/unit/cluster_metadata_spec.rb +41 -0
  52. data/spec/unit/compression_spec.rb +17 -0
  53. data/spec/unit/connection_spec.rb +4 -0
  54. data/spec/unit/fetched_message_spec.rb +11 -0
  55. data/spec/unit/message_conductor_spec.rb +147 -0
  56. data/spec/unit/message_set_spec.rb +42 -0
  57. data/spec/unit/message_spec.rb +112 -0
  58. data/spec/unit/message_to_send_spec.rb +10 -0
  59. data/spec/unit/messages_for_broker_spec.rb +54 -0
  60. data/spec/unit/messages_to_send_batch_spec.rb +25 -0
  61. data/spec/unit/messages_to_send_spec.rb +63 -0
  62. data/spec/unit/partition_consumer_spec.rb +124 -0
  63. data/spec/unit/producer_compression_config_spec.rb +35 -0
  64. data/spec/unit/producer_spec.rb +45 -0
  65. data/spec/unit/protocol_spec.rb +54 -0
  66. data/spec/unit/sync_producer_spec.rb +141 -0
  67. data/spec/unit/topic_metadata_spec.rb +17 -0
  68. metadata +206 -0
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+
3
+ include Protocol
4
+ describe MessageConductor do
5
+ context "two avialable partitions" do
6
+ before(:each) do
7
+ partitions = [
8
+ PartitionMetadata.new(nil, 0, 1, [1,2], [1,2]),
9
+ PartitionMetadata.new(nil, 1, 2, [2,1], [2,1])
10
+ ]
11
+ topics = [TopicMetadata.new(TopicMetadataStruct.new(nil, "test", partitions))]
12
+ brokers = [Broker.new(1, "host1", 1), Broker.new(2, "host2", 2)]
13
+
14
+ @mr = MetadataResponse.new(nil, brokers, topics)
15
+
16
+ @cm = ClusterMetadata.new
17
+ @cm.update(@mr)
18
+ end
19
+
20
+ context "no custom partitioner" do
21
+ before(:each) do
22
+ @mc = MessageConductor.new(@cm, nil)
23
+ end
24
+
25
+ context "for unkeyed messages" do
26
+ it "round robins which partition the message should go to" do
27
+ [0,1,0,1].each do |destination|
28
+ expect(@mc.destination("test").first).to eq(destination)
29
+ end
30
+ end
31
+
32
+ context "unknown topic" do
33
+ it "returns -1 for broker and partition" do
34
+ expect(@mc.destination("no_exist")).to eq([-1,-1])
35
+ end
36
+ end
37
+ end
38
+
39
+ context "keyed message" do
40
+ it "sends the same keys to the same destinations" do
41
+ keys = 1000.times.map { rand(500).to_s }
42
+ key_destinations = {}
43
+
44
+ keys.sort_by { rand }.each do |k|
45
+ partition,broker = @mc.destination("test", k)
46
+
47
+ key_destinations[k] ||= []
48
+ key_destinations[k].push([partition,broker])
49
+ end
50
+
51
+ expect(key_destinations.values.all? { |destinations| destinations.uniq.size == 1 }).to eq(true)
52
+ end
53
+ end
54
+ end
55
+
56
+ context "custom partitioner" do
57
+ before(:each) do
58
+ partitioner = Proc.new { |key, count| key.split("_").first.to_i % count }
59
+ @mc = MessageConductor.new(@cm, partitioner)
60
+ end
61
+
62
+ it "obeys custom partitioner" do
63
+ expect(@mc.destination("test", "2_hello").first).to eq(0)
64
+ expect(@mc.destination("test", "3_hello").first).to eq(1)
65
+ end
66
+ end
67
+
68
+ context "broken partitioner" do
69
+ before(:each) do
70
+ partitioner = Proc.new { |key, count| count + 1 }
71
+ @mc = MessageConductor.new(@cm, partitioner)
72
+ end
73
+
74
+ it "raises InvalidPartitionError" do
75
+ expect{@mc.destination("test", "2_hello").first}.to raise_error(Errors::InvalidPartitionError)
76
+ end
77
+ end
78
+ end
79
+
80
+ context "two partitions, one is unavailable" do
81
+ before(:each) do
82
+ partitions = [
83
+ Protocol::PartitionMetadata.new(nil, 0, 1, [1,2], [1,2]),
84
+ Protocol::PartitionMetadata.new(nil, 1, nil, [2,1], [2,1])
85
+ ]
86
+ topics = [TopicMetadata.new(TopicMetadataStruct.new(nil, "test", partitions))]
87
+ brokers = [Broker.new(1, "host1", 1), Broker.new(2, "host2", 2)]
88
+
89
+ @mr = MetadataResponse.new(nil, brokers, topics)
90
+
91
+ @cm = ClusterMetadata.new
92
+ @cm.update(@mr)
93
+
94
+ @mc = MessageConductor.new(@cm, nil)
95
+ end
96
+
97
+ context "keyless message" do
98
+ it "is never sent to an unavailable partition" do
99
+ 10.times do |destination|
100
+ expect(@mc.destination("test").first).to eq(0)
101
+ end
102
+ end
103
+ end
104
+
105
+ context "keyed message" do
106
+ it "is sent to unavailable partition" do
107
+ destinations = Set.new
108
+ 100.times do |key|
109
+ destinations << @mc.destination("test",key.to_s).first
110
+ end
111
+ expect(destinations).to eq(Set.new([0,1]))
112
+ end
113
+ end
114
+ end
115
+
116
+ context "no available partitions" do
117
+ before(:each) do
118
+ partitions = [
119
+ Protocol::PartitionMetadata.new(nil, 0, nil, [1,2], [1,2]),
120
+ Protocol::PartitionMetadata.new(nil, 1, nil, [2,1], [2,1])
121
+ ]
122
+ topics = [TopicMetadata.new(TopicMetadataStruct.new(nil, "test", partitions))]
123
+ brokers = [Broker.new(1, "host1", 1), Broker.new(2, "host2", 2)]
124
+
125
+ @mr = MetadataResponse.new(nil, brokers, topics)
126
+
127
+ @cm = ClusterMetadata.new
128
+ @cm.update(@mr)
129
+
130
+ @mc = MessageConductor.new(@cm, nil)
131
+ end
132
+
133
+ context "keyless message" do
134
+ it "return -1 for broker and partition" do
135
+ expect(@mc.destination("test").first).to eq(-1)
136
+ end
137
+ end
138
+
139
+ context "keyed message" do
140
+ it "returns a valid partition and -1 for broker" do
141
+ partition_id, broker_id = @mc.destination("test", "key")
142
+ expect(partition_id).to_not eq(-1)
143
+ expect(broker_id).to eq(-1)
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe MessageSet do
4
+ describe "converting to a compressed message" do
5
+ before(:each) do
6
+ ms = MessageSet.new([Message.new(:value => "I will be compressed", :topic => "test")])
7
+
8
+ @compressed_message_set = ms.compress(Compression::GzipCodec)
9
+ end
10
+
11
+ it "contains a compressed message" do
12
+ expect(@compressed_message_set.messages.first.compressed?).to eq(true)
13
+ end
14
+
15
+ it "can be decompressed and reconstituted" do
16
+ expect(@compressed_message_set.flatten.first.value).to eq("I will be compressed")
17
+ end
18
+ end
19
+
20
+ describe "adding messages" do
21
+ it "adds the message to the struct" do
22
+ m = Message.new(:value => "sup", :topic => "topic")
23
+ ms = MessageSet.new
24
+ ms << m
25
+ expect(ms.struct.messages.first).to eq(m)
26
+ end
27
+ end
28
+
29
+ describe "encoding" do
30
+ it "round trips" do
31
+ m = Message.new(:value => "sup", :key => "keyz", :topic => "hello")
32
+ ms = MessageSet.new
33
+ ms << m
34
+
35
+ request_buffer = Protocol::RequestBuffer.new
36
+ ms.write(request_buffer)
37
+
38
+ response_buffer = Protocol::ResponseBuffer.new(request_buffer.to_s)
39
+ expect(MessageSet.read(response_buffer)).to eq(ms)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ describe Message do
4
+ describe "when constructing a new message" do
5
+ it 'raises an ArgumentError on unknown options' do
6
+ expect { Message.new(:cow => "dog") }.to raise_error(ArgumentError)
7
+ end
8
+
9
+ it 'handles options correctly' do
10
+ m = Message.new(:value => "value",
11
+ :key => "key",
12
+ :attributes => 1,
13
+ :topic => "topic")
14
+
15
+ expect(m.value).to eq("value")
16
+ expect(m.key).to eq("key")
17
+ expect(m.compressed?).to eq(true)
18
+ expect(m.topic).to eq("topic")
19
+ end
20
+ end
21
+
22
+ describe "checksum" do
23
+ context "is incorrect" do
24
+ before(:each) do
25
+ m = Message.new(:value => "value",
26
+ :key => "key",
27
+ :topic => "topic")
28
+
29
+ req_buf = Protocol::RequestBuffer.new
30
+ m.write(req_buf)
31
+
32
+ @s = req_buf.to_s
33
+ @s[-1] = "q" # break checksum
34
+ end
35
+
36
+ it "knows it" do
37
+ expect { Message.read(Protocol::ResponseBuffer.new(@s)) }.to raise_error(Errors::ChecksumError)
38
+ end
39
+ end
40
+
41
+ context 'is correct' do
42
+ before(:each) do
43
+ m = Message.new(:value => "value",
44
+ :key => "key",
45
+ :topic => "topic")
46
+
47
+ req_buf = Protocol::RequestBuffer.new
48
+ m.write(req_buf)
49
+
50
+ @s = req_buf.to_s
51
+ end
52
+
53
+ it "raises no error" do
54
+ expect { Message.read(Protocol::ResponseBuffer.new(@s)) }.to_not raise_error
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "truncated message" do
60
+ before(:each) do
61
+ m = Message.new(:value => "value",
62
+ :key => "key",
63
+ :topic => "topic")
64
+
65
+ req_buf = Protocol::RequestBuffer.new
66
+ m.write(req_buf)
67
+
68
+ @s = req_buf.to_s
69
+ end
70
+
71
+ it "reading returns nil" do
72
+ expect(Message.read(Protocol::ResponseBuffer.new(@s[0..-4]))).to eq(nil)
73
+ end
74
+ end
75
+
76
+ context "invalid utf8 string for value" do
77
+ it "builds the payload without error" do
78
+ s = "asdf\xffasdf"
79
+ m = Message.new(:value => s,
80
+ :key => "key",
81
+ :topic => "topic")
82
+
83
+ req_buf = Protocol::RequestBuffer.new
84
+ expect {
85
+ m.write(req_buf)
86
+ }.to_not raise_error
87
+ end
88
+ end
89
+
90
+ context "frozen string for value" do
91
+ it "builds the payload without error" do
92
+ s = "asdffasdf".freeze
93
+ m = Message.new(:value => s,
94
+ :key => "key",
95
+ :topic => "topic")
96
+
97
+ req_buf = Protocol::RequestBuffer.new
98
+ expect {
99
+ m.write(req_buf)
100
+ }.to_not raise_error
101
+ end
102
+ end
103
+
104
+ it "decompresses a compressed value"
105
+
106
+ it "raises an error if you try to decompress an uncompressed value"
107
+
108
+ describe "#write" do
109
+ it 'writes a MessageWithOffsetStruct to the request buffer' do
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe MessageToSend do
4
+ it "provides access to topic,value,key" do
5
+ mts = MessageToSend.new("hello_topic", "Hello World", "key")
6
+ expect(mts.topic).to eq("hello_topic")
7
+ expect(mts.value).to eq("Hello World")
8
+ expect(mts.key).to eq("key")
9
+ end
10
+ end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe MessagesForBroker do
4
+ context "twos message one to broker 0, partition 0, another to partition 1" do
5
+ before(:each) do
6
+ @messages = [ Message.new(:topic => "topic1",:value => "hi0"),
7
+ Message.new(:topic => "topic1",:value => "hi1")]
8
+
9
+ @compression_config = stub('compression_config',
10
+ :compression_codec_for_topic => nil)
11
+
12
+ @mfb = MessagesForBroker.new(0)
13
+ @mfb.add(@messages[0], 0)
14
+ @mfb.add(@messages[1], 1)
15
+ end
16
+
17
+ it "provides the messages" do
18
+ expect(@mfb.messages.to_set).to eq(@messages.to_set)
19
+ end
20
+
21
+ it "is has a broker_id of 0" do
22
+ expect(@mfb.broker_id).to eq(0)
23
+ end
24
+
25
+ it "builds the protocol object correctly" do
26
+ protocol_object = @mfb.build_protocol_objects(@compression_config)
27
+
28
+ messages_for_topics = [
29
+ MessagesForTopic.new("topic1",
30
+ [
31
+ MessagesForPartition.new(0, MessageSet.new([@messages[0]])),
32
+ MessagesForPartition.new(1, MessageSet.new([@messages[1]])),
33
+ ])
34
+ ]
35
+ expect(protocol_object).to eq(messages_for_topics)
36
+ end
37
+
38
+ context "and topic is compressed" do
39
+ it "builds the protocol object correctly" do
40
+ @compression_config.stub!(:compression_codec_for_topic => Compression::GzipCodec)
41
+ protocol_object = @mfb.build_protocol_objects(@compression_config)
42
+
43
+ messages_for_topics = [
44
+ MessagesForTopic.new("topic1",
45
+ [
46
+ MessagesForPartition.new(0, MessageSet.new([@messages[0]]).compress(Compression::GzipCodec)),
47
+ MessagesForPartition.new(1, MessageSet.new([@messages[1]]).compress(Compression::GzipCodec)),
48
+ ])
49
+ ]
50
+ expect(protocol_object).to eq(messages_for_topics)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe MessagesToSendBatch do
4
+ context "messages sent to two different brokers" do
5
+ before(:each) do
6
+ message_conductor = stub('message_conductor')
7
+ message_conductor.stub!(:destination).and_return([0,0],[1,1])
8
+
9
+ @messages = [
10
+ Message.new(:topic => "topic1", :value => "hi"),
11
+ Message.new(:topic => "topic1", :value => "hi")
12
+ ]
13
+ @batch = MessagesToSendBatch.new(@messages, message_conductor)
14
+ end
15
+
16
+ it "returns a couple messages brokers" do
17
+ expect(@batch.messages_for_brokers.size).to eq(2)
18
+ end
19
+
20
+ it "has all messages in the returned message brokers" do
21
+ messages = @batch.messages_for_brokers.map(&:messages).flatten
22
+ expect(messages.to_set).to eq(@messages.to_set)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe MessagesToSend do
4
+ before(:each) do
5
+ @messages = []
6
+ @messages << Message.new(:topic => "test1", :value => "hi")
7
+ @messages << Message.new(:topic => "test2", :value => "hi")
8
+ @messages << Message.new(:topic => "test2", :value => "hi")
9
+
10
+
11
+ @cluster_metadata = stub('cluster_metdata').as_null_object
12
+ @mts = MessagesToSend.new(@messages, @cluster_metadata)
13
+ end
14
+
15
+ describe "needing metadata" do
16
+ it "returns set of topics" do
17
+ expect(@mts.topic_set).to eq(Set.new(["test1","test2"]))
18
+ end
19
+
20
+ it "asks ClusterMetadata about having metadata" do
21
+ @cluster_metadata.stub!(:have_metadata_for_topics?).and_return(true)
22
+
23
+ expect(@mts.needs_metadata?).to eq(false)
24
+ end
25
+ end
26
+
27
+ describe "sending" do
28
+ before(:each) do
29
+ @mfb = stub('mfb', :messages => @messages)
30
+ @messages_for_brokers = [@mfb]
31
+
32
+ @mtsb = stub('messages_to_send_batch').as_null_object
33
+ @mtsb.stub!(:messages_for_brokers).and_return(@messages_for_brokers)
34
+
35
+ MessagesToSendBatch.stub!(:new).and_return(@mtsb)
36
+ end
37
+
38
+ context "is successful" do
39
+ before(:each) do
40
+ @mts.messages_for_brokers(nil).each do |mfb|
41
+ @mts.successfully_sent(mfb)
42
+ end
43
+ end
44
+
45
+ it "successfully sends all" do
46
+ expect(@mts.all_sent?).to eq(true)
47
+ end
48
+ end
49
+
50
+ context "is not successful" do
51
+ before(:each) do
52
+ @mts.messages_for_brokers(nil).each do |mfb|
53
+ end
54
+ end
55
+
56
+ it "does not send all" do
57
+ @mts.messages_for_brokers(nil).each do |mfb|
58
+ end
59
+ expect(@mts.all_sent?).to eq(false)
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,124 @@
1
+ require 'spec_helper'
2
+
3
+ describe PartitionConsumer do
4
+ before(:each) do
5
+ @connection = stub('connection')
6
+ Connection.stub!(:new).and_return(@connection)
7
+
8
+ offset = Protocol::Offset.new(100)
9
+ partition_offsets = [Protocol::PartitionOffset.new(0, 0, [offset])]
10
+ @offset_response = [Protocol::TopicOffsetResponse.new("test_topic", partition_offsets)]
11
+ @connection.stub(:offset).and_return(@offset_response)
12
+ end
13
+
14
+ describe "creation" do
15
+ context "when passed unknown options" do
16
+ it "raises an ArgumentError" do
17
+ expect { PartitionConsumer.new("test_client", "localhost", 9092, "test_topic", 0,-2, :unknown => true) }.to raise_error(ArgumentError)
18
+ end
19
+ end
20
+
21
+ context "when passed an unknown offset" do
22
+ it "raises an ArgumentError" do
23
+ expect { PartitionConsumer.new("test_client", "localhost", 9092, "test_topic", 0,:coolest_offset) }.to raise_error(ArgumentError)
24
+ end
25
+ end
26
+ end
27
+
28
+ describe "next offset" do
29
+ context "when offset is not set" do
30
+ it "resolves offset if it's not set" do
31
+ @connection.should_receive(:offset).and_return(@offset_response)
32
+ pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic",
33
+ 0, -2)
34
+
35
+ pc.next_offset
36
+ end
37
+
38
+ it "returns resolved offset" do
39
+ pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic",
40
+ 0, -2)
41
+ expect(pc.next_offset).to eq(100)
42
+ end
43
+ end
44
+
45
+ context "when offset is set" do
46
+ it "does not resolve it" do
47
+ pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic",
48
+ 0, 200)
49
+ pc.next_offset
50
+ end
51
+ end
52
+
53
+ context "when call returns an error" do
54
+ it "is raised" do
55
+ @offset_response.first.partition_offsets.first.stub!(:error).and_return(2)
56
+ pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic",
57
+ 0, -2)
58
+
59
+ expect { pc.next_offset }.to raise_error(Errors::InvalidMessage)
60
+ end
61
+ end
62
+
63
+ context "when no offset exists" do
64
+ it "sets offset to 0" do
65
+ pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic",
66
+ 0, -2)
67
+
68
+ @offset_response.first.partition_offsets.first.stub!(:offsets).and_return([])
69
+ expect(pc.next_offset).to eq(0)
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "fetching messages" do
75
+ before(:each) do
76
+ message_set = MessageSet.new
77
+ message_set << Message.new(:value => "value", :key => "key", :offset => 90)
78
+ partition_fetch_response = Protocol::PartitionFetchResponse.new(0, 0, 100, message_set)
79
+ topic_fetch_response = Protocol::TopicFetchResponse.new('test_topic',
80
+ [partition_fetch_response])
81
+ @response = Protocol::FetchResponse.new(stub('common'), [topic_fetch_response])
82
+
83
+ @connection.stub(:fetch).and_return(@response)
84
+ @pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic", 0, -2)
85
+ end
86
+
87
+ it "returns FetchedMessage objects" do
88
+ expect(@pc.fetch.first.class).to eq(FetchedMessage)
89
+ end
90
+
91
+ it "uses object defaults" do
92
+ @connection.should_receive(:fetch).with(10_000, 0, anything)
93
+ @pc.fetch
94
+ end
95
+
96
+ context "when options are passed" do
97
+ it "overrides object defaults" do
98
+ @connection.should_receive(:fetch).with(20_000, 0, anything)
99
+ @pc = PartitionConsumer.new("test_client", "localhost", 9092, "test_topic", 0, -2, :max_wait_ms => 20_000)
100
+
101
+ @pc.fetch
102
+ end
103
+ end
104
+
105
+ context "when call returns an error" do
106
+ it "is raised" do
107
+ pfr = @response.topic_fetch_responses.first.partition_fetch_responses.first
108
+ pfr.stub!(:error).and_return(2)
109
+
110
+ expect { @pc.fetch }.to raise_error(Errors::InvalidMessage)
111
+ end
112
+ end
113
+
114
+ it "sets the highwater mark" do
115
+ @pc.fetch
116
+ expect(@pc.highwater_mark).to eq(100)
117
+ end
118
+
119
+ it "sets the latest offset" do
120
+ @pc.fetch
121
+ expect(@pc.next_offset).to eq(91)
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe ProducerCompressionConfig do
4
+ describe "creation" do
5
+ it "raises ArgumentError when codec is unknown" do
6
+ expect { ProducerCompressionConfig.new(:ripple, nil) }.to raise_error(ArgumentError)
7
+ end
8
+ end
9
+
10
+ context "no codec set" do
11
+ it "compresses no topics" do
12
+ pcc = ProducerCompressionConfig.new(nil,nil)
13
+ expect(pcc.compression_codec_for_topic("test")).to eq(false)
14
+ end
15
+ end
16
+
17
+ describe "compression codec no topics specified" do
18
+ it "compresses any topic" do
19
+ pcc = ProducerCompressionConfig.new(:gzip,nil)
20
+ expect(pcc.compression_codec_for_topic("test")).to eq(Compression::GzipCodec)
21
+ end
22
+ end
23
+
24
+ describe "compression codec set, but only compress 'compressed' topic" do
25
+ it "compresses 'compressed' topic" do
26
+ pcc = ProducerCompressionConfig.new(:gzip, ["compressed"])
27
+ expect(pcc.compression_codec_for_topic("compressed")).to eq(Compression::GzipCodec)
28
+ end
29
+
30
+ it "does not compresses 'test' topic" do
31
+ pcc = ProducerCompressionConfig.new(:gzip, ["compressed"])
32
+ expect(pcc.compression_codec_for_topic("test")).to eq(false)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Producer do
4
+ it "requires brokers and client_id" do
5
+ expect { Producer.new }.to raise_error
6
+ end
7
+
8
+ it "raises ArgumentError on unknown arguments" do
9
+ expect { Producer.new([],"client_id", :unknown => true) }.to raise_error(ArgumentError)
10
+ end
11
+
12
+ it "raises ArgumentError unless brokers is an enumerable" do
13
+ expect { Producer.new("host:port","client_id") }.to raise_error(ArgumentError)
14
+ end
15
+
16
+ it "raises ProducerShutdown if we try to send to a shutdown producer" do
17
+ p = Producer.new(["host:port"],"client_id")
18
+ p.shutdown
19
+ expect { p.send_messages([]) }.to raise_error(Errors::ProducerShutdownError)
20
+ end
21
+
22
+ it "accepts all options" do
23
+ expect { Producer.new([],"client_id", Producer::OPTION_DEFAULTS.dup) }.not_to raise_error
24
+ end
25
+
26
+ describe "sending messages" do
27
+ before(:each) do
28
+ @sync_producer = double('sync_producer').as_null_object
29
+ SyncProducer.stub!(:new).and_return(@sync_producer)
30
+
31
+ @producer = Producer.new([], "client_id", :type => :sync)
32
+ end
33
+
34
+ it "turns MessagesToSend into Message objects" do
35
+ @sync_producer.should_receive(:send_messages).with([an_instance_of(Message)])
36
+
37
+ m = MessageToSend.new("topic", "value")
38
+ @producer.send_messages([m])
39
+ end
40
+
41
+ it "raises an ArgumentError if you try to send a single message" do
42
+ expect { @producer.send_messages(MessageToSend.new("topic", "value")) }.to raise_error(ArgumentError)
43
+ end
44
+ end
45
+ end