right_agent 1.0.1 → 2.0.7
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/README.rdoc +10 -8
- data/Rakefile +31 -5
- data/lib/right_agent.rb +6 -1
- data/lib/right_agent/actor.rb +4 -20
- data/lib/right_agent/actors/agent_manager.rb +1 -1
- data/lib/right_agent/agent.rb +357 -144
- data/lib/right_agent/agent_config.rb +7 -6
- data/lib/right_agent/agent_identity.rb +13 -11
- data/lib/right_agent/agent_tag_manager.rb +60 -64
- data/{spec/results_mock.rb → lib/right_agent/clients.rb} +10 -24
- data/lib/right_agent/clients/api_client.rb +383 -0
- data/lib/right_agent/clients/auth_client.rb +247 -0
- data/lib/right_agent/clients/balanced_http_client.rb +369 -0
- data/lib/right_agent/clients/base_retry_client.rb +495 -0
- data/lib/right_agent/clients/right_http_client.rb +279 -0
- data/lib/right_agent/clients/router_client.rb +493 -0
- data/lib/right_agent/command/command_io.rb +4 -4
- data/lib/right_agent/command/command_parser.rb +2 -2
- data/lib/right_agent/command/command_runner.rb +1 -1
- data/lib/right_agent/connectivity_checker.rb +179 -0
- data/lib/right_agent/core_payload_types/secure_document_location.rb +2 -2
- data/lib/right_agent/dispatcher.rb +12 -10
- data/lib/right_agent/enrollment_result.rb +16 -12
- data/lib/right_agent/exceptions.rb +34 -20
- data/lib/right_agent/history.rb +10 -5
- data/lib/right_agent/log.rb +5 -5
- data/lib/right_agent/minimal.rb +1 -0
- data/lib/right_agent/multiplexer.rb +1 -1
- data/lib/right_agent/offline_handler.rb +270 -0
- data/lib/right_agent/packets.rb +7 -7
- data/lib/right_agent/payload_formatter.rb +1 -1
- data/lib/right_agent/pending_requests.rb +128 -0
- data/lib/right_agent/platform.rb +1 -1
- data/lib/right_agent/protocol_version_mixin.rb +69 -0
- data/lib/right_agent/{idempotent_request.rb → retryable_request.rb} +7 -7
- data/lib/right_agent/scripts/agent_controller.rb +28 -26
- data/lib/right_agent/scripts/agent_deployer.rb +37 -22
- data/lib/right_agent/scripts/common_parser.rb +10 -3
- data/lib/right_agent/secure_identity.rb +1 -1
- data/lib/right_agent/sender.rb +299 -785
- data/lib/right_agent/serialize/secure_serializer.rb +3 -1
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +2 -2
- data/lib/right_agent/serialize/serializable.rb +8 -3
- data/right_agent.gemspec +49 -18
- data/spec/agent_config_spec.rb +7 -7
- data/spec/agent_identity_spec.rb +7 -4
- data/spec/agent_spec.rb +43 -7
- data/spec/agent_tag_manager_spec.rb +72 -83
- data/spec/clients/api_client_spec.rb +423 -0
- data/spec/clients/auth_client_spec.rb +272 -0
- data/spec/clients/balanced_http_client_spec.rb +576 -0
- data/spec/clients/base_retry_client_spec.rb +635 -0
- data/spec/clients/router_client_spec.rb +594 -0
- data/spec/clients/spec_helper.rb +111 -0
- data/spec/command/command_io_spec.rb +1 -1
- data/spec/command/command_parser_spec.rb +1 -1
- data/spec/connectivity_checker_spec.rb +83 -0
- data/spec/dispatcher_spec.rb +3 -2
- data/spec/enrollment_result_spec.rb +2 -2
- data/spec/history_spec.rb +51 -39
- data/spec/offline_handler_spec.rb +340 -0
- data/spec/pending_requests_spec.rb +136 -0
- data/spec/{idempotent_request_spec.rb → retryable_request_spec.rb} +73 -73
- data/spec/sender_spec.rb +835 -1052
- data/spec/serialize/secure_serializer_spec.rb +3 -2
- data/spec/spec_helper.rb +54 -1
- metadata +71 -12
data/lib/right_agent/history.rb
CHANGED
@@ -77,17 +77,17 @@ module RightScale
|
|
77
77
|
# :graceful_exits(Integer|nil):: Number of graceful terminations, if any
|
78
78
|
# :crashes(Integer|nil):: Number of crashes, if any
|
79
79
|
# :last_crash_time(Integer|nil):: Time in seconds in Unix-epoch when last crash occurred, if any
|
80
|
+
# :crashed_last(Boolean):: Whether crashed last time it was started
|
80
81
|
def analyze_service
|
81
82
|
now = Time.now.to_i
|
82
83
|
if @last_analysis && @last_event == @last_update
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@last_analysis[:total_uptime] += delta
|
87
|
-
end
|
84
|
+
delta = now - @last_analysis_time
|
85
|
+
@last_analysis[:uptime] += delta
|
86
|
+
@last_analysis[:total_uptime] += delta
|
88
87
|
else
|
89
88
|
last_run = last_crash = @last_event = {:time => 0, :pid => 0, :event => nil}
|
90
89
|
restarts = graceful_exits = crashes = accumulated_uptime = 0
|
90
|
+
crashed_last = false
|
91
91
|
load.each do |event|
|
92
92
|
event = SerializationHelper.symbolize_keys(event)
|
93
93
|
case event[:event]
|
@@ -98,19 +98,23 @@ module RightScale
|
|
98
98
|
when "start"
|
99
99
|
crashes += 1
|
100
100
|
last_crash = event
|
101
|
+
crashed_last = true
|
101
102
|
when "run"
|
102
103
|
crashes += 1
|
103
104
|
last_crash = event
|
105
|
+
crashed_last = true
|
104
106
|
# Accumulating uptime here although this will wrongly include recovery time
|
105
107
|
accumulated_uptime += (event[:time] - @last_event[:time])
|
106
108
|
end
|
107
109
|
when "run"
|
108
110
|
last_run = event
|
109
111
|
when "stop"
|
112
|
+
crashed_last = false
|
110
113
|
if @last_event[:event] == "run" && @last_event[:pid] == event[:pid]
|
111
114
|
accumulated_uptime += (event[:time] - @last_event[:time])
|
112
115
|
end
|
113
116
|
when "graceful exit"
|
117
|
+
crashed_last = false
|
114
118
|
graceful_exits += 1
|
115
119
|
else
|
116
120
|
next
|
@@ -129,6 +133,7 @@ module RightScale
|
|
129
133
|
if crashes > 0
|
130
134
|
@last_analysis[:crashes] = crashes
|
131
135
|
@last_analysis[:last_crash_time] = last_crash[:time]
|
136
|
+
@last_analysis[:crashed_last] = crashed_last
|
132
137
|
end
|
133
138
|
end
|
134
139
|
@last_analysis_time = now
|
data/lib/right_agent/log.rb
CHANGED
@@ -133,7 +133,7 @@ module RightScale
|
|
133
133
|
def method_missing(m, *args)
|
134
134
|
init unless @initialized
|
135
135
|
@logger.level = level_from_sym(level) if @level_frozen
|
136
|
-
@logger.
|
136
|
+
@logger.send(m, *args)
|
137
137
|
end
|
138
138
|
|
139
139
|
# Determine whether this object, or its method_missing proxy, responds
|
@@ -218,9 +218,9 @@ module RightScale
|
|
218
218
|
# lvl(Constant):: One of Logger::DEBUG ... Logger::FATAL
|
219
219
|
#
|
220
220
|
# === Raise
|
221
|
-
# (
|
221
|
+
# (ArgumentError):: if level symbol is invalid
|
222
222
|
def level_from_sym(sym)
|
223
|
-
raise
|
223
|
+
raise ArgumentError, "Invalid log level symbol :#{sym}" unless LEVELS_MAP.include?(sym)
|
224
224
|
lvl = LEVELS_MAP[sym]
|
225
225
|
end
|
226
226
|
|
@@ -233,10 +233,10 @@ module RightScale
|
|
233
233
|
# sym(Symbol):: One of :debug, :info, :warn, :error or :fatal
|
234
234
|
#
|
235
235
|
# === Raise
|
236
|
-
# (
|
236
|
+
# (ArgumentError):: if level is invalid
|
237
237
|
def level_to_sym(lvl)
|
238
238
|
@@inverted_levels_map ||= LEVELS_MAP.invert
|
239
|
-
raise
|
239
|
+
raise ArgumentError, "Invalid log level: #{lvl}" unless @@inverted_levels_map.include?(lvl)
|
240
240
|
sym = @@inverted_levels_map[lvl]
|
241
241
|
end
|
242
242
|
|
data/lib/right_agent/minimal.rb
CHANGED
@@ -39,6 +39,7 @@ end
|
|
39
39
|
# requires are oriented toward that. any additional use cases may require a
|
40
40
|
# rethink of minimal loading.
|
41
41
|
require ::File.normalize_path('agent_config', RIGHT_AGENT_BASE_DIR)
|
42
|
+
require ::File.normalize_path('protocol_version_mixin', RIGHT_AGENT_BASE_DIR)
|
42
43
|
require ::File.normalize_path('command', RIGHT_AGENT_BASE_DIR)
|
43
44
|
require ::File.normalize_path('log', RIGHT_AGENT_BASE_DIR)
|
44
45
|
require ::File.normalize_path('pid_file', RIGHT_AGENT_BASE_DIR)
|
@@ -83,7 +83,7 @@ module RightScale
|
|
83
83
|
# === Return
|
84
84
|
# res(Object):: Result of first target in list
|
85
85
|
def method_missing(m, *args)
|
86
|
-
res = @targets.inject([]) { |res, t| res << t.
|
86
|
+
res = @targets.inject([]) { |res, t| res << t.send(m, *args) }
|
87
87
|
res[0]
|
88
88
|
end
|
89
89
|
|
@@ -0,0 +1,270 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2013 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
|
+
# Handler for queueing of requests when offline relative to RightNet
|
26
|
+
# and then sending the requests when successfully reconnect
|
27
|
+
class OfflineHandler
|
28
|
+
|
29
|
+
# Maximum seconds to wait before starting flushing offline queue when disabling offline mode
|
30
|
+
MAX_QUEUE_FLUSH_DELAY = 2 * 60
|
31
|
+
|
32
|
+
# Maximum number of offline queued requests before triggering restart vote
|
33
|
+
MAX_QUEUED_REQUESTS = 100
|
34
|
+
|
35
|
+
# Number of seconds that should be spent in offline mode before triggering a restart vote
|
36
|
+
RESTART_VOTE_DELAY = 15 * 60
|
37
|
+
|
38
|
+
# (Symbol) Current queue state with possible values:
|
39
|
+
# Value Description Action Next state
|
40
|
+
# :created Queue created init :initializing
|
41
|
+
# :initializing Agent still initializing start :running
|
42
|
+
# :running Queue has been started disable when offline :flushing
|
43
|
+
# :flushing Sending queued requests enable :running
|
44
|
+
# :terminating Agent terminating
|
45
|
+
attr_reader :state
|
46
|
+
|
47
|
+
# (Symbol) Current offline handling mode with possible values:
|
48
|
+
# Value Description
|
49
|
+
# :initializing Agent still initializing
|
50
|
+
# :online Agent connected
|
51
|
+
# :offline Agent disconnected
|
52
|
+
attr_reader :mode
|
53
|
+
|
54
|
+
# (Array) Offline queue
|
55
|
+
attr_accessor :queue
|
56
|
+
|
57
|
+
# Create offline queueing handler
|
58
|
+
#
|
59
|
+
# === Parameters
|
60
|
+
# restart_callback(Proc):: Callback that is activated on each restart vote with votes being initiated
|
61
|
+
# by offline queue exceeding MAX_QUEUED_REQUESTS
|
62
|
+
# offline_stats(RightSupport::Stats::Activity):: Offline queue tracking statistics
|
63
|
+
def initialize(restart_callback, offline_stats)
|
64
|
+
@restart_vote = restart_callback
|
65
|
+
@restart_vote_timer = nil
|
66
|
+
@restart_vote_count = 0
|
67
|
+
@offline_stats = offline_stats
|
68
|
+
@state = :created
|
69
|
+
@mode = :initializing
|
70
|
+
@queue = []
|
71
|
+
end
|
72
|
+
|
73
|
+
# Initialize the offline queue
|
74
|
+
# All requests sent prior to running this initialization are queued
|
75
|
+
# and then are sent once this initialization has run
|
76
|
+
# All requests following this call and prior to calling start
|
77
|
+
# are prepended to the request queue
|
78
|
+
#
|
79
|
+
# === Return
|
80
|
+
# true:: Always return true
|
81
|
+
def init
|
82
|
+
@state = :initializing if @state == :created
|
83
|
+
true
|
84
|
+
end
|
85
|
+
|
86
|
+
# Switch to online mode and send all buffered messages
|
87
|
+
#
|
88
|
+
# === Return
|
89
|
+
# true:: Always return true
|
90
|
+
def start
|
91
|
+
if @state == :initializing
|
92
|
+
if @mode == :offline
|
93
|
+
@state = :running
|
94
|
+
else
|
95
|
+
@state = :flushing
|
96
|
+
flush
|
97
|
+
end
|
98
|
+
@mode = :online if @mode == :initializing
|
99
|
+
end
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
# Is agent currently offline?
|
104
|
+
#
|
105
|
+
# === Return
|
106
|
+
# (Boolean):: true if agent offline, otherwise false
|
107
|
+
def offline?
|
108
|
+
@mode == :offline || @state == :created
|
109
|
+
end
|
110
|
+
|
111
|
+
# In request queueing mode?
|
112
|
+
#
|
113
|
+
# === Return
|
114
|
+
# (Boolean):: true if should queue request, otherwise false
|
115
|
+
def queueing?
|
116
|
+
offline? && @state != :flushing
|
117
|
+
end
|
118
|
+
|
119
|
+
# Switch to offline mode
|
120
|
+
# In this mode requests are queued in memory rather than being sent
|
121
|
+
# Idempotent
|
122
|
+
#
|
123
|
+
# === Return
|
124
|
+
# true:: Always return true
|
125
|
+
def enable
|
126
|
+
if offline?
|
127
|
+
if @state == :flushing
|
128
|
+
# If we were in offline mode then switched back to online but are still in the
|
129
|
+
# process of flushing the in-memory queue and are now switching to offline mode
|
130
|
+
# again then stop the flushing
|
131
|
+
@state = :running
|
132
|
+
end
|
133
|
+
else
|
134
|
+
Log.info("[offline] Disconnect from RightNet detected, entering offline mode")
|
135
|
+
Log.info("[offline] Messages will be queued in memory until RightNet connection is re-established")
|
136
|
+
@offline_stats.update
|
137
|
+
@queue ||= [] # Ensure queue is valid without losing any messages when going offline
|
138
|
+
@mode = :offline
|
139
|
+
start_timer
|
140
|
+
end
|
141
|
+
true
|
142
|
+
end
|
143
|
+
|
144
|
+
# Switch back to sending requests after in-memory queue gets flushed
|
145
|
+
# Idempotent
|
146
|
+
#
|
147
|
+
# === Return
|
148
|
+
# true:: Always return true
|
149
|
+
def disable
|
150
|
+
if offline? && @state != :created
|
151
|
+
Log.info("[offline] Connection to RightNet re-established")
|
152
|
+
@offline_stats.finish
|
153
|
+
cancel_timer
|
154
|
+
@state = :flushing
|
155
|
+
# Wait a bit to avoid flooding RightNet
|
156
|
+
EM.add_timer(rand(MAX_QUEUE_FLUSH_DELAY)) { flush }
|
157
|
+
end
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
# Queue given request in memory
|
162
|
+
#
|
163
|
+
# === Parameters
|
164
|
+
# request(Hash):: Request to be stored
|
165
|
+
#
|
166
|
+
# === Return
|
167
|
+
# true:: Always return true
|
168
|
+
def queue_request(kind, type, payload, target, callback)
|
169
|
+
request = {:kind => kind, :type => type, :payload => payload, :target => target, :callback => callback}
|
170
|
+
Log.info("[offline] Queuing request: #{request.inspect}")
|
171
|
+
vote_to_restart if (@restart_vote_count += 1) >= MAX_QUEUED_REQUESTS
|
172
|
+
if @state == :initializing
|
173
|
+
# We are in the initialization callback, requests should be put at the head of the queue
|
174
|
+
@queue.unshift(request)
|
175
|
+
else
|
176
|
+
@queue << request
|
177
|
+
end
|
178
|
+
true
|
179
|
+
end
|
180
|
+
|
181
|
+
# Prepare for agent termination
|
182
|
+
#
|
183
|
+
# === Return
|
184
|
+
# true:: Always return true
|
185
|
+
def terminate
|
186
|
+
@state = :terminating
|
187
|
+
cancel_timer
|
188
|
+
true
|
189
|
+
end
|
190
|
+
|
191
|
+
protected
|
192
|
+
|
193
|
+
# Send any requests that were queued while in offline mode
|
194
|
+
# Do this asynchronously to allow for agents to respond to requests
|
195
|
+
# Once all in-memory requests have been flushed, switch off offline mode
|
196
|
+
#
|
197
|
+
# === Parameters
|
198
|
+
# again(Boolean):: Whether being called in a loop
|
199
|
+
#
|
200
|
+
# === Return
|
201
|
+
# true:: Always return true
|
202
|
+
def flush(again = false)
|
203
|
+
if @state == :flushing
|
204
|
+
Log.info("[offline] Starting to flush request queue of size #{@queue.size}") unless again || @mode == :initializing
|
205
|
+
if @queue.any?
|
206
|
+
r = @queue.shift
|
207
|
+
if r[:callback]
|
208
|
+
Sender.instance.send(r[:kind], r[:type], r[:payload], r[:target]) { |result| r[:callback].call(result) }
|
209
|
+
else
|
210
|
+
Sender.instance.send(r[:kind], r[:type], r[:payload], r[:target])
|
211
|
+
end
|
212
|
+
end
|
213
|
+
if @queue.empty?
|
214
|
+
Log.info("[offline] Request queue flushed, resuming normal operations") unless @mode == :initializing
|
215
|
+
@mode = :online
|
216
|
+
@state = :running
|
217
|
+
else
|
218
|
+
EM.next_tick { flush(true) }
|
219
|
+
end
|
220
|
+
end
|
221
|
+
true
|
222
|
+
end
|
223
|
+
|
224
|
+
# Vote for restart and reset trigger
|
225
|
+
#
|
226
|
+
# === Parameters
|
227
|
+
# timer_trigger(Boolean):: true if vote was triggered by timer, false if it
|
228
|
+
# was triggered by number of messages in in-memory queue
|
229
|
+
#
|
230
|
+
# === Return
|
231
|
+
# true:: Always return true
|
232
|
+
def vote_to_restart(timer_trigger = false)
|
233
|
+
if @restart_vote
|
234
|
+
@restart_vote.call
|
235
|
+
if timer_trigger
|
236
|
+
start_timer
|
237
|
+
else
|
238
|
+
@restart_vote_count = 0
|
239
|
+
end
|
240
|
+
end
|
241
|
+
true
|
242
|
+
end
|
243
|
+
|
244
|
+
# Start restart vote timer
|
245
|
+
#
|
246
|
+
# === Return
|
247
|
+
# true:: Always return true
|
248
|
+
def start_timer
|
249
|
+
if @restart_vote && @state != :terminating
|
250
|
+
@restart_vote_timer ||= EM::Timer.new(RESTART_VOTE_DELAY) { vote_to_restart(timer_trigger = true) }
|
251
|
+
end
|
252
|
+
true
|
253
|
+
end
|
254
|
+
|
255
|
+
# Cancel restart vote timer
|
256
|
+
#
|
257
|
+
# === Return
|
258
|
+
# true:: Always return true
|
259
|
+
def cancel_timer
|
260
|
+
if @restart_vote_timer
|
261
|
+
@restart_vote_timer.cancel
|
262
|
+
@restart_vote_timer = nil
|
263
|
+
@restart_vote_count = 0
|
264
|
+
end
|
265
|
+
true
|
266
|
+
end
|
267
|
+
|
268
|
+
end # OfflineHandler
|
269
|
+
|
270
|
+
end # RightScale
|
data/lib/right_agent/packets.rb
CHANGED
@@ -40,7 +40,7 @@ end
|
|
40
40
|
|
41
41
|
module RightScale
|
42
42
|
|
43
|
-
# Base class for all packets flowing through the
|
43
|
+
# Base class for all packets flowing through the RightNet routers
|
44
44
|
# Knows how to dump itself to MessagePack or JSON
|
45
45
|
class Packet
|
46
46
|
|
@@ -156,7 +156,7 @@ module RightScale
|
|
156
156
|
# === Return
|
157
157
|
# log_msg(String):: Log representation
|
158
158
|
def to_s(filter = nil, version = nil)
|
159
|
-
v =
|
159
|
+
v = send(version) if version
|
160
160
|
v = (v && v != DEFAULT_VERSION) ? " v#{v}" : ""
|
161
161
|
log_msg = "[#{name}#{v}]"
|
162
162
|
duration = if @duration && (filter.nil? || filter.include?(:duration))
|
@@ -295,8 +295,8 @@ module RightScale
|
|
295
295
|
# opts(Hash):: Optional settings:
|
296
296
|
# :from(String):: Sender identity
|
297
297
|
# :scope(Hash):: Define behavior that should be used to resolve tag based routing
|
298
|
-
# :token(String):: Generated request id that a
|
299
|
-
# :reply_to(String):: Identity of the node that actor replies to, usually a
|
298
|
+
# :token(String):: Generated request id that a router uses to identify replies
|
299
|
+
# :reply_to(String):: Identity of the node that actor replies to, usually a router itself
|
300
300
|
# :selector(Symbol):: Selector used to route the request: :any or :all, defaults to :any,
|
301
301
|
# :all deprecated for version 13 and above
|
302
302
|
# :target(String|Array):: Target recipient(s)
|
@@ -426,12 +426,12 @@ module RightScale
|
|
426
426
|
# opts(Hash):: Optional settings:
|
427
427
|
# :from(String):: Sender identity
|
428
428
|
# :scope(Hash):: Define behavior that should be used to resolve tag based routing
|
429
|
-
# :token(String):: Generated request id that a
|
429
|
+
# :token(String):: Generated request id that a router uses to identify replies
|
430
430
|
# :selector(Symbol):: Selector used to route the request: :any or :all, defaults to :any
|
431
431
|
# :target(String|Array):: Target recipient(s)
|
432
432
|
# :persistent(Boolean):: Indicates if this request should be saved to persistent storage
|
433
433
|
# by the AMQP broker
|
434
|
-
# :confirm(Boolean):: Whether require confirmation response from
|
434
|
+
# :confirm(Boolean):: Whether require confirmation response from router containing targets
|
435
435
|
# to which request was published but not necessarily delivered
|
436
436
|
# :expires_at(Integer|nil):: Time in seconds in Unix-epoch when this request expires and
|
437
437
|
# is to be ignored by the receiver; value 0 means never expire; defaults to 0
|
@@ -532,7 +532,7 @@ module RightScale
|
|
532
532
|
# Create packet
|
533
533
|
#
|
534
534
|
# === Parameters
|
535
|
-
# token(String):: Generated request id that a
|
535
|
+
# token(String):: Generated request id that a router uses to identify replies
|
536
536
|
# to(String):: Identity of the node to which result should be delivered
|
537
537
|
# results(Any):: Arbitrary data that is transferred from actor, a result of actor's work
|
538
538
|
# from(String):: Sender identity
|
@@ -39,7 +39,7 @@ module RightScale
|
|
39
39
|
parts = type.split('/')
|
40
40
|
meth = "#{parts[1]}_#{parts[2]}".to_sym
|
41
41
|
res = nil
|
42
|
-
res = @formatter.
|
42
|
+
res = @formatter.send(meth, payload) if @formatter.respond_to?(meth)
|
43
43
|
res
|
44
44
|
end
|
45
45
|
|
@@ -0,0 +1,128 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2013 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
|
+
# Request that is waiting for a response
|
26
|
+
class PendingRequest
|
27
|
+
|
28
|
+
# (Symbol) Kind of request: :send_push or :send_request
|
29
|
+
attr_reader :kind
|
30
|
+
|
31
|
+
# (Time) Time when request message was received
|
32
|
+
attr_reader :receive_time
|
33
|
+
|
34
|
+
# (Proc) Block to be activated when response is received
|
35
|
+
attr_reader :response_handler
|
36
|
+
|
37
|
+
# (String) Token for parent request in a retry situation
|
38
|
+
attr_accessor :retry_parent_token
|
39
|
+
|
40
|
+
# (String) Non-delivery reason if any
|
41
|
+
attr_accessor :non_delivery
|
42
|
+
|
43
|
+
def initialize(kind, receive_time, response_handler)
|
44
|
+
@kind = kind
|
45
|
+
@receive_time = receive_time
|
46
|
+
@response_handler = response_handler
|
47
|
+
@retry_parent_token = nil
|
48
|
+
@non_delivery = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end # PendingRequest
|
52
|
+
|
53
|
+
# Cache for requests that are waiting for a response
|
54
|
+
# Automatically deletes push requests when get too old
|
55
|
+
# Retains non-push requests until explicitly deleted
|
56
|
+
class PendingRequests < Hash
|
57
|
+
|
58
|
+
# Maximum number of seconds to retain send pushes in cache
|
59
|
+
MAX_PUSH_AGE = 2 * 60
|
60
|
+
|
61
|
+
# Minimum number of seconds between push cleanups
|
62
|
+
MIN_CLEANUP_INTERVAL = 15
|
63
|
+
|
64
|
+
# Create cache
|
65
|
+
def initialize
|
66
|
+
@last_cleanup = Time.now
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
# Store pending request
|
71
|
+
#
|
72
|
+
# === Parameters
|
73
|
+
# token(String):: Generated message identifier
|
74
|
+
# pending_request(PendingRequest):: Pending request
|
75
|
+
#
|
76
|
+
# === Return
|
77
|
+
# (PendingRequest):: Stored request
|
78
|
+
def []=(token, pending_request)
|
79
|
+
now = Time.now
|
80
|
+
if (now - @last_cleanup) > MIN_CLEANUP_INTERVAL
|
81
|
+
self.reject! { |t, r| r.kind == :send_push && (now - r.receive_time) > MAX_PUSH_AGE }
|
82
|
+
@last_cleanup = now
|
83
|
+
end
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Select cache entries of the given kind
|
88
|
+
#
|
89
|
+
# === Parameters
|
90
|
+
# kind(Symbol):: Kind of request to be included: :send_push or :send_request
|
91
|
+
#
|
92
|
+
# === Return
|
93
|
+
# (Hash):: Requests of specified kind
|
94
|
+
def kind(kind)
|
95
|
+
self.reject { |t, r| r.kind != kind}
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get age of youngest pending request
|
99
|
+
#
|
100
|
+
# === Return
|
101
|
+
# age(Integer):: Age of youngest request
|
102
|
+
def youngest_age
|
103
|
+
now = Time.now
|
104
|
+
age = nil
|
105
|
+
self.each_value do |r|
|
106
|
+
seconds = (now - r.receive_time).to_i
|
107
|
+
age = seconds if age.nil? || seconds < age
|
108
|
+
end
|
109
|
+
age
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get age of oldest pending request
|
113
|
+
#
|
114
|
+
# === Return
|
115
|
+
# age(Integer):: Age of oldest request
|
116
|
+
def oldest_age
|
117
|
+
now = Time.now
|
118
|
+
age = nil
|
119
|
+
self.each_value do |r|
|
120
|
+
seconds = (now - r.receive_time).to_i
|
121
|
+
age = seconds if age.nil? || seconds > age
|
122
|
+
end
|
123
|
+
age
|
124
|
+
end
|
125
|
+
|
126
|
+
end # PendingRequests
|
127
|
+
|
128
|
+
end # RightScale
|