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)


34
35
36
37
38
39
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 34

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)


41
42
43
44
45
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 41

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



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

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.



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 11

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