Class: ActiveRecordSpannerAdapter::Transaction
- Inherits:
-
Object
- Object
- ActiveRecordSpannerAdapter::Transaction
- Defined in:
- lib/activerecord_spanner_adapter/transaction.rb
Instance Attribute Summary collapse
-
#state ⇒ Object
readonly
Returns the value of attribute state.
Instance Method Summary collapse
- #_transaction_isolation_level_to_grpc(isolation) ⇒ Object
- #active? ⇒ Boolean
-
#begin ⇒ Object
Begins the transaction.
- #buffer(mutation) ⇒ Object
- #commit ⇒ Object
-
#force_begin_read_write ⇒ Object
Forces a BeginTransaction RPC for a read/write transaction.
-
#grpc_transaction=(grpc) ⇒ Object
Sets the underlying gRPC transaction to use for this Transaction.
-
#initialize(connection, isolation) ⇒ Transaction
constructor
A new instance of Transaction.
- #isolation ⇒ Object
- #mark_aborted ⇒ Object
- #next_sequence_number ⇒ Object
- #rollback ⇒ Object
- #shoot_and_forget_rollback ⇒ Object
- #transaction_selector ⇒ Object
Constructor Details
#initialize(connection, isolation) ⇒ Transaction
Returns a new instance of Transaction.
11 12 13 14 15 16 17 18 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 11 def initialize connection, isolation @connection = connection @isolation = isolation @committable = ![:read_only, :pdml].include?(isolation) && !isolation.is_a?(Hash) @state = :INITIALIZED @sequence_number = 0 @mutations = [] end |
Instance Attribute Details
#state ⇒ Object (readonly)
Returns the value of attribute state.
9 10 11 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 9 def state @state end |
Instance Method Details
#_transaction_isolation_level_to_grpc(isolation) ⇒ Object
79 80 81 82 83 84 85 86 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 79 def _transaction_isolation_level_to_grpc isolation case isolation when :serializable Google::Cloud::Spanner::V1::TransactionOptions::IsolationLevel::SERIALIZABLE when :repeatable_read Google::Cloud::Spanner::V1::TransactionOptions::IsolationLevel::REPEATABLE_READ end end |
#active? ⇒ Boolean
20 21 22 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 20 def active? @state == :STARTED end |
#begin ⇒ Object
Begins the transaction.
Read-only and PDML transactions are started by executing a BeginTransaction RPC. Read/write transactions are not really started by this method, and instead a transaction selector is prepared that will be included with the first statement on the transaction.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 39 def begin raise "Nested transactions are not allowed" if @state != :INITIALIZED begin case @isolation when Hash if @isolation[:timestamp] @grpc_transaction = @connection.session.create_snapshot timestamp: @isolation[:timestamp] elsif @isolation[:staleness] @grpc_transaction = @connection.session.create_snapshot staleness: @isolation[:staleness] elsif @isolation[:strong] @grpc_transaction = @connection.session.create_snapshot strong: true else raise "Invalid snapshot argument: #{@isolation}" end when :read_only @grpc_transaction = @connection.session.create_snapshot strong: true when :pdml @grpc_transaction = @connection.session.create_pdml else grpc_isolation = _transaction_isolation_level_to_grpc @isolation @begin_transaction_selector = Google::Cloud::Spanner::V1::TransactionSelector.new \ begin: Google::Cloud::Spanner::V1::TransactionOptions.new( read_write: Google::Cloud::Spanner::V1::TransactionOptions::ReadWrite.new, isolation_level: grpc_isolation ) end @state = :STARTED rescue Google::Cloud::NotFoundError => e if @connection.session_not_found? e @connection.reset! retry end @state = :FAILED raise rescue StandardError @state = :FAILED raise end end |
#buffer(mutation) ⇒ Object
29 30 31 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 29 def buffer mutation @mutations << mutation end |
#commit ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 98 def commit raise "This transaction is not active" unless active? begin # Start a transaction with an explicit BeginTransaction RPC if the transaction only contains mutations. force_begin_read_write if @committable && !@mutations.empty? && !@grpc_transaction @connection.session.commit_transaction @grpc_transaction, @mutations if @committable && @grpc_transaction @state = :COMMITTED rescue Google::Cloud::NotFoundError => e if @connection.session_not_found? e shoot_and_forget_rollback @connection.reset! @connection.raise_aborted_err end @state = :FAILED raise rescue StandardError @state = :FAILED raise end end |
#force_begin_read_write ⇒ Object
Forces a BeginTransaction RPC for a read/write transaction. This is used by a connection if the first statement of a transaction failed.
90 91 92 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 90 def force_begin_read_write @grpc_transaction = @connection.session.create_transaction end |
#grpc_transaction=(grpc) ⇒ Object
Sets the underlying gRPC transaction to use for this Transaction. This is used for queries/DML statements that inlined the BeginTransaction option and returned a transaction in the metadata.
147 148 149 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 147 def grpc_transaction= grpc @grpc_transaction = Google::Cloud::Spanner::Transaction.from_grpc grpc, @connection.session end |
#isolation ⇒ Object
24 25 26 27 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 24 def isolation return nil unless active? @isolation end |
#mark_aborted ⇒ Object
140 141 142 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 140 def mark_aborted @state = :ABORTED end |
#next_sequence_number ⇒ Object
94 95 96 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 94 def next_sequence_number @sequence_number += 1 if @committable end |
#rollback ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 121 def rollback # Allow rollback after abort and/or a failed commit. raise "This transaction is not active" unless active? || @state == :FAILED || @state == :ABORTED if active? && @grpc_transaction # We do a shoot-and-forget rollback here, as the error that caused the transaction to be rolled back could # also have invalidated the transaction (e.g. `Session not found`). If the rollback fails for any other # reason, we also do not need to retry it or propagate the error to the application, as the transaction will # automatically be aborted by Cloud Spanner after 10 seconds anyways. shoot_and_forget_rollback end @state = :ROLLED_BACK end |
#shoot_and_forget_rollback ⇒ Object
134 135 136 137 138 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 134 def shoot_and_forget_rollback @connection.session.rollback @grpc_transaction.transaction_id if @committable rescue StandardError # Ignored end |
#transaction_selector ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/activerecord_spanner_adapter/transaction.rb', line 151 def transaction_selector return unless active? # Use the transaction that has been started by a BeginTransaction RPC or returned by a # statement, if present. return Google::Cloud::Spanner::V1::TransactionSelector.new id: @grpc_transaction.transaction_id \ if @grpc_transaction # Return a transaction selector that will instruct the statement to also start a transaction # and return its id as a side effect. @begin_transaction_selector end |