right_agent 2.2.1 → 2.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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/lib/right_agent.rb +1 -0
  3. data/lib/right_agent/actor.rb +0 -28
  4. data/lib/right_agent/actors/agent_manager.rb +20 -18
  5. data/lib/right_agent/agent.rb +69 -87
  6. data/lib/right_agent/agent_tag_manager.rb +1 -1
  7. data/lib/right_agent/clients/api_client.rb +0 -1
  8. data/lib/right_agent/clients/auth_client.rb +2 -6
  9. data/lib/right_agent/clients/balanced_http_client.rb +2 -2
  10. data/lib/right_agent/clients/base_retry_client.rb +12 -19
  11. data/lib/right_agent/clients/right_http_client.rb +1 -5
  12. data/lib/right_agent/clients/router_client.rb +8 -15
  13. data/lib/right_agent/command/command_parser.rb +3 -3
  14. data/lib/right_agent/command/command_runner.rb +1 -1
  15. data/lib/right_agent/connectivity_checker.rb +7 -11
  16. data/lib/right_agent/dispatcher.rb +7 -42
  17. data/lib/right_agent/enrollment_result.rb +2 -2
  18. data/lib/right_agent/error_tracker.rb +202 -0
  19. data/lib/right_agent/log.rb +0 -2
  20. data/lib/right_agent/packets.rb +1 -1
  21. data/lib/right_agent/pending_requests.rb +10 -4
  22. data/lib/right_agent/pid_file.rb +3 -3
  23. data/lib/right_agent/protocol_version_mixin.rb +3 -3
  24. data/lib/right_agent/scripts/agent_deployer.rb +13 -1
  25. data/lib/right_agent/sender.rb +14 -30
  26. data/lib/right_agent/serialize/secure_serializer.rb +4 -4
  27. data/right_agent.gemspec +2 -2
  28. data/spec/agent_spec.rb +5 -5
  29. data/spec/clients/auth_client_spec.rb +1 -1
  30. data/spec/clients/balanced_http_client_spec.rb +4 -2
  31. data/spec/clients/base_retry_client_spec.rb +5 -6
  32. data/spec/clients/router_client_spec.rb +1 -4
  33. data/spec/dispatcher_spec.rb +6 -55
  34. data/spec/error_tracker_spec.rb +293 -0
  35. data/spec/pending_requests_spec.rb +2 -2
  36. data/spec/sender_spec.rb +3 -3
  37. data/spec/spec_helper.rb +4 -2
  38. metadata +33 -66
@@ -20,8 +20,8 @@ module RightScale
20
20
  # Versions 5 and above use an identical format for the enrollment result
21
21
  SUPPORTED_VERSIONS = 5..AgentConfig.protocol_version
22
22
 
23
- class IntegrityFailure < Exception; end
24
- class VersionError < Exception; end
23
+ class IntegrityFailure < StandardError; end
24
+ class VersionError < StandardError; end
25
25
 
26
26
  attr_reader :r_s_version, :timestamp, :router_cert, :id_cert, :id_key
27
27
 
