activemessaging-kestrel-adapter 0.0.1 → 0.0.2

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.
@@ -1,3 +1,10 @@
1
+ == 0.0.2 / 2011-02-03
2
+ * Implement simple retry in the face of receive errors to avoid log spew
3
+ by the ActiveMessaging Gateway as it retries over and over. Extensible
4
+ with other retry policies.
5
+ * Works with ActiveMessaging up to version 0.7.1
6
+ * Add some configuration tests
7
+
1
8
  == 0.0.1 / 2011-02-02
2
9
  * First release, actually works in a Rails application.
3
10
 
data/README.md CHANGED
@@ -19,6 +19,13 @@ Examples
19
19
  # development:
20
20
  # adapter: kestrel
21
21
  # servers: localhost:22133
22
+ # retry_policy:
23
+ # strategy: SimpleRetry
24
+ # config:
25
+ # tries: 1
26
+ # delay: 5
27
+ # the retry_policy: second is optional and should be left out under
28
+ # most circumstances (see below).
22
29
  config = YAML.load(File.read("broker.yml"))
23
30
 
24
31
  adapter = ActiveMessaging::Adapter::Kestrel::Connection.new(config[:development])
@@ -28,12 +35,25 @@ Examples
28
35
  adapter.subscribe("queue2")
29
36
  adapter.receive # get a message from any of the subscribed queues, or nil if they are all empty
30
37
 
38
+ Configuration
39
+ -------------
40
+
41
+ See the ActiveMessaging project for details of broker configuration. This
42
+ Kestrel adapter can be configured with a retry_policy, but it is not
43
+ necessary to do so as a sensible default will be used if the configuration
44
+ is not present. The default policy is as shown above. The default
45
+ policy in the face of receive exceptions is to sleep 5 seconds, then retry
46
+ once. If an exception still occurs, it is raised. The strategy must
47
+ refer to a class that once instantiated, responds to the do_work() method.
48
+ The method must take a options hash. The value of the options hash will
49
+ be the config: key in the configuration file. For 99% of use cases, you
50
+ should not even bother defining the retry_policy as the default is fine.
51
+ The ability to override the policy is provided for edge cases.
31
52
 
32
53
  Requirements
33
54
  ------------
34
55
 
35
- * depends on memcached-client
36
- * flesh out the configuration
56
+ * memcached-client
37
57
 
38
58
  Future
39
59
  ------
