right_agent 0.10.13 → 0.13.5

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 (39) hide show
  1. data/lib/right_agent.rb +2 -0
  2. data/lib/right_agent/actor.rb +45 -10
  3. data/lib/right_agent/actor_registry.rb +5 -5
  4. data/lib/right_agent/actors/agent_manager.rb +4 -4
  5. data/lib/right_agent/agent.rb +97 -37
  6. data/lib/right_agent/agent_tag_manager.rb +1 -2
  7. data/lib/right_agent/command/command_io.rb +1 -3
  8. data/lib/right_agent/command/command_runner.rb +9 -3
  9. data/lib/right_agent/dispatched_cache.rb +110 -0
  10. data/lib/right_agent/dispatcher.rb +119 -180
  11. data/lib/right_agent/history.rb +136 -0
  12. data/lib/right_agent/log.rb +6 -3
  13. data/lib/right_agent/monkey_patches/ruby_patch.rb +0 -1
  14. data/lib/right_agent/pid_file.rb +1 -1
  15. data/lib/right_agent/platform.rb +2 -2
  16. data/lib/right_agent/platform/linux.rb +8 -1
  17. data/lib/right_agent/platform/windows.rb +1 -1
  18. data/lib/right_agent/sender.rb +57 -41
  19. data/right_agent.gemspec +4 -4
  20. data/spec/actor_registry_spec.rb +7 -8
  21. data/spec/actor_spec.rb +87 -24
  22. data/spec/agent_spec.rb +107 -8
  23. data/spec/command/command_runner_spec.rb +12 -1
  24. data/spec/dispatched_cache_spec.rb +142 -0
  25. data/spec/dispatcher_spec.rb +110 -129
  26. data/spec/history_spec.rb +234 -0
  27. data/spec/idempotent_request_spec.rb +1 -1
  28. data/spec/log_spec.rb +15 -0
  29. data/spec/operation_result_spec.rb +4 -2
  30. data/spec/platform/darwin_spec.rb +13 -0
  31. data/spec/platform/linux_spec.rb +38 -0
  32. data/spec/platform/platform_spec.rb +46 -51
  33. data/spec/platform/windows_spec.rb +13 -0
  34. data/spec/sender_spec.rb +81 -38
  35. metadata +12 -9
  36. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +0 -45
  37. data/spec/platform/darwin.rb +0 -11
  38. data/spec/platform/linux.rb +0 -23
  39. data/spec/platform/windows.rb +0 -11
@@ -9,8 +9,6 @@
9
9
  # License Agreement between RightScale.com, Inc. and
10
10
  # the licensee.
11
11
 
12
- require 'singleton'
13
-
14
12
  module RightScale
15
13
 
16
14
  # Class which allows listening for data and sending data on sockets
@@ -18,7 +16,7 @@ module RightScale
18
16
  # the agent without having to go through RabbitMQ.
19
17
  class CommandIO
20
18
 
21
- include Singleton
19
+ include RightSupport::Ruby::EasySingleton
22
20
 
23
21
  # ensure uniqueness of handler to avoid confusion.
24
22
  raise "#{ServerInputHandler.name} is already defined" if defined?(ServerInputHandler)
@@ -24,7 +24,7 @@ module RightScale
24
24
 
25
25
  # Run commands exposed by an agent.
26
26
  # External processes can send commands through a socket with the specified port.
27
- # Command runner accepts connections and deserializes commands using YAML.
27
+ # Command runner accepts connections and unserializes commands using YAML.
28
28
  # Each command is expected to be a hash containing the :name and :options keys.
29
29
  class CommandRunner
30
30
  class << self
@@ -42,6 +42,8 @@ module RightScale
42
42
  # increment and retry if port already taken
43
43
  # identity(String):: Agent identity
44
44
  # commands(Hash):: Commands exposed by agent
45
+ # fiber_pool(NB::FiberPool):: Pool of initialized fibers to be used for executing
46
+ # received commands in non-blocking fashion
45
47
  #
46
48
  # === Block
47
49
  # If a block is provided, this method will yield after all setup has been completed,
@@ -54,7 +56,7 @@ module RightScale
54
56
  #
55
57
  # === Raise
56
58
  # (RightScale::Exceptions::Application):: If +start+ has already been called and +stop+ hasn't since