@@ -0,0 +1,202 @@
1
+ # Copyright (c) 2014 RightScale, Inc, All Rights Reserved Worldwide.
2
+ #
3
+ # THIS PROGRAM IS CONFIDENTIAL AND PROPRIETARY TO RIGHTSCALE
4
+ # AND CONSTITUTES A VALUABLE TRADE SECRET. Any unauthorized use,
5
+ # reproduction, modification, or disclosure of this program is
6
+ # strictly prohibited. Any use of this program by an authorized
7
+ # licensee is strictly subject to the terms and conditions,
8
+ # including confidentiality obligations, set forth in the applicable
9
+ # License Agreement between RightScale.com, Inc. and
10
+ # the licensee.
11
+
12
+ module RightScale
13
+
14
+ # Tracker for unexpected errors
15
+ # Logs them with appropriate trace information
16
+ # Accumulates statistics about exceptions
17
+ # Reports exceptions to external Errbit service via HydraulicBrake
18
+ class ErrorTracker
19
+
20
+ include RightSupport::Ruby::EasySingleton
21
+
22
+ # Container for exception statistics
23
+ attr_reader :exception_stats
24
+
25
+ # Initialize error tracker
26
+ #
27
+ # @param [Object] agent object using this tracker
28
+ # @param [String] agent_name uniquely identifying agent process on given server
29
+ #
30
+ # @option options [Integer, NilClass] :shard_id identifying shard of database in use
31
+ # @option options [Hash] :trace_level for restricting backtracing and Errbit reporting
32
+ # with exception class as key and :no_trace, :caller, or :trace as value; exceptions
33
+ # with :no_trace are not backtraced when logging nor are they recorded in stats
34
+ # or reported to Errbit
35
+ # @option options [String] :airbrake_endpoint URL for Airbrake for reporting exceptions
36
+ # to Errbit
37
+ # @option options [String] :airbrake_api_key for using the Airbrake API to access Errbit
38
+ #
39
+ # @return [TrueClass] always true
40
+ def init(agent, agent_name, options = {})
41
+ @agent = agent
42
+ @trace_level = options[:trace_level] || {}
43
+ notify_init(agent_name, options[:shard_id], options[:airbrake_endpoint], options[:airbrake_api_key])
44
+ reset_stats
45
+ true
46
+ end
47
+
48
+ # Log error and optionally track in stats
49
+ # Errbit notification is left to the callback configured in the stats tracker
50
+ #
51
+ # @param [String, Object] component reporting error; non-string is snake-cased
52
+ # @param [String] description of failure for use in logging
53
+ # @param [Exception, String] exception to be logged and tracked in stats;
54
+ # string errors are logged but not tracked in stats
55
+ # @param [Packet, Hash, NilClass] packet associated with exception
56
+ # @param [Symbol, NilClass] trace level override unless excluded by configured
57
+ # trace levels
58
+ #
59
+ # @return [Boolean] true if successfully logged, otherwise false
60
+ def log(component, description, exception = nil, packet = nil, trace = nil)
61
+ if exception.nil?
62
+ Log.error(description)
63
+ elsif exception.is_a?(String)
64
+ Log.error(description, exception)
65
+ else
66
+ trace = (@trace_level && @trace_level[exception.class]) || trace || :trace
67
+ Log.error(description, exception, trace)
68
+ track(component, exception, packet) if trace != :no_trace
69
+ end
70
+ true
71
+ rescue StandardError => e
72
+ Log.error("Failed to log error", e, :trace) rescue nil
73
+ false
74
+ end
75
+
76
+ # Track error in stats
77
+ #
78
+ # @param [String] component reporting error
79
+ # @param [Exception] exception to be tracked
80
+ # @param [Packet, Hash, NilClass] packet associated with exception
81
+ #
82
+ # @return [TrueClass] always true
83
+ def track(component, exception, packet = nil)
84
+ component = component.class.name.split("::").last.snake_case unless component.is_a?(String)
85
+ @exception_stats.track(component, exception, packet)
86
+ end
87
+
88
+ # Notify Errbit of error if notification enabled
89
+ #
90
+ # @param [Exception, String] exception raised
91
+ # @param [Packet, Hash] packet associated with exception
92
+ # @param [Object] agent object reporting error
93
+ # @param [String] component or service area where error occurred
94
+ #
95
+ # @return [TrueClass] always true
96
+ def notify(exception, packet = nil, agent = nil, component = nil)
97
+ if @notify_enabled
98
+ data = {
99
+ :error_message => exception.respond_to?(:message) ? exception.message : exception.to_s,
100
+ :backtrace => exception.respond_to?(:backtrace) ? exception.backtrace : caller,
101
+ :environment_name => ENV["RAILS_ENV"],
102
+ }
103
+ if agent
104
+ data[:cgi_data] = (@cgi_data || {}).merge(:agent_class => agent.class.name)
105
+ elsif @cgi_data
106
+ data[:cgi_data] = @cgi_data
107
+ end
108
+ data[:error_class] = exception.class.name if exception.is_a?(Exception)
109
+ data[:component] = component if component
110
+ if packet && packet.is_a?(Packet)
111
+ data[:action] = packet.type.split("/").last if packet.respond_to?(:type)
112
+ data[:parameters] = packet.payload if packet.respond_to?(:payload)
113
+ uuid = packet.token if packet.respond_to?(:token)
114
+ elsif packet.is_a?(Hash)
115
+ action = packet[:path] || packet["path"]
116
+ data[:action] = action.split("/").last if action
117
+ data[:parameters] = packet[:data] || packet["data"]
118
+ uuid = packet[:uuid] || packet["uuid"]
119
+ end
120
+ data[:session_data] = {:uuid => uuid} if uuid
121
+ HydraulicBrake.notify(data)
122
+ end
123
+ true
124
+ rescue Exception => e
125
+ Log.error("Failed to notify Errbit", e, :trace)
126
+ end
127
+
128
+ # Create proc for making callback to notifier
129
+ #
130
+ # @return [Proc] notifier callback
131
+ def notify_callback
132
+ Proc.new do |exception, packet, agent, component|
133
+ notify(exception, packet, agent, component)
134
+ end
135
+ end
136
+
137
+ # Get exception statistics
138
+ #
139
+ # @param reset [Boolean] Whether to reset the statistics after getting the current ones
140
+ #
141
+ # @return [Hash] current statistics
142
+ def stats(reset = false)
143
+ stats = {"exceptions" => @exception_stats.all}
144
+ reset_stats if reset
145
+ stats
146
+ end
147
+
148
+ protected
149
+
150
+ # Reset statistics
151
+ # Do not recreate exception stats since may be referenced externally
152
+ #
153
+ # @return [TrueClass] always true
154
+ def reset_stats
155
+ @exception_stats ||= RightSupport::Stats::Exceptions.new(@agent, notify_callback)
156
+ @exception_stats.reset
157
+ end
158
+
159
+ # Configure HydraulicBreak for exception notification
160
+ #
161
+ # @param [String] agent_name uniquely identifying agent process on given server
162
+ # @param [Integer, NilClass] shard_id identifying shard of database in use
163
+ # @param [String] endpoint URL for Airbrake for reporting exceptions to Errbit
164
+ # @param [String] api_key for using the Airbrake API to access Errbit
165
+ #
166
+ # @return [TrueClass] always true
167
+ #
168
+ # @raise [RuntimeError] hydraulic_brake gem missing
169
+ def notify_init(agent_name, shard_id, endpoint, api_key)
170
+ if endpoint && api_key
171
+ unless require_succeeds?("hydraulic_brake")
172
+ raise RuntimeError, "hydraulic_brake gem missing - required if airbrake options used in ErrorTracker"
173
+ end
174
+
175
+ @cgi_data = {
176
+ :shard_id => shard_id,
177
+ :process => $0,
178
+ :pid => Process.pid,
179
+ :agent_name => agent_name
180
+ }
181
+ @cgi_data[:shard_id] = shard_id if shard_id
182
+ @cgi_data[:sha] = CURRENT_SOURCE_SHA if defined?(CURRENT_SOURCE_SHA)
183
+
184
+ uri = URI.parse(endpoint)
185
+ HydraulicBrake.configure do |config|
186
+ config.secure = (uri.scheme == "https")
187
+ config.host = uri.host
188
+ config.port = uri.port
189
+ config.api_key = api_key
190
+ config.project_root = AgentConfig.root_dir
191
+ end
192
+ @notify_enabled = true
193
+ else
194
+ @cgi_data = {}
195
+ @notify_enabled = false
196
+ end
197
+ true
198
+ end
199
+
200
+ end # ErrorTracker
201
+
202
+ end # RightScale
@@ -410,8 +410,6 @@ module RightScale
410
410
  @logger = logger
