poseidon 0.0.1

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