Module: ActiveRecord::ConnectionAdapters::CockroachDB::TransactionManagerMonkeyPatch
- Included in:
- TransactionManager
- Defined in:
- lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb
Instance Method Summary collapse
- #retryable?(error) ⇒ Boolean
-
#rollback_transaction(transaction = nil) ⇒ Object
OVERRIDE: the ‘rescue ActiveRecord::StatementInvalid` block is new, see comment.
- #serialization_error?(error) ⇒ Boolean
- #sleep_rand_seconds(attempts) ⇒ Object
-
#within_new_transaction(isolation: nil, joinable: true, attempts: 0) ⇒ Object
Capture ActiveRecord::SerializationFailure errors caused by transactions that fail due to serialization errors.
Instance Method Details
#retryable?(error) ⇒ Boolean
69 70 71 72 73 74 |
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 69 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 |
#rollback_transaction(transaction = nil) ⇒ Object
OVERRIDE: the ‘rescue ActiveRecord::StatementInvalid` block is new, see comment.
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 49 def rollback_transaction(transaction = nil) @connection.lock.synchronize do transaction ||= @stack.last begin transaction.rollback rescue ActiveRecord::StatementInvalid => err # This is important to make Active Record aware the record was not inserted/saved # Otherwise Active Record will assume save was successful and it doesn't retry the transaction # See this thread for more details: # https://github.com/cockroachdb/activerecord-cockroachdb-adapter/issues/258#issuecomment-2256633329 transaction.rollback_records if err.cause.is_a?(PG::NoActiveSqlTransaction) raise ensure @stack.pop if @stack.last == transaction end transaction.rollback_records end end |
#serialization_error?(error) ⇒ Boolean
76 77 78 79 80 |
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 76 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
82 83 84 85 |
# File 'lib/active_record/connection_adapters/cockroachdb/transaction_manager.rb', line 82 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 |