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.
- data/History.txt +7 -0
- data/README.md +22 -2
- data/Rakefile +1 -1
- data/lib/active_messaging/adapters/kestrel.rb +67 -18
- data/test/test_activemessaging-kestrel-adapter.rb +29 -0
- data/version.txt +1 -1
- metadata +25 -10
- data/.Rakefile.swp +0 -0
data/History.txt
CHANGED
@@ -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
|
-
*
|
36
|
-
* flesh out the configuration
|
56
|
+
* memcached-client
|
37
57
|
|
38
58
|
Future
|
39
59
|
------
|
data/Rakefile
CHANGED
@@ -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::
|
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 :
|
54
|
+
attr_accessor :retry_policy
|
21
55
|
|
22
56
|
def initialize(cfg)
|
23
|
-
|
24
|
-
|
25
|
-
@
|
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
|
-
@
|
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 @
|
80
|
+
if @subscriptions[destination_name]
|
43
81
|
# TODO: Should you get an exception or no?
|
44
82
|
else
|
45
|
-
@
|
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
|
-
@
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
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
|
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
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:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
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-
|
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:
|
29
|
+
hash: 61
|
30
30
|
segments:
|
31
31
|
- 1
|
32
32
|
- 8
|
33
|
-
-
|
34
|
-
version: 1.8.
|
33
|
+
- 5
|
34
|
+
version: 1.8.5
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
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: *
|
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
|
data/.Rakefile.swp
DELETED
Binary file
|