411
411
  end
412
412
 
413
- protected
414
-
415
413
  # Initialize logger
416
414
  #
417
415
  # === Parameters
@@ -107,7 +107,7 @@ module RightScale
107
107
  }.to_msgpack(*a)
108
108
  @size = msg.size
109
109
  # For ruby 1.9 size attribute moves from front to back of packet
110
- re = RUBY_VERSION < "1.9.0" ? /size\xC0/ : /size\xC0$/
110
+ re = RUBY_VERSION < "1.9.0" ? Regexp.new("size\xC0") : Regexp.new("size\xC0$", nil, "n")
111
111
  # For msgpack 0.5.1 the to_msgpack result is a MessagePack::Packer so need to convert to string
112
112
  msg = msg.to_s.sub!(re) { |m| "size" + @size.to_msgpack }
113
113
  msg
@@ -97,12 +97,15 @@ module RightScale
97
97
 
98
98
  # Get age of youngest pending request
99
99
  #
100
+ # === Parameters
101
+ # pending_requests(Hash):: Pending requests to be examined
102
+ #
100
103
  # === Return
101
104
  # age(Integer):: Age of youngest request
102
- def youngest_age
105
+ def self.youngest_age(pending_requests)
103
106
  now = Time.now
104
107
  age = nil
105
- self.each_value do |r|
108
+ pending_requests.each_value do |r|
106
109
  seconds = (now - r.receive_time).to_i
