class Rubybear::ErrorParser

Constants

DUPECHECK
REPREPARE_BLACKLIST
REPREPARE_WHITELIST

Attributes

api_method[R]
api_name[R]
api_params[R]
can_reprepare[R]
can_reprepare?[R]
can_retry[R]
can_retry?[R]
debug[R]
error[R]
error_code[R]
error_message[R]
expiry[R]
expiry?[R]
node_degraded[R]
node_degraded?[R]
response[R]
trx_id[R]

Public Class Methods

new(response) click to toggle source
# File lib/rubybear/error_parser.rb, line 23
def initialize(response)
  @response = response
  
  @error = nil
  @error_code = nil
  @error_message = nil
  @api_name = nil
  @api_method = nil
  @api_params = nil
  
  @expiry = nil
  @can_retry = nil
  @can_reprepare = nil
  @trx_id = nil
  @debug = nil
  
  parse_error_response
end

Public Instance Methods

coerce_backtrace() click to toggle source
# File lib/rubybear/error_parser.rb, line 169
def coerce_backtrace
  can_retry = false
  
  case @error['code']
  when -32003
    any_of = [
      'Internal Error"',
      '_api_plugin not enabled.'
    ]
    
    can_retry = error_match?('Unable to acquire database lock')
    
    if !can_retry && error_match?(any_of)
      can_retry = true
      @node_degraded = true
    else
      @node_degraded = false
    end
  when -32002
    can_retry = @node_degraded = error_match?('Could not find API')
  when 1
    can_retry = @node_degraded = error_match?('no method with name \'condenser_api')
  end
    
  can_retry
end
error_match?(matches) click to toggle source
# File lib/rubybear/error_parser.rb, line 196
def error_match?(matches)
  matches = [matches].flatten
  
  any = matches.map do |match|
    case match
    when String
      @error['message'] && @error['message'].include?(match)
    when ::Array
      if @error['message']
        match.map { |m| m.include?(match) }.include? true
      else
        false
      end
    else; false
    end
  end
  
  any.include?(true)
end
inspect() click to toggle source
# File lib/rubybear/error_parser.rb, line 224
def inspect
  "#<#{self.class.name} [#{to_s}]>"
end
parse_error_response() click to toggle source
# File lib/rubybear/error_parser.rb, line 42
def parse_error_response
  if response.nil?
    @expiry = false
    @can_retry = false
    @can_reprepare = false
    
    return
  end
  
  @response = JSON[response] if response.class == String

  @error = if !!@response['error']
    response['error']
  else
    response
  end
  
  begin
    if !!@error['data']
      # These are, by far, the more interesting errors, so we try to pull
      # them out first, if possible.
      
      @error_code = @error['data']['code']
      stacks = @error['data']['stack']
      stack_formats = nil
      
      @error_message = if !!stacks
        stack_formats = stacks.map { |s| s['format'] }
        stack_datum = stacks.map { |s| s['data'] }
        data_call_method = stack_datum.find { |data| data['call.method'] == 'call' }
        data_name = stack_datum.find { |data| !!data['name'] }
        
        # See if we can recover a transaction id out of this hot mess.
        data_trx_ix = stack_datum.find { |data| !!data['trx_ix'] }
        @trx_id = data_trx_ix['trx_ix'] if !!data_trx_ix
        
        stack_formats.reject(&:empty?).join('; ')
      else
        @error_code ||= @error['code']
        @error['message']
      end
      
      @api_name, @api_method, @api_params = if !!data_call_method
        @api_name = data_call_method['call.params']
      end
    else
      @error_code = @error['code']
      @error_message = @error['message']
      @expiry = false
      @can_retry = false
      @can_reprepare = false
    end
    
    case @error_code
    when -32603
      if error_match?('Internal Error')
        @expiry = false
        @can_retry = true
        @can_reprepare = true
      end
    when -32003
      if error_match?('Unable to acquire database lock')
        @expiry = false
        @can_retry = true
        @can_reprepare = true
      end
    when -32000
      @expiry = false
      @can_retry = coerce_backtrace
      @can_reprepare = if @api_name == 'network_broadcast_api'
        error_match(REPREPARE_WHITELIST)
      else
        false
      end
    when 10
      @expiry = false
      @can_retry = coerce_backtrace
      @can_reprepare = !!stack_formats && (stack_formats & REPREPARE_WHITELIST).any?
    when 13
      @error_message = @error['data']['message']
      @expiry = false
      @can_retry = false
      @can_reprepare = false
    when 3030000
      @error_message = @error['data']['message']
      @expiry = false
      @can_retry = false
      @can_reprepare = false
    when 4030100
      # Code 4030100 is "transaction_expiration_exception: transaction
      # expiration exception".  If we assume the expiration was valid, the
      # node might be bad and needs to be dropped.
      
      @expiry = true
      @can_retry = true
      @can_reprepare = false
    when 4030200
      # Code 4030200 is "transaction tapos exception".  They are recoverable
      # if the transaction hasn't expired yet.  A tapos exception can be
      # retried in situations where the node is behind and the tapos is
      # based on a block the node doesn't know about yet.
      
      @expiry = false
      @can_retry = true
      
      # Allow fall back to reprepare if retry fails.
      @can_reprepare = true
    else
      @expiry = false
      @can_retry = false
      @can_reprepare = false
    end
  rescue => e
    if defined? ap
      if ENV['DEBUG'] == 'true'
        ap error_parser_exception: e, original_response: response, backtrace: e.backtrace
      else
        ap error_parser_exception: e, original_response: response
      end
    end
    
    @expiry = false
    @can_retry = false
    @can_reprepare = false
  end
end
to_s() click to toggle source
# File lib/rubybear/error_parser.rb, line 216
def to_s
  if !!error_message && !error_message.empty?
    "#{error_code}: #{error_message}"
  else
    error_code.to_s
  end
end