57
- def self.start(socket_port, identity, commands)
59
+ def self.start(socket_port, identity, commands, fiber_pool = nil)
58
60
  cmd_options = nil
59
61
  @listen_port = socket_port
60
62
 
@@ -65,7 +67,11 @@ module RightScale
65
67
  if cmd_cookie == @cookie
66
68
  cmd_name = c[:name].to_sym
67
69
  if commands.include?(cmd_name)
68
- commands[cmd_name].call(c, conn)
70
+ if fiber_pool
71
+ fiber_pool.spawn { commands[cmd_name].call(c, conn) }
72
+ else
73
+ commands[cmd_name].call(c, conn)
74
+ end
69
75
  else
70
76
  Log.warning("Unknown command '#{cmd_name}', known commands: #{commands.keys.join(', ')}")
71
77
  end
@@ -0,0 +1,110 @@
1
+ #
2
+ # Copyright (c) 2012 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightScale
24
+
25
+ # Cache for requests that have been dispatched recently
26
+ # This cache is intended for use in checking for duplicate requests
27
+ # Since this is a local cache, it is not usable for requests received from a shared queue
28
+ class DispatchedCache
29
+
30
+ # Maximum number of seconds to retain a dispatched request in cache
31
+ # This must be greater than the maximum possible retry timeout to avoid
32
+ # duplicate execution of a request
33
+ MAX_AGE = 12 * 60 * 60
34
+
35
+ # Initialize cache
36
+ #
37
+ # === Parameters
38
+ # identity(String):: Serialized identity of agent
39
+ def initialize(identity)
40
+ @identity = identity
41
+ @cache = {}
42
+ @lru = []
43
+ @max_age = MAX_AGE
44
+ end
45
+
46
+ # Store dispatched request token in cache unless from shared queue
47
+ #
48
+ # === Parameters
49
+ # token(String):: Generated message identifier
50
+ # shared_queue(String|nil):: Name of shared queue if being dispatched from a shared queue
51
+ #
52
+ # === Return
53
+ # true:: Always return true
54
+ def store(token, shared_queue)
55
+ if token && shared_queue.nil?
56
+ now = Time.now.to_i
57
+ if @cache.has_key?(token)
58
+ @cache[token] = now
59
+ @lru.push(@lru.delete(token))
60
+ else
61
+ @cache[token] = now
62
+ @lru.push(token)
63
+ @cache.delete(@lru.shift) while (now - @cache[@lru.first]) > @max_age
64
+ end
65
+ end
66
+ true
67
+ end
68
+
69
+ # Determine whether request has already been serviced
70
+ #
71
+ # === Parameters
72
+ # token(String):: Generated message identifier
73
+ #
74
+ # === Return
75
+ # (String|nil):: Identity of agent that already serviced request, or nil if none
76
+ def serviced_by(token)
77
+ if @cache[token]
78
+ @cache[token] = Time.now.to_i
79
+ @lru.push(@lru.delete(token))
80
+ @identity
81
+ end
82
+ end
83
+
84
+ # Get local cache statistics
85
+ #
86
+ # === Return
87
+ # stats(Hash|nil):: Current statistics, or nil if cache empty
88
+ # "local total"(Integer):: Total number in local cache, or nil if none
89
+ # "local max age"(String):: Time since oldest local cache entry created or updated
90
+ def stats
91
+ if (s = size) > 0
92
+ now = Time.now.to_i
93
+ {
94
+ "local total" => s,
95
+ "local max age" => RightSupport::Stats.elapsed(now - @cache[@lru.first])
96
+ }
97
+ end
98
+ end
99
+
100
+ # Get local cache size
101
+ #
102
+ # === Return
103
+ # (Integer):: Number of cache entries
104
+ def size
105
+ @cache.size
106
+ end
107
+
108
+ end # DispatchedCache
109
+
110
+ end # RightScale
@@ -28,82 +28,6 @@ module RightScale
28
28
  # Response queue name
29
29
  RESPONSE_QUEUE = "response"
30
30
 
