activemessaging-kestrel-adapter 0.0.1 → 0.0.2

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