107
110
  age = seconds if age.nil? || seconds < age
108
111
  end
@@ -111,12 +114,15 @@ module RightScale
111
114
 
112
115
  # Get age of oldest pending request
113
116
  #
117
+ # === Parameters
118
+ # pending_requests(Hash):: Pending requests to be examined
119
+ #
114
120
  # === Return
115
121
  # age(Integer):: Age of oldest request
116
- def oldest_age
122
+ def self.oldest_age(pending_requests)
117
123
  now = Time.now
118
124
  age = nil
119
- self.each_value do |r|
125
+ pending_requests.each_value do |r|
120
126
  seconds = (now - r.receive_time).to_i
121
127
  age = seconds if age.nil? || seconds > age
122
128
  end
@@ -31,7 +31,7 @@ module RightScale
31
31
  # the command protocol
32
32
  class PidFile
33
33
 
34
- class AlreadyRunning < Exception; end
34
+ class AlreadyRunning < RuntimeError; end
35
35
 
36
36
  attr_reader :identity, :pid_file, :cookie_file
37
37
 
@@ -72,8 +72,8 @@ module RightScale
72
72
  FileUtils.mkdir_p(@pid_dir)
73
73
  open(@pid_file,'w') { |f| f.write(Process.pid) }
74
74
  File.chmod(0644, @pid_file)
75
- rescue Exception => e
76
- Log.error "Failed to create PID file: #{e.message}"
75
+ rescue StandardError => e
76
+ ErrorTracker.log(self, "Failed to create PID file", e, nil, :caller)
77
77
  raise
78
78
  end
79
79
  true
@@ -34,9 +34,9 @@ module RightScale
34
34
  # Packet::DEFAULT_VERSION, which is true of all with version >= 12)
35
35
  def can_put_version_in_packet?(version); version && version != 0 end
36
36
 
37
- # Test whether given version of agent uses /mapper/query_tags rather than the
38
- # deprecated TagQuery packet
39
- def can_use_mapper_query_tags?(version); version && version >= 8 end
37
+ # Test whether given version of agent uses /router/query_tags or /mapper/query_tags
38
+ # rather than the deprecated TagQuery packet
39
+ def can_use_router_query_tags?(version); version && version >= 8 end
40
40
 
41
41
  # Test whether given version of agent can handle a request that is being retried
42
42
  # as indicated by a retries count in the Request packet
@@ -45,6 +45,8 @@
45
45
  # --grace-timeout SEC Set number of seconds before graceful termination times out
46
46
  # --[no-]dup-check Set whether to check for and reject duplicate requests, .e.g., due to retries
47
47
  # --fiber-pool-size, -f N Set size of fiber pool
48
+ # --airbrake-endpoint URL Set URL for Airbrake endpoint for reporting exceptions to Errbit
49
+ # --airbrake-api-key KEY Set Airbrake API key for use in reporting exceptions to Errbit
48
50
  # --options, -o KEY=VAL Set options that act as final override for any persisted configuration settings
49
51
  # --monit Generate monit configuration file
50
52
  # --test Build test deployment using default test settings
@@ -205,6 +207,14 @@ module RightScale
205
207
  options[:heartbeat] = sec.to_i
206
208
  end
207
209
 
210
+ opts.on('--airbrake-endpoint URL') do |url|
211
+ options[:airbrake_endpoint] = url
212
+ end
213
+
214
+ opts.on('--airbrake-api-key KEY') do |key|
215
+ options[:airbrake_api_key] = key
216
+ end
217
+
208
218
  opts.on('-o', '--options OPT') do |e|
209
219
  fail("Invalid option definition #{e}' (use '=' to separate name and value)") unless e.include?('=')
210
220
  key, val = e.split(/=/)
@@ -277,7 +287,7 @@ module RightScale
277
287
  actors_dirs = AgentConfig.actors_dirs
278
288
  actors.each do |a|
279
289
  found = false
