krakow 0.2.2 → 0.3.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.
Files changed (44) hide show
  1. data/CHANGELOG.md +16 -0
  2. data/CONTRIBUTING.md +25 -0
  3. data/LICENSE +13 -0
  4. data/README.md +62 -9
  5. data/krakow.gemspec +3 -1
  6. data/lib/krakow/command/cls.rb +3 -4
  7. data/lib/krakow/command/fin.rb +13 -4
  8. data/lib/krakow/command/identify.rb +22 -9
  9. data/lib/krakow/command/mpub.rb +14 -4
  10. data/lib/krakow/command/nop.rb +3 -4
  11. data/lib/krakow/command/pub.rb +15 -5
  12. data/lib/krakow/command/rdy.rb +13 -4
  13. data/lib/krakow/command/req.rb +14 -4
  14. data/lib/krakow/command/sub.rb +14 -4
  15. data/lib/krakow/command/touch.rb +13 -4
  16. data/lib/krakow/command.rb +25 -3
  17. data/lib/krakow/connection.rb +286 -60
  18. data/lib/krakow/connection_features/deflate.rb +26 -1
  19. data/lib/krakow/connection_features/snappy_frames.rb +34 -3
  20. data/lib/krakow/connection_features/ssl.rb +43 -1
  21. data/lib/krakow/connection_features.rb +1 -0
  22. data/lib/krakow/consumer.rb +162 -49
  23. data/lib/krakow/discovery.rb +17 -6
  24. data/lib/krakow/distribution/default.rb +61 -33
  25. data/lib/krakow/distribution.rb +107 -57
  26. data/lib/krakow/exceptions.rb +14 -0
  27. data/lib/krakow/frame_type/error.rb +13 -7
  28. data/lib/krakow/frame_type/message.rb +47 -4
  29. data/lib/krakow/frame_type/response.rb +14 -4
  30. data/lib/krakow/frame_type.rb +20 -8
  31. data/lib/krakow/producer/http.rb +95 -6
  32. data/lib/krakow/producer.rb +60 -17
  33. data/lib/krakow/utils/lazy.rb +99 -40
  34. data/lib/krakow/utils/logging.rb +11 -0
  35. data/lib/krakow/utils.rb +3 -0
  36. data/lib/krakow/version.rb +3 -1
  37. data/lib/krakow.rb +1 -0
  38. metadata +11 -11
  39. data/Gemfile +0 -5
  40. data/Gemfile.lock +0 -34
  41. data/test/spec.rb +0 -81
  42. data/test/specs/consumer.rb +0 -49
  43. data/test/specs/http_producer.rb +0 -123
  44. data/test/specs/producer.rb +0 -20
data/CHANGELOG.md CHANGED
@@ -1,3 +1,19 @@
1
+ ## v0.3.0
2
+ * Include jitter to discovery interval lookups
3
+ * Typecast to String on PUB and MPUB
4
+ * Update exception types used for not implemented methods
5
+ * Add #confirm, #requeue, and #touch helpers to FrameType::Message instances
6
+ * Update Utils::Lazy implementation to be faster and clearer
7
+ * Add #safe_socket method on Connection to add stability
8
+ * Rebuild connections on error to prevent consumer teardown
9
+ * Reference connections without requirement of connection instance being alive
10
+ * Use #read over #recv on underlying socket to ensure proper number of bytes (thanks @thomas-holmes)
11
+ * Expand spec testing
12
+
13
+ A big thanks to @bschwartz for a large contribution in this changeset
14
+ including expanded spec coverage, message proxy helper methods, and
15
+ isolation of instability around Connection interactions.
16
+
1
17
  ## v0.2.2
2
18
  * Fix `nsqlookupd` attribute in `Consumer` and `Discovery`