31
- # Cache for requests that have been dispatched recently
32
- # This cache is intended for use in checking for duplicate requests
33
- class Dispatched
34
-
35
- # Maximum number of seconds to retain a dispatched request in cache
36
- # This must be greater than the maximum possible retry timeout to avoid
37
- # duplicate execution of a request
38
- MAX_AGE = 12 * 60 * 60
39
-
40
- # Initialize cache
41
- def initialize
42
- @cache = {}
43
- @lru = []
44
- end
45
-
46
- # Store dispatched request token in cache
47
- #
48
- # === Parameters
49
- # token(String):: Generated message identifier
50
- #
51
- # === Return
52
- # true:: Always return true
53
- def store(token)
54
- now = Time.now.to_i
55
- if @cache.has_key?(token)
56
- @cache[token] = now
57
- @lru.push(@lru.delete(token))
58
- else
59
- @cache[token] = now
60
- @lru.push(token)
61
- @cache.delete(@lru.shift) while (now - @cache[@lru.first]) > MAX_AGE
62
- end
63
- true
64
- end
65
-
66
- # Fetch request
67
- #
68
- # === Parameters
69
- # token(String):: Generated message identifier
70
- #
71
- # === Return
72
- # (Boolean):: true if request has been dispatched, otherwise false
73
- def fetch(token)
74
- if @cache[token]
75
- @cache[token] = Time.now.to_i
76
- @lru.push(@lru.delete(token))
77
- end
78
- end
79
-
80
- # Get cache size
81
- #
82
- # === Return
83
- # (Integer):: Number of cache entries
84
- def size
85
- @cache.size
86
- end
87
-
88
- # Get cache statistics
89
- #
90
- # === Return
91
- # stats(Hash|nil):: Current statistics, or nil if cache empty
92
- # "total"(Integer):: Total number in cache, or nil if none
93
- # "oldest"(Integer):: Number of seconds since oldest cache entry created or updated
94
- # "youngest"(Integer):: Number of seconds since youngest cache entry created or updated
95
- def stats
96
- if size > 0
97
- {
98
- "total" => size,
99
- "oldest age" => size > 0 ? Time.now.to_i - @cache[@lru.first] : 0,
100
- "youngest age" => size > 0 ? Time.now.to_i - @cache[@lru.last] : 0
101
- }
102
- end
103
- end
104
-
105
- end # Dispatched
106
-
107
31
  # (ActorRegistry) Registry for actors
108
32
  attr_reader :registry
109
33
 
@@ -120,13 +44,13 @@ module RightScale
120
44
  #
121
45
  # === Parameters
122
46
  # agent(Agent):: Agent using this dispatcher; uses its identity, broker, registry, and following options:
123
- # :dup_check(Boolean):: Whether to check for and reject duplicate requests, e.g., due to retries,
124
- # but only for requests that are dispatched from non-shared queues
125
47
  # :secure(Boolean):: true indicates to use Security features of RabbitMQ to restrict agents to themselves
126
48
  # :single_threaded(Boolean):: true indicates to run all operations in one thread; false indicates
127
49
  # to do requested work on event machine defer thread and all else, such as pings on main thread
128
50
  # :threadpool_size(Integer):: Number of threads in event machine thread pool
129
- def initialize(agent)
51
+ # dispatched_cache(DispatchedCache|nil):: Cache for dispatched requests that is used for detecting
52
+ # duplicate requests, or nil if duplicate checking is disabled
53
+ def initialize(agent, dispatched_cache = nil)
130
54
  @agent = agent
131
55
  @broker = @agent.broker
132
56
  @registry = @agent.registry
@@ -134,116 +58,131 @@ module RightScale
134
58
  options = @agent.options
135
59
  @secure = options[:secure]
136
60
  @single_threaded = options[:single_threaded]
137
- @dup_check = options[:dup_check]
138
61
  @pending_dispatches = 0
139
62
  @em = EM
140
63
  @em.threadpool_size = (options[:threadpool_size] || 20).to_i
141
64
  reset_stats
142
65
 
143
- # Only access following from primary thread
144
- @dispatched = Dispatched.new if @dup_check
66
+ # Only access this cache from primary thread
67
+ @dispatched_cache = dispatched_cache
145
68
  end
146
69
 
147
70
  # Dispatch request to appropriate actor for servicing
148
71
  # Handle returning of result to requester including logging any exceptions
149
72
  # Reject requests whose TTL has expired or that are duplicates of work already dispatched
150
- # but do not do duplicate checking if being dispatched from a shared queue
151
73
  # Work is done in background defer thread if single threaded option is false
74
+ # Acknowledge request after actor has responded
152
75
  #
153
76
  # === Parameters
154
77
  # request(Request|Push):: Packet containing request