280
- actors_dirs.each { |d| break if found = File.exist?(File.normalize_path(File.join(d, "#{a}.rb"))) }
290
+ actors_dirs.each { |d| break if (found = File.exist?(File.normalize_path(File.join(d, "#{a}.rb")))) }
281
291
  fail("Cannot find source for actor #{a.inspect} in #{actors_dirs.inspect}") unless found
282
292
  end
283
293
  true
@@ -318,6 +328,8 @@ module RightScale
318
328
  cfg[:http_proxy] = options[:http_proxy] if options[:http_proxy]
319
329
  cfg[:http_no_proxy] = options[:http_no_proxy] if options[:http_no_proxy]
320
330
  cfg[:fiber_pool_size] = options[:fiber_pool_size] if options[:fiber_pool_size]
331
+ cfg[:airbrake_endpoint] = options[:airbrake_endpoint] if options[:airbrake_endpoint]
332
+ cfg[:airbrake_api_key] = options[:airbrake_api_key] if options[:airbrake_api_key]
321
333
  cfg
322
334
  end
323
335
 
@@ -68,10 +68,6 @@ module RightScale
68
68
  #
69
69
  # === Parameters
70
70
  # agent(Agent):: Agent using this sender; uses its identity, client, and following options:
71
- # :exception_callback(Proc):: Callback with following parameters that is activated on exception events:
72
- # exception(Exception):: Exception
73
- # message(Packet):: Message being processed
74
- # agent(Agent):: Reference to agent
75
71
  # :offline_queueing(Boolean):: Whether to queue request if client currently disconnected,
76
72
  # also requires agent invocation of initialize_offline_queue and start_offline_queue methods below,
77
73
  # as well as enable_offline_mode and disable_offline_mode as client connection status changes
@@ -102,7 +98,7 @@ module RightScale
102
98
  @connectivity_checker = if @mode == :amqp
103
99
  # Only need connectivity checker for AMQP broker since RightHttpClient does its own checking
104
100
  # via periodic session renewal
105
- ConnectivityChecker.new(self, @options[:ping_interval] || 0, @ping_stats, @exception_stats)
101
+ ConnectivityChecker.new(self, @options[:ping_interval] || 0, @ping_stats)
106
102
  end
107
103
  @@instance = self
108
104
  end
@@ -418,7 +414,7 @@ module RightScale
418
414
  @offline_handler.terminate
419
415
  @connectivity_checker.terminate if @connectivity_checker
420
416
  pending = @pending_requests.kind(:send_request)
421
- [pending.size, pending.youngest_age]
417
+ [pending.size, PendingRequests.youngest_age(pending)]
422
418
  else
423
419
  [0, nil]
424
420
  end
@@ -448,9 +444,6 @@ module RightScale
448
444
  #
449
445
  # === Return
450
446
  # stats(Hash):: Current statistics:
451
- # "exceptions"(Hash|nil):: Exceptions raised per category, or nil if none
452
- # "total"(Integer):: Total exceptions for this category
453
- # "recent"(Array):: Most recent as a hash of "count", "type", "message", "when", and "where"
454
447
  # "non-deliveries"(Hash|nil):: Non-delivery activity stats with keys "total", "percent", "last",
455
448
  # and 'rate' with percentage breakdown per reason, or nil if none
456
449
  # "offlines"(Hash|nil):: Offline activity stats with keys "total", "last", and "duration",
@@ -482,11 +475,10 @@ module RightScale
482
475
  pending["pushes"] = @pending_requests.kind(:send_push).size
483
476
  requests = @pending_requests.kind(:send_request)
484
477
  if (pending["requests"] = requests.size) > 0
485
- pending["oldest age"] = requests.oldest_age
478
+ pending["oldest age"] = PendingRequests.oldest_age(requests)
486
479
  end
487
480
  end
