poseidon 0.0.5.pre1 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -3
- data/CHANGES.md +11 -3
- data/README.md +4 -1
- data/Rakefile +1 -1
- data/lib/poseidon.rb +1 -0
- data/lib/poseidon/broker_pool.rb +15 -5
- data/lib/poseidon/compression/gzip_codec.rb +3 -3
- data/lib/poseidon/compression/snappy_codec.rb +14 -2
- data/lib/poseidon/connection.rb +9 -0
- data/lib/poseidon/partition_consumer.rb +5 -5
- data/lib/poseidon/producer.rb +9 -6
- data/lib/poseidon/producer_compression_config.rb +5 -4
- data/lib/poseidon/protocol/request_buffer.rb +5 -14
- data/lib/poseidon/protocol/response_buffer.rb +7 -7
- data/lib/poseidon/sync_producer.rb +5 -3
- data/lib/poseidon/version.rb +1 -1
- data/poseidon.gemspec +2 -1
- data/spec/integration/multiple_brokers/consumer_spec.rb +1 -1
- data/spec/integration/multiple_brokers/metadata_failures_spec.rb +1 -1
- data/spec/integration/multiple_brokers/rebalance_spec.rb +1 -1
- data/spec/integration/multiple_brokers/round_robin_spec.rb +1 -1
- data/spec/integration/simple/compression_spec.rb +2 -2
- data/spec/integration/simple/connection_spec.rb +4 -4
- data/spec/integration/simple/multiple_brokers_spec.rb +1 -1
- data/spec/integration/simple/simple_producer_and_consumer_spec.rb +7 -7
- data/spec/integration/simple/truncated_messages_spec.rb +4 -4
- data/spec/integration/simple/unavailable_broker_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -7
- data/spec/unit/broker_pool_spec.rb +14 -14
- data/spec/unit/cluster_metadata_spec.rb +1 -1
- data/spec/unit/compression/gzip_codec_spec.rb +34 -0
- data/spec/unit/compression/snappy_codec_spec.rb +49 -0
- data/spec/unit/compression_spec.rb +1 -1
- data/spec/unit/connection_spec.rb +1 -1
- data/spec/unit/fetched_message_spec.rb +1 -1
- data/spec/unit/message_conductor_spec.rb +1 -1
- data/spec/unit/message_set_spec.rb +1 -1
- data/spec/unit/message_spec.rb +2 -2
- data/spec/unit/message_to_send_spec.rb +1 -1
- data/spec/unit/messages_for_broker_spec.rb +3 -3
- data/spec/unit/messages_to_send_batch_spec.rb +4 -4
- data/spec/unit/messages_to_send_spec.rb +8 -8
- data/spec/unit/partition_consumer_spec.rb +13 -13
- data/spec/unit/producer_compression_config_spec.rb +8 -1
- data/spec/unit/producer_spec.rb +10 -4
- data/spec/unit/protocol/request_buffer_spec.rb +16 -0
- data/spec/unit/protocol_spec.rb +5 -5
- data/spec/unit/sync_producer_spec.rb +22 -22
- data/spec/unit/topic_metadata_spec.rb +1 -1
- metadata +28 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9abab19842081422bfcb903cf47a9dd248202bf
|
4
|
+
data.tar.gz: 94a4d93957d331fd9b1c78e8f32f0eab323135ba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a1590c28e36139583f9fdd6558e7cc2b3497806909c2811fd68184cc5c8b5d3bdfca1c87746901d68cf7531b8d0641490dcb4871e1b583afdf30b8e2dc6084fb
|
7
|
+
data.tar.gz: 1b3f297c8c75ebdcc5bd2560eb56d6b2f8e390a4ffaf6ee3277ace90e8b10b3c77f9abc929e658dddbd60d5dcf2a94f91a9ae418f2e2f84ad02bc2f220d41653
|
data/.travis.yml
CHANGED
data/CHANGES.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
|
-
# 0.0.5
|
2
|
-
|
3
|
-
*
|
1
|
+
# 0.0.5
|
2
|
+
|
3
|
+
* Add support for negative offsets. [GH-24]
|
4
|
+
* Fix serious bug where we would send messages to the wrong partition. [GH-36] (Thanks @sclasen and @jorgeortiz85 for tracking this down.)
|
5
|
+
* Better error message when we can't connect to a broker. [GH-42]
|
6
|
+
* Handle broker rebalances. [GH-43]
|
7
|
+
* PartitionConsumer: Block for messages by default. [GH-48]
|
8
|
+
* Add a logger to help debug issues. [GH-51]
|
9
|
+
* Add snappy support. [GH-57]
|
10
|
+
* Allow `:none` value for `:compression_codec` option. [GH-72]
|
11
|
+
* Allow request buffer to accept mixed encodings. [GH-74]
|
4
12
|
|
5
13
|
# 0.0.4
|
6
14
|
|
data/README.md
CHANGED
@@ -30,7 +30,6 @@ producer.send_messages(messages)
|
|
30
30
|
|
31
31
|
More detailed [Poseidon::Producer](http://rubydoc.info/github/bpot/poseidon/Poseidon/Producer) documentation.
|
32
32
|
|
33
|
-
|
34
33
|
### Fetching messages from Kafka
|
35
34
|
|
36
35
|
```ruby
|
@@ -49,6 +48,10 @@ end
|
|
49
48
|
|
50
49
|
More detailed [Poseidon::PartitionConsumer](http://rubydoc.info/github/bpot/poseidon/Poseidon/PartitionConsumer) documentation.
|
51
50
|
|
51
|
+
### Using snappy compression
|
52
|
+
|
53
|
+
To use snappy compression in your producers or consumers, install the [snappy](http://rubygems.org/gems/snappy) gem or simply add `gem 'snappy'` to your project's Gemfile.
|
54
|
+
|
52
55
|
## Semantic Versioning
|
53
56
|
|
54
57
|
This gem follows [SemVer](http://semver.org). In particular, the public API should not be considered stable and anything may change without warning until Version 1.0.0. Additionally, for the purposes of the versioning the public API is everything documented in the [public API docs](http://rubydoc.info/github/bpot/poseidon).
|
data/Rakefile
CHANGED
data/lib/poseidon.rb
CHANGED
data/lib/poseidon/broker_pool.rb
CHANGED
@@ -5,6 +5,15 @@ module Poseidon
|
|
5
5
|
class BrokerPool
|
6
6
|
class UnknownBroker < StandardError; end
|
7
7
|
|
8
|
+
# @yieldparam [BrokerPool]
|
9
|
+
def self.open(client_id, seed_brokers, socket_timeout_ms, &block)
|
10
|
+
broker_pool = new(client_id, seed_brokers, socket_timeout_ms)
|
11
|
+
|
12
|
+
yield broker_pool
|
13
|
+
ensure
|
14
|
+
broker_pool.close
|
15
|
+
end
|
16
|
+
|
8
17
|
# @param [String] client_id
|
9
18
|
def initialize(client_id, seed_brokers, socket_timeout_ms)
|
10
19
|
@connections = {}
|
@@ -45,20 +54,21 @@ module Poseidon
|
|
45
54
|
end
|
46
55
|
|
47
56
|
# Closes all open connections to brokers
|
48
|
-
def
|
57
|
+
def close
|
49
58
|
@brokers.values(&:close)
|
50
59
|
@brokers = {}
|
51
60
|
end
|
52
61
|
|
62
|
+
alias_method :shutdown, :close
|
63
|
+
|
53
64
|
private
|
54
65
|
def fetch_metadata_from_broker(broker, topics)
|
55
66
|
host, port = broker.split(":")
|
56
|
-
|
57
|
-
|
67
|
+
Connection.open(host, port, @client_id, @socket_timeout_ms) do |connection|
|
68
|
+
connection.topic_metadata(topics)
|
69
|
+
end
|
58
70
|
rescue Connection::ConnectionFailedError
|
59
71
|
return nil
|
60
|
-
ensure
|
61
|
-
c && c.close
|
62
72
|
end
|
63
73
|
|
64
74
|
def connection(broker_id)
|
@@ -7,8 +7,8 @@ module Poseidon
|
|
7
7
|
|
8
8
|
def self.compress(s)
|
9
9
|
io = StringIO.new
|
10
|
-
io.set_encoding(
|
11
|
-
gz = Zlib::GzipWriter.new io, Zlib::DEFAULT_COMPRESSION, Zlib::DEFAULT_STRATEGY
|
10
|
+
io.set_encoding(Encoding::BINARY)
|
11
|
+
gz = Zlib::GzipWriter.new io, Zlib::DEFAULT_COMPRESSION, Zlib::DEFAULT_STRATEGY
|
12
12
|
gz.write s
|
13
13
|
gz.close
|
14
14
|
io.string
|
@@ -16,7 +16,7 @@ module Poseidon
|
|
16
16
|
|
17
17
|
def self.decompress(s)
|
18
18
|
io = StringIO.new(s)
|
19
|
-
Zlib::GzipReader.new(io
|
19
|
+
Zlib::GzipReader.new(io).read
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -6,12 +6,24 @@ module Poseidon
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.compress(s)
|
9
|
-
|
9
|
+
check!
|
10
|
+
Snappy.deflate(s)
|
10
11
|
end
|
11
12
|
|
12
13
|
def self.decompress(s)
|
13
|
-
|
14
|
+
check!
|
15
|
+
Snappy::Reader.new(StringIO.new(s)).read
|
14
16
|
end
|
17
|
+
|
18
|
+
def self.check!
|
19
|
+
@checked ||= begin
|
20
|
+
require 'snappy'
|
21
|
+
true
|
22
|
+
rescue LoadError
|
23
|
+
raise "Snappy compression is not available, please install the 'snappy' gem"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
15
27
|
end
|
16
28
|
end
|
17
29
|
end
|
data/lib/poseidon/connection.rb
CHANGED
@@ -11,6 +11,15 @@ module Poseidon
|
|
11
11
|
API_VERSION = 0
|
12
12
|
REPLICA_ID = -1 # Replica id is always -1 for non-brokers
|
13
13
|
|
14
|
+
# @yieldparam [Connection]
|
15
|
+
def self.open(host, port, client_id, socket_timeout_ms, &block)
|
16
|
+
connection = new(host, port, client_id, socket_timeout_ms)
|
17
|
+
|
18
|
+
yield connection
|
19
|
+
ensure
|
20
|
+
connection.close
|
21
|
+
end
|
22
|
+
|
14
23
|
attr_reader :host, :port
|
15
24
|
|
16
25
|
# Create a new connection
|
@@ -22,13 +22,13 @@ module Poseidon
|
|
22
22
|
# this is a stop-gap.
|
23
23
|
#
|
24
24
|
def self.consumer_for_partition(client_id, seed_brokers, topic, partition, offset, options = {})
|
25
|
-
broker_pool = BrokerPool.new(client_id, seed_brokers, options[:socket_timeout_ms] || 10_000)
|
26
25
|
|
27
|
-
|
28
|
-
|
26
|
+
broker = BrokerPool.open(client_id, seed_brokers, options[:socket_timeout_ms] || 10_000) do |broker_pool|
|
27
|
+
cluster_metadata = ClusterMetadata.new
|
28
|
+
cluster_metadata.update(broker_pool.fetch_metadata([topic]))
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
cluster_metadata.lead_broker_for_partition(topic, partition)
|
31
|
+
end
|
32
32
|
|
33
33
|
new(client_id, broker.host, broker.port, topic, partition, offset, options)
|
34
34
|
end
|
data/lib/poseidon/producer.rb
CHANGED
@@ -72,15 +72,16 @@ module Poseidon
|
|
72
72
|
class Producer
|
73
73
|
# @api private
|
74
74
|
VALID_OPTIONS = [
|
75
|
-
:
|
76
|
-
:compression_codec,
|
75
|
+
:ack_timeout_ms,
|
77
76
|
:compressed_topics,
|
77
|
+
:compression_codec,
|
78
|
+
:max_send_retries,
|
78
79
|
:metadata_refresh_interval_ms,
|
79
80
|
:partitioner,
|
80
|
-
:max_send_retries,
|
81
81
|
:retry_backoff_ms,
|
82
82
|
:required_acks,
|
83
|
-
:
|
83
|
+
:socket_timeout_ms,
|
84
|
+
:type,
|
84
85
|
]
|
85
86
|
|
86
87
|
# @api private
|
@@ -163,11 +164,13 @@ module Poseidon
|
|
163
164
|
end
|
164
165
|
|
165
166
|
# Closes all open connections to brokers
|
166
|
-
def
|
167
|
+
def close
|
167
168
|
@shutdown = true
|
168
|
-
@producer.
|
169
|
+
@producer.close
|
169
170
|
end
|
170
171
|
|
172
|
+
alias_method :shutdown, :close
|
173
|
+
|
171
174
|
private
|
172
175
|
def validate_options(options)
|
173
176
|
unknown_keys = options.keys - VALID_OPTIONS
|
@@ -3,15 +3,16 @@ module Poseidon
|
|
3
3
|
class ProducerCompressionConfig
|
4
4
|
COMPRESSION_CODEC_MAP = {
|
5
5
|
:gzip => Compression::GzipCodec,
|
6
|
-
:snappy => Compression::SnappyCodec
|
6
|
+
:snappy => Compression::SnappyCodec,
|
7
|
+
:none => nil
|
7
8
|
}
|
8
9
|
|
9
10
|
def initialize(compression_codec, compressed_topics)
|
10
11
|
if compression_codec
|
11
|
-
|
12
|
-
|
13
|
-
raise ArgumentError, "Uknown compression codec: '#{compression_codec}' (accepted: #{COMPRESSION_CODEC_MAP.keys.inspect})"
|
12
|
+
unless COMPRESSION_CODEC_MAP.has_key?(compression_codec)
|
13
|
+
raise ArgumentError, "Unknown compression codec: '#{compression_codec}' (accepted: #{COMPRESSION_CODEC_MAP.keys.inspect})"
|
14
14
|
end
|
15
|
+
@compression_codec = COMPRESSION_CODEC_MAP[compression_codec]
|
15
16
|
else
|
16
17
|
@compression_codec = nil
|
17
18
|
end
|
@@ -4,13 +4,14 @@ module Poseidon
|
|
4
4
|
#
|
5
5
|
# API parallels the primitive types described on the wiki, with some
|
6
6
|
# sugar for prepending message sizes and checksums.
|
7
|
-
# (https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ProtocolPrimitiveTypes)
|
7
|
+
# (https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ProtocolPrimitiveTypes)
|
8
8
|
class RequestBuffer
|
9
9
|
def initialize
|
10
|
-
@s = ''
|
10
|
+
@s = ''.encode(Encoding::BINARY)
|
11
11
|
end
|
12
12
|
|
13
13
|
def append(string)
|
14
|
+
string = string.dup.force_encoding(Encoding::BINARY)
|
14
15
|
@s << string
|
15
16
|
nil
|
16
17
|
end
|
@@ -32,7 +33,7 @@ module Poseidon
|
|
32
33
|
end
|
33
34
|
|
34
35
|
# Add a string
|
35
|
-
#
|
36
|
+
#
|
36
37
|
# @param [String] string
|
37
38
|
def string(string)
|
38
39
|
if string.nil?
|
@@ -53,33 +54,23 @@ module Poseidon
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def prepend_crc32
|
56
|
-
ensure_ascii
|
57
57
|
checksum_pos = @s.bytesize
|
58
58
|
@s += " "
|
59
59
|
yield
|
60
|
-
ensure_ascii
|
61
60
|
@s[checksum_pos] = [Zlib::crc32(@s[(checksum_pos+1)..-1])].pack("N")
|
62
61
|
nil
|
63
62
|
end
|
64
63
|
|
65
64
|
def prepend_size
|
66
|
-
ensure_ascii
|
67
65
|
size_pos = @s.bytesize
|
68
66
|
@s += " "
|
69
67
|
yield
|
70
|
-
ensure_ascii
|
71
68
|
@s[size_pos] = [(@s.bytesize-1) - size_pos].pack("N")
|
72
69
|
nil
|
73
70
|
end
|
74
71
|
|
75
72
|
def to_s
|
76
|
-
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
-
def ensure_ascii
|
82
|
-
@s.force_encoding("ASCII-8BIT")
|
73
|
+
@s
|
83
74
|
end
|
84
75
|
end
|
85
76
|
end
|
@@ -7,44 +7,44 @@ module Poseidon
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def int8
|
10
|
-
byte = @s.
|
10
|
+
byte = @s.byteslice(@pos, 1).unpack("C").first
|
11
11
|
@pos += 1
|
12
12
|
byte
|
13
13
|
end
|
14
14
|
|
15
15
|
def int16
|
16
|
-
short = @s.
|
16
|
+
short = @s.byteslice(@pos, 2).unpack("s>").first
|
17
17
|
@pos += 2
|
18
18
|
short
|
19
19
|
end
|
20
20
|
|
21
21
|
def int32
|
22
|
-
int = @s.
|
22
|
+
int = @s.byteslice(@pos, 4).unpack("l>").first
|
23
23
|
@pos += 4
|
24
24
|
int
|
25
25
|
end
|
26
26
|
|
27
27
|
def int64
|
28
|
-
long = @s.
|
28
|
+
long = @s.byteslice(@pos, 8).unpack("q>").first
|
29
29
|
@pos += 8
|
30
30
|
long
|
31
31
|
end
|
32
32
|
|
33
33
|
def string
|
34
34
|
len = int16
|
35
|
-
string = @s.
|
35
|
+
string = @s.byteslice(@pos, len)
|
36
36
|
@pos += len
|
37
37
|
string
|
38
38
|
end
|
39
39
|
|
40
40
|
def read(bytes)
|
41
|
-
data = @s.
|
41
|
+
data = @s.byteslice(@pos, bytes)
|
42
42
|
@pos += bytes
|
43
43
|
data
|
44
44
|
end
|
45
45
|
|
46
46
|
def peek(bytes)
|
47
|
-
@s.
|
47
|
+
@s.byteslice(@pos, bytes)
|
48
48
|
end
|
49
49
|
|
50
50
|
def bytes
|
data/lib/poseidon/version.rb
CHANGED
data/poseidon.gemspec
CHANGED
@@ -19,7 +19,8 @@ Gem::Specification.new do |gem|
|
|
19
19
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
20
|
gem.require_paths = ["lib"]
|
21
21
|
|
22
|
-
gem.add_development_dependency(%q<rspec>, '
|
22
|
+
gem.add_development_dependency(%q<rspec>, '>= 3')
|
23
23
|
gem.add_development_dependency(%q<yard>)
|
24
24
|
gem.add_development_dependency(%q<simplecov>)
|
25
|
+
gem.add_development_dependency(%q<snappy>)
|
25
26
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'integration/multiple_brokers/spec_helper'
|
2
2
|
|
3
|
-
describe "consuming with multiple brokers" do
|
3
|
+
RSpec.describe "consuming with multiple brokers", :type => :request do
|
4
4
|
before(:each) do
|
5
5
|
# autocreate the topic by asking for information about it
|
6
6
|
c = Connection.new("localhost", 9092, "metadata_fetcher", 10_000)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'integration/multiple_brokers/spec_helper'
|
2
2
|
|
3
|
-
describe "producer handles rebalancing" do
|
3
|
+
RSpec.describe "producer handles rebalancing", :type => :request do
|
4
4
|
before(:each) do
|
5
5
|
# autocreate the topic by asking for information about it
|
6
6
|
@c = Connection.new("localhost", 9093, "metadata_fetcher", 10_000)
|