155
- # shared(Boolean):: Whether being dispatched from a shared queue
78
+ # header(AMQP::Frame::Header|nil):: Request header containing ack control
79
+ # shared_queue(String|nil):: Name of shared queue if being dispatched from a shared queue
156
80
  #
157
81
  # === Return
158
- # r(Result):: Result from dispatched request, nil if not dispatched because dup or stale
159
- def dispatch(request, shared = false)
160
-
161
- # Determine which actor this request is for
162
- prefix, method = request.type.split('/')[1..-1]
163
- method ||= :index
164
- actor = @registry.actor_for(prefix)
165
- token = request.token
166
- received_at = @requests.update(method, (token if request.kind_of?(Request)))
167
- if actor.nil?
168
- Log.error("No actor for dispatching request <#{request.token}> of type #{request.type}")
169
- return nil
170
- end
82
+ # (Result|nil):: Result from dispatched request, nil if not dispatched because dup or stale
83
+ def dispatch(request, header = nil, shared_queue = nil)
84
+ begin
85
+ ack_deferred = false
171
86
 
172
- # Reject this request if its TTL has expired
173
- if (expires_at = request.expires_at) && expires_at > 0 && received_at.to_i >= expires_at
174
- @rejects.update("expired (#{method})")
175
- Log.info("REJECT EXPIRED <#{token}> from #{request.from} TTL #{RightSupport::Stats.elapsed(received_at.to_i - expires_at)} ago")
176
- if request.is_a?(Request)
177
- # For agents that do not know about non-delivery, use error result
178
- non_delivery = if request.recv_version < 13
179
- OperationResult.error("Could not deliver request (#{OperationResult::TTL_EXPIRATION})")
180
- else
181
- OperationResult.non_delivery(OperationResult::TTL_EXPIRATION)
182
- end
183
- result = Result.new(token, request.reply_to, non_delivery, @identity, request.from, request.tries, request.persistent)
184
- exchange = {:type => :queue, :name => RESPONSE_QUEUE, :options => {:durable => true, :no_declare => @secure}}
185
- @broker.publish(exchange, result, :persistent => true, :mandatory => true)
87
+ # Determine which actor this request is for
88
+ prefix, method = request.type.split('/')[1..-1]
89
+ method ||= :index
90
+ method = method.to_sym
91
+ actor = @registry.actor_for(prefix)
92
+ token = request.token
93
+ received_at = @requests.update(method, (token if request.kind_of?(Request)))
94
+ if actor.nil?
95
+ Log.error("No actor for dispatching request <#{token}> of type #{request.type}")
96
+ return nil
186
97
  end
187
- return nil
188
- end
98
+ method_idempotent = actor.class.idempotent?(method)
189
99
 
190
- # Reject this request if it is a duplicate
191
- if @dup_check && !shared && request.kind_of?(Request)
192
- if @dispatched.fetch(token)
193
- @rejects.update("duplicate (#{method})")
194
- Log.info("REJECT DUP <#{token}> of self")
100
+ # Reject this request if its TTL has expired
101
+ if (expires_at = request.expires_at) && expires_at > 0 && received_at.to_i >= expires_at
102
+ @rejects.update("expired (#{method})")
103
+ Log.info("REJECT EXPIRED <#{token}> from #{request.from} TTL #{RightSupport::Stats.elapsed(received_at.to_i - expires_at)} ago")
104
+ if request.is_a?(Request)
105
+ # For agents that do not know about non-delivery, use error result
106
+ non_delivery = if request.recv_version < 13
107
+ OperationResult.error("Could not deliver request (#{OperationResult::TTL_EXPIRATION})")
108
+ else
109
+ OperationResult.non_delivery(OperationResult::TTL_EXPIRATION)
110
+ end
111
+ result = Result.new(token, request.reply_to, non_delivery, @identity, request.from, request.tries, request.persistent)
112
+ exchange = {:type => :queue, :name => RESPONSE_QUEUE, :options => {:durable => true, :no_declare => @secure}}
113
+ @broker.publish(exchange, result, :persistent => true, :mandatory => true)
114
+ end
195
115
  return nil
196
116
  end
