Class: RightScale::IdempotentRequest

Inherits:
Object
  • Object
show all
Includes:
EM::Deferrable, OperationResultHelper
Defined in:
lib/right_agent/idempotent_request.rb

Overview

This is a retryable request for use when the execution of the request by the receiver is known to be idempotent and when there is a need to indefinitely pursue getting a usable response, e.g., when an instance is launching. It is implemented as an EM::Deferrable and as such invokes the Proc defined with its #callback method with the result content from a OperationResult::SUCCESS response, or it will invoke the Proc defined with its #errback method with error content if the response is an OperationResult::ERROR or CANCEL, or if the request has timed out. The request can be canceled with the #cancel method, or the receiver of the request may respond with a CANCEL result to cause the request to be canceled. This is useful in situations where the request is never expected to succeed regardless of the number of retries. By default if the response to the request is an OperationResult::RETRY or NON_DELIVERY indication, the request is automatically retried, as is also the case for an ERROR indication if the :retry_on_error option is specified. The retry algorithm is controlled by the :retry_delay, :retry_delay_count, and :max_retry_delay settings. The initial retry interval is the default or specified :retry_delay and this interval is used :retry_delay_count times, at which point the :retry_delay is doubled and the :retry_delay_count is halved. This backoff is again applied after the new :retry_delay_count is reached, and so on until :retry_delay reaches :max_retry_delay which then is used as the interval until the default or specified :timeout is reached. The default :timeout is 4 days.

Constant Summary collapse

DEFAULT_RETRY_DELAY =

Default delay before initial retry in case of failure with -1 meaning no delay

5
DEFAULT_RETRY_DELAY_COUNT =

Default minimum number of retries before beginning backoff

60
DEFAULT_MAX_RETRY_DELAY =

Maximum default delay before retry when backing off

60
RETRY_BACKOFF_FACTOR =

Factor used for exponential backoff of retry delay

2
DEFAULT_TIMEOUT =

Default timeout with -1 meaning never timeout

4 * 24 * 60 * 60

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from OperationResultHelper

#cancel_result, #continue_result, #error_result, #non_delivery_result, #result_from, #retry_result, #success_result

Constructor Details

#initialize(operation, payload, options = {}) ⇒ IdempotentRequest

Send idempotent request Retry until timeout is reached (indefinitely if timeout <= 0) Calls deferrable callback on completion, error callback on timeout

Parameters

operation(String)

Request operation (e.g., ‘/booter/get_boot_bundle’)

payload(Hash)

Request payload

options(Hash)

Request options

:targets(Array)

Targets from which to randomly choose one

:retry_on_error(Boolean)

Whether request should be retried if recipient returned an error

:retry_delay(Fixnum)

Number of seconds delay before initial retry with -1 meaning no delay,

  defaults to DEFAULT_RETRY_DELAY
:retry_delay_count(Fixnum):: Minimum number of retries at initial :retry_delay value before
  increasing delay exponentially and decreasing this count exponentially, defaults to
  DEFAULT_RETRY_DELAY_COUNT
:max_retry_delay(Fixnum):: Maximum number of seconds of retry delay, defaults to DEFAULT_MAX_RETRY_DELAY
:timeout(Fixnum):: Number of seconds with no response before error callback gets called, with
  -1 meaning never, defaults to DEFAULT_TIMEOUT

Raises

ArgumentError

If operation or payload not specified

Raises:

  • (ArgumentError)


88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/right_agent/idempotent_request.rb', line 88

def initialize(operation, payload, options = {})
  raise ArgumentError.new("operation is required") unless @operation = operation
  raise ArgumentError.new("payload is required") unless @payload = payload
  @retry_on_error = options[:retry_on_error] || false
  @timeout = options[:timeout] || DEFAULT_TIMEOUT
  @retry_delay = options[:retry_delay] || DEFAULT_RETRY_DELAY
  @retry_delay_count = options[:retry_delay_count] || DEFAULT_RETRY_DELAY_COUNT
  @max_retry_delay = options[:max_retry_delay] || DEFAULT_MAX_RETRY_DELAY
  @retries = 0
  @targets = options[:targets]
  @raw_response = nil
  @done = false
end

Instance Attribute Details

#raw_responseObject (readonly)

Returns the value of attribute raw_response.



65
66
67
# File 'lib/right_agent/idempotent_request.rb', line 65

def raw_response
  @raw_response
end

Instance Method Details

#cancel(msg) ⇒ Object

Cancel request and call error callback

Parameters

msg(String)

Reason why request is cancelled, given to error callback

Return

true

Always return true



126
127
128
129
130
131
132
133
134
# File 'lib/right_agent/idempotent_request.rb', line 126

def cancel(msg)
  if @cancel_timer
    @cancel_timer.cancel
    @cancel_timer = nil
  end
  @done = true
  fail(msg)
  true
end

#runObject

Send request and retry until timeout is reached or response is received Ignore duplicate responses

Return

true

Always return true



107
108
109
110
111
112
113
114
115
116
117
# File 'lib/right_agent/idempotent_request.rb', line 107

def run
  Sender.instance.send_retryable_request(@operation, @payload, retrieve_target(@targets)) { |r| handle_response(r) }
  if @cancel_timer.nil? && @timeout > 0
    @cancel_timer = EM::Timer.new(@timeout) do
      msg = "Request #{@operation} timed out after #{@timeout} seconds"
      Log.info(msg)
      cancel(msg)
    end
  end
  true
end