488
481
  stats = {
489
- "exceptions" => @exception_stats.stats,
490
482
  "non-deliveries" => @non_delivery_stats.all,
491
483
  "offlines" => offlines,
492
484
  "pings" => @ping_stats.all,
@@ -520,7 +512,6 @@ module RightScale
520
512
  @offline_stats = RightSupport::Stats::Activity.new(measure_rate = false)
521
513
  @request_kind_stats = RightSupport::Stats::Activity.new(measure_rate = false)
522
514
  @send_failure_stats = RightSupport::Stats::Activity.new
523
- @exception_stats = RightSupport::Stats::Exceptions.new(@agent, @options[:exception_callback])
524
515
  true
525
516
  end
526
517
 
@@ -614,9 +605,8 @@ module RightScale
614
605
  EM_S.next_tick do
615
606
  begin
616
607
  http_send_once(kind, target, packet, received_at, &callback)
617
- rescue Exception => e
618
- Log.error("Failed sending or handling response for #{packet.trace} #{packet.type}", e, :trace)
619
- @exception_stats.track("request", e)
608
+ rescue StandardError => e
609
+ ErrorTracker.log(self, "Failed sending or handling response for #{packet.trace} #{packet.type}", e)
620
610
  end
621
611
  end
622
612
  else
@@ -668,8 +658,7 @@ module RightScale
668
658
  result = error_result(e.inspect)
669
659
  else
670
660
  agent_type = AgentIdentity.parse(@identity).agent_type
671
- Log.error("Failed to send #{packet.trace} #{packet.type}", e, :trace)
672
- @exception_stats.track("request", e)
661
+ ErrorTracker.log(self, "Failed to send #{packet.trace} #{packet.type}", e)
673
662
  result = error_result("#{agent_type.capitalize} agent internal error")
674
663
  end
675
664
  end
@@ -739,21 +728,17 @@ module RightScale
739
728
  # TemporarilyOffline:: If cannot send request because RightNet client currently disconnected
740
729
  # and offline queueing is disabled
741
730
  def amqp_send_once(packet, ids = nil)
742
- name =
743
731
  exchange = {:type => :fanout, :name => @request_queue, :options => {:durable => true, :no_declare => @secure}}
744
732
  @agent.client.publish(exchange, packet, :persistent => packet.persistent, :mandatory => true,
745
733
  :log_filter => [:tags, :target, :tries, :persistent], :brokers => ids)
746
734
  rescue RightAMQP::HABrokerClient::NoConnectedBrokers => e
747
- msg = "Failed to publish request #{packet.trace} #{packet.type}"
748
- Log.error(msg, e)
735
+ ErrorTracker.log(self, error = "Failed to publish request #{packet.trace} #{packet.type}", e, packet)
749
736
  @send_failure_stats.update("NoConnectedBrokers")
750
- raise TemporarilyOffline.new(msg + " (#{e.class}: #{e.message})")
751
- rescue Exception => e
752
- msg = "Failed to publish request #{packet.trace} #{packet.type}"
753
- Log.error(msg, e, :trace)
737
+ raise TemporarilyOffline.new(error + " (#{e.class}: #{e.message})")
738
+ rescue StandardError => e
739
+ ErrorTracker.log(self, error = "Failed to publish request #{packet.trace} #{packet.type}", e, packet)
754
740
  @send_failure_stats.update(e.class.name)
755
- @exception_stats.track("publish", e, packet)
756
- raise SendFailure.new(msg + " (#{e.class}: #{e.message})")
741
+ raise SendFailure.new(error + " (#{e.class}: #{e.message})")
757
742
  end
758
743
 
759
744
  # Send request via AMQP with one or more retries if do not receive a response in time
@@ -802,14 +787,13 @@ module RightScale
802
787
  @connectivity_checker.check(check_broker_ids.first) if check_broker_ids.any? && count == 1
803
788
  end
804
789
  rescue TemporarilyOffline => e
805
- Log.error("Failed retry for #{packet.trace} #{packet.type} because temporarily offline")
790
+ ErrorTracker.log(self, "Failed retry for #{packet.trace} #{packet.type} because temporarily offline")
806
791
  rescue SendFailure => e
807
- Log.error("Failed retry for #{packet.trace} #{packet.type} because of send failure")
792
+ ErrorTracker.log(self, "Failed retry for #{packet.trace} #{packet.type} because of send failure")
808
793
  rescue Exception => e
809
794
  # Not sending a response here because something more basic is broken in the retry
810
795
  # mechanism and don't want an error response to preempt a delayed actual response
811
- Log.error("Failed retry for #{packet.trace} #{packet.type} without responding", e, :trace)
812
- @exception_stats.track("retry", e, packet)
796
+ ErrorTracker.log(self, "Failed retry for #{packet.trace} #{packet.type} without responding", e, packet)
813
797
  end
814
798
  end
815
799
  end