197
- request.tries.each do |t|
198
- if @dispatched.fetch(t)
199
- @rejects.update("retry duplicate (#{method})")
200
- Log.info("REJECT RETRY DUP <#{token}> of <#{t}>")
117
+
118
+ # Reject this request if it is a duplicate
119
+ if !method_idempotent && @dispatched_cache
120
+ if by = @dispatched_cache.serviced_by(token)
121
+ @rejects.update("duplicate (#{method})")
122
+ Log.info("REJECT DUP <#{token}> serviced by #{by == @identity ? 'self' : by}")
201
123
  return nil
202
124
  end
125
+ request.tries.each do |t|
126
+ if by = @dispatched_cache.serviced_by(t)
127
+ @rejects.update("retry duplicate (#{method})")
128
+ Log.info("REJECT RETRY DUP <#{token}> of <#{t}> serviced by #{by == @identity ? 'self' : by}")
129
+ return nil
130
+ end
131
+ end
203
132
  end
204
- end
205
133
 
206
- # Proc for performing request in actor
207
- operation = lambda do
208
- begin
209
- @pending_dispatches += 1
210
- @last_request_dispatch_time = received_at.to_i
211
- @dispatched.store(token) if @dup_check && !shared && request.kind_of?(Request) && token
212
- if actor.method(method).arity.abs == 1
213
- actor.__send__(method, request.payload)
214
- else
215
- actor.__send__(method, request.payload, request)
134
+ # Proc for performing request in actor
135
+ operation = lambda do
136
+ begin
137
+ @pending_dispatches += 1
138
+ @last_request_dispatch_time = received_at.to_i
139
+ @dispatched_cache.store(token, shared_queue) if !method_idempotent && @dispatched_cache
140
+ if actor.method(method).arity.abs == 1
141
+ actor.__send__(method, request.payload)
142
+ else
143
+ actor.__send__(method, request.payload, request)
144
+ end
145
+ rescue Exception => e
146
+ @pending_dispatches = [@pending_dispatches - 1, 0].max
147
+ OperationResult.error(handle_exception(actor, method, request, e))
216
148
  end
217
- rescue Exception => e
218
- @pending_dispatches = [@pending_dispatches - 1, 0].max
219
- handle_exception(actor, method, request, e)
220
149
  end
221
- end
222
-
223
- # Proc for sending response
224
- callback = lambda do |r|
225
- begin
226
- @pending_dispatches = [@pending_dispatches - 1, 0].max
227
- if request.kind_of?(Request)
228
- duration = @requests.finish(received_at, token)
229
- r = Result.new(token, request.reply_to, r, @identity, request.from, request.tries, request.persistent, duration)
230
- exchange = {:type => :queue, :name => RESPONSE_QUEUE, :options => {:durable => true, :no_declare => @secure}}
231
- @broker.publish(exchange, r, :persistent => true, :mandatory => true, :log_filter => [:tries, :persistent, :duration])
150
+
151
+ # Proc for sending response
152
+ callback = lambda do |r|
153
+ begin
154
+ if request.kind_of?(Request)
155
+ duration = @requests.finish(received_at, token)
156
+ r = Result.new(token, request.reply_to, r, @identity, request.from, request.tries, request.persistent, duration)
157
+ exchange = {:type => :queue, :name => RESPONSE_QUEUE, :options => {:durable => true, :no_declare => @secure}}
158
+ @broker.publish(exchange, r, :persistent => true, :mandatory => true, :log_filter => [:tries, :persistent, :duration])
159
+ end
160
+ rescue RightAMQP::HABrokerClient::NoConnectedBrokers => e
161
+ Log.error("Failed to publish result of dispatched request #{request.trace}", e)
162
+ rescue Exception => e
163
+ Log.error("Failed to publish result of dispatched request #{request.trace}", e, :trace)
164
+ @exceptions.track("publish response", e)
165
+ ensure
166
+ header.ack if header
167
+ @pending_dispatches = [@pending_dispatches - 1, 0].max
232
168
  end
233
- rescue RightAMQP::HABrokerClient::NoConnectedBrokers => e
234
- Log.error("Failed to publish result of dispatched request #{request.trace}", e)
235
- rescue Exception => e
236
- Log.error("Failed to publish result of dispatched request #{request.trace}", e, :trace)
237
- @exceptions.track("publish response", e)
169
+ r # For unit tests
238
170
  end
239
- r # For unit tests
240
- end
241
171
 
