Module: ActiveRecord::ConnectionAdapters::CockroachDB::TransactionManagerMonkeyPatch

Included in:
TransactionManager
Defined in:
lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb

Instance Method Summary collapse

Instance Method Details

#retryable?(error) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 48

def retryable?(error)
  return true if serialization_error?(error)
  return true if error.is_a? ActiveRecord::SerializationFailure
  return retryable? error.cause if error.cause
  false
end

#serialization_error?(error) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
58
59
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 55

def serialization_error?(error)
  errors = [error]
  errors << error.cause if error.cause
  errors.any? {|e| e.is_a? PG::TRSerializationFailure }
end

#sleep_rand_seconds(attempts) ⇒ Object



61
62
63
64
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 61

def sleep_rand_seconds(attempts)
  sleep_seconds = (2 ** attempts + rand) / 10
  sleep(sleep_seconds)
end

#within_new_transaction(isolation: nil, joinable: true, attempts: 0) ⇒ Object

Capture ActiveRecord::SerializationFailure errors caused by transactions that fail due to serialization errors. Failed transactions will be retried until they pass or the max retry limit is exceeded.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 25

def within_new_transaction(isolation: nil, joinable: true, attempts: 0)
  super(isolation: isolation, joinable: joinable)
rescue ActiveRecord::ConnectionNotEstablished => error
  raise unless retryable? error
  raise if attempts >= @connection.max_transaction_retries

  sleep_rand_seconds(attempts)

  unless @connection.active?
    warn "connection isn't active, reconnecting"
    @connection.reconnect!
  end

  within_new_transaction(isolation: isolation, joinable: joinable, attempts: attempts + 1) { yield }
rescue ActiveRecord::StatementInvalid => error
  raise unless retryable? error
  raise if attempts >= @connection.max_transaction_retries

  sleep_rand_seconds(attempts)

  within_new_transaction(isolation: isolation, joinable: joinable, attempts: attempts + 1) { yield }
end