data/Rakefile CHANGED
@@ -14,9 +14,9 @@ Bones {
14
14
  url 'http://github.org/seifertd/activemessaging-kestrel-adapter'
15
15
  ignore_file '.gitignore'
16
16
  readme_file 'README.md'
17
- version '0.0.1'
18
17
 
19
18
  depend_on 'memcache-client'
19
+ depend_on 'activemessaging'
20
20
 
21
21
  rdoc.include << 'README.md'
22
22
 
@@ -9,22 +9,60 @@ module ActiveMessaging
9
9
  # Simple struct for wrapping received messages
10
10
  Message = Struct.new(:headers, :body, :command)
11
11
 
12
+ # Resolve the provided string into a class object
13
+ def to_class(class_name, initial_scope = Kernel)
14
+ class_name.split('::').inject(initial_scope) { |scope, const_name| scope.const_get(const_name) }
15
+ end
16
+
17
+ # class for retrying things (should be pulled into a separate gem)
18
+ class SimpleRetry
19
+ # Yields to caller's block and retries up to options[:tries] (default 3)
20
+ # times in the face of exceptions. Returns return result of block if
21
+ # successful. If number of tries is exhausted, the exception is reraised.
22
+ # Retry loop will sleep options[:delay] seconds between retries (default 5).
23
+ # If you want error logging, pass in :logger. If not provided, Rails.logger
24
+ # is used if defined.
25
+ def do_work(options = {})
26
+ logger = defined?(::Rails) ? ::Rails.logger : nil
27
+ use_options = {:tries => 3, :delay => 5, :logger => logger}.merge(options || {})
28
+ exception = nil
29
+ return_value = nil
30
+ logger = use_options[:logger]
31
+ use_options[:tries].times do |try|
32
+ begin
33
+ exception = nil
34
+ return_value = yield
35
+ break
36
+ rescue Exception => e
37
+ exception = e
38
+ logger.warn("Got error on try #{try}: #{exception} retrying after #{use_options[:delay]} seconds") if logger
39
+ end
40
+ sleep use_options[:delay]
41
+ end
42
+
43
+ raise exception if exception
44
+ return return_value
45
+ end
46
+ end
47
+
12
48
  # Connection to a kestrel message queue server
13
- class Connection < ActiveMessaging::Adapters::Base::Connection
49
+ class Connection < ActiveMessaging::Adapters::BaseConnection
14
50
  include ActiveMessaging::Adapter
15
51
  register :kestrel
16
52
  attr_accessor :reliable
17
- # Time to sleep between polls of empty queues
18
- attr_accessor :receive_throttle
19
53
  # Reconnect on error
20
- attr_accessor :error_policy
54
+ attr_accessor :retry_policy
21
55
 
22
56
  def initialize(cfg)
23
- @receive_throttle = cfg.delete(:receive_throttle) || 0.1
24
- # TODO: Implement error policies
25
- @error_policy = cfg.delete(:error_policy) || :reconnect
57
+ cfg = symbolize_keys(cfg)
58
+ @retry_policy = cfg.delete(:retry_policy) || {:strategy => SimpleRetry, :config => {:tries => 1, :delay => 5}}
59
+ if @retry_policy[:strategy].is_a?(String)
60
+ # TODO: when getting a retry policy strategy from the config file, it will be a string. Convert it to a class
61
+ # using the Kestrel module as a context, then Kernel.
62
+ @retry_policy[:strategy] = Kestrel.const_get(retry_policy[:strategy]) rescue Kestrel.to_class(@retry_policy[:strategy])
63
+ end
26
64
  @config = cfg
27
- @queues = {}
65
+ @subscriptions = {}
28
66
  connect
29
67
  nil
30
68
  end
@@ -39,17 +77,17 @@ module ActiveMessaging
39
77
  # messages from it
40
78
  def subscribe(destination_name, headers = {})
41
79
  headers[:destination] = destination_name
42
- if @queues[destination_name]
80
+ if @subscriptions[destination_name]
43
81
  # TODO: Should you get an exception or no?
44
82
  else
45
- @queues[destination_name] = headers
83
+ @subscriptions[destination_name] = headers
46
84
  end
47
85
  nil
48
86
  end
49
87
 
50
88
  # Stop receiving messages from the named destination
51
89
  def unsubscribe(destination_name, headers = {})
52
- @queues.delete(destination_name)
90
+ @subscriptions.delete(destination_name)
53
91
  end
54
92
 
55
93
  # Send a message to the named destination. headers can
@@ -67,10 +105,13 @@ module ActiveMessaging
67
105
  # Gets a message from any subscribed destination and returns it as a
68
106
  # ActiveMessaging::Adaptors::Kestrel::Message object
69
107
  def receive
70
- # TODO: Is this what ActiveMessaging expects: can it handle a nil return?
71
- #while (true)
72
- # Get a message from a subscribed queue, but don't favor any queue over another
73
- queues_to_check = @queues.size > 1 ? @queues.keys.sort_by{rand} : @queues.keys
108
+ return nil if @subscriptions.size < 1
109
+
110
+ # instantiate a class for doing the retries
111
+ retrier = @retry_policy[:strategy].new
112
+
113
+ retrier.do_work(@retry_policy[:config]) do
114
+ queues_to_check = @subscriptions.size > 1 ? @subscriptions.keys.sort_by{rand} : @subscriptions.keys
74
115
  queues_to_check.each do |queue|
75
116
  if item = @kestrel.get(normalize(queue))
76
117
  # TODO: ActiveMessaging ought to provide a way to do messaging
@@ -78,9 +119,7 @@ module ActiveMessaging
78
119
  return Message.new({'destination' => queue}, item, 'MESSAGE')
79
120
  end
80
121
  end
81
- # # Sleep a bit so we don't get into a spinloop
82
- # sleep @receive_throttle
83
- #end
122
+ end
84
123
  return nil
85
124
  end
86
125
 
@@ -91,6 +130,16 @@ module ActiveMessaging
91
130
  @normalized_names ||= Hash.new {|h,k| h[k] = k.gsub('/', '--FS--')}
92
131
  @normalized_names[name]
93
132
  end
133
+
134
+ def symbolize_keys(hash)
135
+ hash.inject({}) do |new_hash, (k, v)|
136
+ if v.is_a?(Hash)
137
+ v = symbolize_keys(v)
138
+ end
139
+ new_hash[k.to_sym] = v
140
+ new_hash
141
+ end
142
+ end
94
143
  end
95
144
  end
96
145
  end
@@ -0,0 +1,29 @@
1
+ require 'test/unit'
2
+ require 'activemessaging'
3
+ require 'activemessaging-kestrel-adapter'
4
+
5
+ class TestActiveMessagingKestrelAdapter < Test::Unit::TestCase
6
+ def test_default_config
7
+ cfg = {:servers => 'localhost:22133'}
8
+ adapter = ActiveMessaging::Adapters::Kestrel::Connection.new(cfg)
9
+ assert_equal adapter.instance_variable_get("@retry_policy")[:strategy], ActiveMessaging::Adapters::Kestrel::SimpleRetry
10
+ assert_equal adapter.instance_variable_get("@retry_policy")[:config], {:tries => 1, :delay => 5}
11
+ end
12
+
13
+ def test_config_from_yaml
14
+ yaml = <<-YAML
15
+ development:
16
+ adapter: kestrel
17
+ servers: localhost:22133
18
+ retry_policy:
19
+ strategy: SimpleRetry
20
+ config:
21
+ tries: 1
22
+ delay: 5
23
+ YAML
24
+ cfg = YAML.load(yaml)['development']
25
+ adapter = ActiveMessaging::Adapters::Kestrel::Connection.new(cfg)
26
+ assert_equal adapter.instance_variable_get("@retry_policy")[:strategy], ActiveMessaging::Adapters::Kestrel::SimpleRetry
27
+ assert_equal adapter.instance_variable_get("@retry_policy")[:config], {:tries => 1, :delay => 5}
28
+ end
29
+ end
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.0.2
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activemessaging-kestrel-adapter
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Douglas A. Seifert
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-02 00:00:00 -08:00
18
+ date: 2011-02-04 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,18 +26,34 @@ dependencies:
26
26
  requirements:
27
27
  - - "="
28
28
  - !ruby/object:Gem::Version
29
- hash: 49
29
+ hash: 61
30
30
  segments:
31
31
  - 1
32
32
  - 8
33
- - 3
34
- version: 1.8.3
33
+ - 5
34
+ version: 1.8.5
35
35
  type: :runtime
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
38
- name: bones
38
+ name: activemessaging
39
39
  prerelease: false
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - "="
44
+ - !ruby/object:Gem::Version
45
+ hash: 1
46
+ segments:
47
+ - 0
48
+ - 7
49
+ - 1
50
+ version: 0.7.1
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: bones
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
41
57
  none: false
42
58
  requirements:
43
59
  - - ">="
@@ -49,7 +65,7 @@ dependencies:
49
65
  - 2
50
66
  version: 3.6.2
51
67
  type: :development
52
- version_requirements: *id002
68
+ version_requirements: *id003
53
69
  description: |-
54
70
  This is an adapter to the kestrel messaging server for the ActiveMessaging framework. It is
55
71
  in the early stages of development.
@@ -63,7 +79,6 @@ extra_rdoc_files:
63
79
  - README.md
64
80
  - bin/activemessaging-kestrel-adapter
65
81
  files:
66
- - .Rakefile.swp
67
82
  - .bnsignore
68
83
  - History.txt
69
84
  - README.md
Binary file