242
- # Process request and send response, if any
243
- if @single_threaded
244
- @em.next_tick { callback.call(operation.call) }
245
- else
246
- @em.defer(operation, callback)
172
+ # Process request and send response, if any
173
+ begin
174
+ ack_deferred = true
175
+ if @single_threaded
176
+ @em.next_tick { callback.call(operation.call) }
177
+ else
178
+ @em.defer(operation, callback)
179
+ end
180
+ rescue Exception
181
+ header.ack if header
182
+ raise
183
+ end
184
+ ensure
185
+ header.ack unless ack_deferred || header.nil?
247
186
  end
248
187
  end
249
188
 
@@ -252,7 +191,7 @@ module RightScale
252
191
  # === Return
253
192
  # (Integer|nil):: Age in seconds of youngest dispatch, or nil if none
254
193
  def dispatch_age
255
- age = Time.now.to_i - @last_request_dispatch_time if @last_request_dispatch_time && @pending_dispatches > 0
194
+ Time.now.to_i - @last_request_dispatch_time if @last_request_dispatch_time && @pending_dispatches > 0
256
195
  end
257
196
 
258
197
  # Get dispatcher statistics
@@ -262,7 +201,7 @@ module RightScale
262
201
  #
263
202
  # === Return
264
203
  # stats(Hash):: Current statistics:
265
- # "cached"(Hash|nil):: Number of dispatched requests cached and age of youngest and oldest,
204
+ # "dispatched cache"(Hash|nil):: Number of dispatched requests cached and age of youngest and oldest,
266
205
  # or nil if empty
267
206
  # "exceptions"(Hash|nil):: Exceptions raised per category, or nil if none
268
207
  # "total"(Integer):: Total for category
@@ -282,12 +221,12 @@ module RightScale
282
221
  }
283
222
  end
284
223
  stats = {
285
- "cached" => (@dispatched.stats if @dup_check),
286
- "exceptions" => @exceptions.stats,
287
- "pending" => pending,
288
- "rejects" => @rejects.all,
289
- "requests" => @requests.all,
290
- "response time" => @requests.avg_duration
224
+ "dispatched cache" => (@dispatched_cache.stats if @dispatched_cache),
225
+ "exceptions" => @exceptions.stats,
226
+ "pending" => pending,
227
+ "rejects" => @rejects.all,
228
+ "requests" => @requests.all,
229
+ "response time" => @requests.avg_duration
291
230
  }
292
231
  reset_stats if reset
293
232
  stats
@@ -311,30 +250,30 @@ module RightScale
311
250
  #
312
251
  # === Parameters
313
252
  # actor(Actor):: Actor that failed to process request
314
- # method(String):: Name of actor method being dispatched to
253
+ # method(Symbol):: Name of actor method being dispatched to
315
254
  # request(Packet):: Packet that dispatcher is acting upon
316
- # e(Exception):: Exception that was raised
255
+ # exception(Exception):: Exception that was raised
317
256
  #
318
257
  # === Return
319
- # error(String):: Error description for this exception
320
- def handle_exception(actor, method, request, e)
321
- error = Log.format("Failed processing #{request.type}", e, :trace)
322
- Log.error(error)
258
+ # (String):: Error description for this exception
259
+ def handle_exception(actor, method, request, exception)
260
+ error = "Could not handle #{request.type} request"
261
+ Log.error(error, exception, :trace)
323
262
  begin
324
263
  if actor && actor.class.exception_callback
325
264
  case actor.class.exception_callback
326
265
  when Symbol, String
327
- actor.send(actor.class.exception_callback, method.to_sym, request, e)
266
+ actor.send(actor.class.exception_callback, method, request, exception)
328
267
  when Proc
329
- actor.instance_exec(method.to_sym, request, e, &actor.class.exception_callback)
268
+ actor.instance_exec(method, request, exception, &actor.class.exception_callback)
330
269
  end
331
270
  end
332
- @exceptions.track(request.type, e)
333
- rescue Exception => e2
334
- Log.error("Failed handling error for #{request.type}", e2, :trace)
335
- @exceptions.track(request.type, e2) rescue nil
271
+ @exceptions.track(request.type, exception)
272
+ rescue Exception => e
273
+ Log.error("Failed handling error for #{request.type}", e, :trace)
274
+ @exceptions.track(request.type, e) rescue nil
336
275
  end
337
- error
276
+ Log.format(error, exception)
338
277
  end
339
278
 
340
279
  end # Dispatcher