3
19
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,25 @@
1
+ # Contributing
2
+
3
+ ## Branches
4
+
5
+ ### `master` branch
6
+
7
+ The master branch is the current stable released version.
8
+
9
+ ### `develop` branch
10
+
11
+ The develop branch is the current edge of development.
12
+
13
+ ## Pull requests
14
+
15
+ * https://github.com/chrisroberts/krakow/pulls
16
+
17
+ Please base all pull requests of the `develop` branch. Merges to
18
+ `master` only occur through the `develop` branch. Pull requests
19
+ based on `master` will likely be cherry picked.
20
+
21
+ ## Issues
22
+
23
+ Need to report an issue? Use the github issues:
24
+
25
+ * https://github.com/chrisroberts/krakow/issues
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2014 Chris Roberts
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
data/README.md CHANGED
@@ -86,7 +86,8 @@ Krakow::Utils::Logging.level = :warn # :debug / :info / :warn / :error / :fatal
86
86
 
87
87
  ### Why is it forcing something called an "unready state"?
88
88
 
89
- Because forcing starvation is mean.
89
+ Because forcing starvation is mean. We don't want to be mean, so we'll ensure we
90
+ are consuming from all registered connections.
90
91
 
91
92
  ### I just want to connect to a producer, not a lookup service
92
93
 
@@ -133,8 +134,10 @@ consumer = Krakow::Consumer.new(
133
134
  :nsqlookupd => 'http://HOST:PORT',
134
135
  :topic => 'target',
135
136
  :channel => 'ship',
136
- :connection_features => {
137
- :tls_v1 => true
137
+ :connection_options => {
138
+ :features => {
139
+ :tls_v1 => true
140
+ }
138
141
  }
139
142
  )
140
143
  ```
@@ -148,8 +151,10 @@ consumer = Krakow::Consumer.new(
148
151
  :nsqlookupd => 'http://HOST:PORT',
149
152
  :topic => 'target',
150
153
  :channel => 'ship',
151
- :connection_features => {
152
- :snappy => true
154
+ :connection_options => {
155
+ :features => {
156
+ :snappy => true
157
+ }
153
158
  }
154
159
  )
155
160
  ```
@@ -163,23 +168,71 @@ consumer = Krakow::Consumer.new(
163
168
  :nsqlookupd => 'http://HOST:PORT',
164
169
  :topic => 'target',
165
170
  :channel => 'ship',
166
- :connection_features => {
167
- :deflate => true
171
+ :connection_options => {
172
+ :features => {
173
+ :deflate => true
174
+ }
168
175
  }
169
176
  )
170
177
  ```
171
178
 
179
+ ### I want to use TLS based authentication!
180
+
181
+ OK!
182
+
183
+ ```ruby
184
+ consumer = Krakow::Consumer.new(
185
+ :nsqlookupd => 'http://HOST:PORT',
186
+ :topic => 'target',
187
+ :channel => 'ship',
188
+ :connection_options => {
189
+ :features => {
190
+ :tls_v1 => true
191
+ },
192
+ :config => {
193
+ :ssl_context => {
194
+ :certificate => '/path/to/cert',
195
+ :key => '/path/to/key'
196
+ }
197
+ }
198
+ }
199
+ )
200
+ ```
201
+
202
+ ### Running the tests
203
+
204
+ Run them all!
205
+
206
+ ```
207
+ bundle exec ruby test/run.rb
208
+ ```
209
+
210
+ Or, run part of them:
211
+
212
+ ```
213
+ bundle exec ruby test/specs/consumer_spec.rb
214
+ ```
215
+
172
216
  ### It doesn't work
173
217
 
174
- Create an issue on the github repository.
218
+ Create an issue on the github repository
219
+ * https://github.com/chrisroberts/krakow/issues
175
220
 
176
221
  ### It doesn't do `x`
177
222
 
178
- Create an issue, or even better, send a PR. Just base it off the `develop` branch.
223
+ Create an issue, or even better, send a PR.
224
+ * https://github.com/chrisroberts/krakow/pulls
179
225
 
180
226
  # Info
181
227
  * Repo: https://github.com/chrisroberts/krakow
228
+ * Docs: http://code.chrisroberts.org/krakow
182
229
  * IRC: Freenode @ spox
183
230
 
184
231
  [1]: http://bitly.github.io/nsq/ "NSQ: a realtime distributed messaging platform"
185
232
  [2]: http://celluloid.io "Celluloid: Actor-based concurrent object framework for Ruby"
233
+
234
+ # Contributors
235
+
236
+ * Brendan Schwartz (@bschwartz)
237
+ * Thomas Holmes (@thomas-holmes)
238
+ * Jeremy Hinegardner (@copiousfreetime)
data/krakow.gemspec CHANGED
@@ -8,11 +8,13 @@ Gem::Specification.new do |s|
8
8
  s.email = 'code@chrisroberts.org'
9
9
  s.homepage = 'http://github.com/chrisroberts/krakow'
10
10
  s.description = 'NSQ ruby library'
11
+ s.license = 'Apache 2.0'
11
12
  s.require_path = 'lib'
12
13
  s.add_dependency 'celluloid-io'
13
14
  s.add_dependency 'http'
14
15
  s.add_dependency 'multi_json'
15
16
  s.add_dependency 'snappy'
16
17
  s.add_dependency 'digest-crc'
17
- s.files = Dir['**/*']
18
+ s.files = Dir['lib/**/*'] + %w(krakow.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
19
+ s.extra_rdoc_files = %w(CHANGELOG.md CONTRIBUTING.md LICENSE)
18
20
  end
@@ -1,11 +1,10 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Close connection
3
6
  class Cls < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- end
8
-
9
8
  def to_line
10
9
  "#{name}\n"
11
10
  end
@@ -1,11 +1,20 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Finish a message
3
6
  class Fin < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :message_id
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :message_id, String, :required => true
16
+
17
+ # @!endgroup
9
18
 
10
19
  def to_line
11
20
  "#{name} #{message_id}\n"
@@ -1,18 +1,31 @@
1
1
  require 'multi_json'
2
+ require 'krakow'
2
3
 
3
4
  module Krakow
4
5
  class Command
6
+ # Update client metadata on server / negotiate features
5
7
  class Identify < Command
6
8
 
7
- def initialize(args={})
8
- super
9
- required! :short_id, :long_id
10
- optional(
11
- :feature_negotiation, :heartbeat_interval, :output_buffer_size,
12
- :output_buffer_timeout, :tls_v1, :snappy, :deflate, :deflate_level,
13
- :sample_rate
14
- )
15
- end
9
+ # @!group Attributes
10
+
11
+ # @!macro [attach] attribute
12
+ # @!method $1
13
+ # @return [$2] the $1 $0
14
+ # @!method $1?
15
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
16
+ attribute :short_id, [String, Numeric], :required => true
17
+ attribute :long_id, [String, Numeric], :required => true
18
+ attribute :feature_negotiation, [TrueClass, FalseClass]
19
+ attribute :heartbeat_interval, Numeric
20
+ attribute :output_buffer_size, Integer
21
+ attribute :output_buffer_timeout, Integer
22
+ attribute :tls_v1, [TrueClass, FalseClass]
23
+ attribute :snappy, [TrueClass, FalseClass]
24
+ attribute :deflate, [TrueClass, FalseClass]
25
+ attribute :deflate_level, Integer
26
+ attribute :sample_rate, Integer
27
+
28
+ # @!endgroup
16
29
 
17
30
  def to_line
18
31
  filtered = Hash[*
@@ -1,14 +1,24 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Publish multiple messages
3
6
  class Mpub < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :topic_name, :messages
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :topic_name, String, :required => true
16
+ attribute :messages, Array, :required => true
9
17
 
18
+ # @!endgroup
10
19
  def to_line
11
20
  formatted_messages = messages.map do |message|
21
+ message = message.to_s
12
22
  [message.length, message].pack('l>a*')
13
23
  end.join
14
24
  [name, ' ', topic_name, "\n", formatted_messages.length, messages.size, formatted_messages].pack('a*a*a*a*l>l>a*')
@@ -1,11 +1,10 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # No-op
3
6
  class Nop < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- end
8
-
9
8
  def to_line
10
9
  "#{name}\n"
11
10
  end
@@ -1,15 +1,25 @@
1
+ require 'krakow'
1
2
 
2
3
  module Krakow
3
4
  class Command
5
+ # Publish single message
4
6
  class Pub < Command
5
7
 
6
- def initialize(args={})
7
- super
8
- required! :topic_name, :message
9
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :topic_name, String, :required => true
16
+ attribute :message, Object, :required => true
17
+
18
+ # @!endgroup
10
19
 
11
20
  def to_line
12
- [name, ' ', topic_name, "\n", message.length, message].pack('a*a*a*a*l>a*')
21
+ msg = message.to_s
22
+ [name, ' ', topic_name, "\n", msg.length, msg].pack('a*a*a*a*l>a*')
13
23
  end
14
24
 
15
25
  class << self
@@ -1,11 +1,20 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Update RDY state
3
6
  class Rdy < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :count
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :count, Integer, :required => true
16
+
17
+ # @!endgroup
9
18
 
10
19
  def to_line
11
20
  "#{name} #{count}\n"
@@ -1,11 +1,21 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Re-queue a message
3
6
  class Req < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :message_id, :timeout
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :message_id, String, :required => true
16
+ attribute :timeout, Integer, :required => true
17
+
18
+ # @!endgroup
9
19
 
10
20
  def to_line
11
21
  "#{name} #{message_id} #{self.timeout}\n"
@@ -1,11 +1,21 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Subscribe to topic/channel
3
6
  class Sub < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :topic_name, :channel_name
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :topic_name, String, :required => true
16
+ attribute :channel_name, String, :required => true
17
+
18
+ # @!endgroup
9
19
 
10
20
  def to_line
11
21
  "#{name} #{topic_name} #{channel_name}\n"
@@ -1,11 +1,20 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
2
4
  class Command
5
+ # Reset timeout for in-flight message
3
6
  class Touch < Command
4
7
 
5
- def initialize(args={})
6
- super
7
- required! :message_id
8
- end
8
+ # @!group Attributes
9
+
10
+ # @!macro [attach] attribute
11
+ # @!method $1
12
+ # @return [$2] the $1 $0
13
+ # @!method $1?
14
+ # @return [TrueClass, FalseClass] truthiness of the $1 $0
15
+ attribute :message_id, String, :required => true
16
+
17
+ # @!endgroup
9
18
 
10
19
  def to_line
11
20
  "#{name} #{message_id}\n"
@@ -1,20 +1,33 @@
1
+ require 'krakow'
2
+
1
3
  module Krakow
4
+ # Messages for sending to remote server
2
5
  class Command
3
6
 
4
7
  include Utils::Lazy
8
+ # @!parse include Utils::Lazy::InstanceMethods
9
+ # @!parse extend Utils::Lazy::ClassMethods
5
10
 
6
11
  class << self
7
12
 
13
+ # Allowed OK return values
14
+ #
15
+ # @return [Array<String>]
8
16
  def ok
9
17
  []
10
18
  end
11
19
 
20
+ # Allowed ERROR return values
21
+ #
22
+ # @return [Array<String>]
12
23
  def error
13
24
  []
14
25
  end
15
26
 
16
- # message:: Krakow::Message
17
- # Returns type of response expected (:none, :error_only, :required)
27
+ # Response type expected
28
+ #
29
+ # @param message [Krakow::Message] message to check
30
+ # @return [Symbol] response expected (:none, :error_only, :required)
18
31
  def response_for(message)
19
32
  if(message.class.ok.empty?)
20
33
  if(message.class.error.empty?)
@@ -29,23 +42,32 @@ module Krakow
29
42
 
30
43
  end
31
44
 
45
+ # @return [Krakow::FrameType] response to command
32
46
  attr_accessor :response
33
47
 
34
- # Return command name
48
+ # @return [String] name of command
35
49
  def name
36
50
  self.class.name.split('::').last.upcase
37
51
  end
38
52
 
39
53
  # Convert to line output
54
+ #
55
+ # @return [String] socket ready string
40
56
  def to_line(*args)
41
57
  raise NotImplementedError.new 'No line conversion method defined!'
42
58
  end
43
59
 
60
+ # Is response OK
61
+ #
62
+ # @return [TrueClass, FalseClass]
44
63
  def ok?(response)
45
64
  response = response.content if response.is_a?(FrameType)
46
65
  self.class.ok.include?(response)
47
66
  end
48
67
 
68
+ # Is response ERROR
69
+ #
70
+ # @return [TrueClass, FalseClass]
49
71
  def error?(response)
50
72
  response = response.content if response.is_a?(FrameType)
51
73
  self.class.error.include?(response)