Module: Familia::Connection::TransactionCore
- Defined in:
- lib/familia/connection/transaction_core.rb
Overview
Core transaction logic shared between global and instance transaction methods
This module provides unified transaction handling with configurable fallback behavior when transactions are unavailable due to connection handler constraints. Eliminates code duplication between Operations and Horreum Connection modules.
Transaction Safety Rules
Rule 1: No Save Operations Inside Transactions
The following methods CANNOT be called within a transaction context:
save,save!,save_if_not_exists!,create!These methods require reading current state for validation, which would return uninspectable Redis::Future objects inside transactions.
Rule 2: Reentrant Transaction Behavior
Nested transaction calls reuse the same connection and do not create new MULTI/EXEC blocks. This ensures atomicity across nested operations.
Rule 3: Read Operations Return Futures
Inside transactions, read operations return Redis::Future objects that cannot be inspected until the transaction completes. Always check conditions before entering the transaction.
Rule 4: Connection Handler Compatibility
- FiberTransactionHandler: Supports reentrant transactions
- ProviderConnectionHandler: Full transaction support
- CreateConnectionHandler: Full transaction support
- FiberConnectionHandler: Blocked (raises OperationModeError)
- DefaultConnectionHandler: Blocked (raises OperationModeError)
Class Method Summary collapse
-
.execute_normal_transaction(dbclient_proc) ⇒ MultiResult
Executes a normal Redis transaction using MULTI/EXEC.
-
.execute_transaction(dbclient_proc) {|Redis| ... } ⇒ MultiResult
Executes a transaction with configurable fallback behavior.
-
.handle_transaction_fallback(dbclient_proc, handler_class) ⇒ MultiResult
Handles transaction fallback based on configured transaction mode.
Class Method Details
.execute_normal_transaction(dbclient_proc) ⇒ MultiResult
Executes a normal Redis transaction using MULTI/EXEC
Handles the standard transaction flow including nested transaction detection, proper Fiber-local state management, and cleanup in ensure blocks.
Implementation Details
- Uses Fiber[:familia_transaction] to track active transaction connection
- Reentrant behavior: yields existing connection if already in transaction
- All commands queued and executed atomically on EXEC
- Returns MultiResult with success status and command results
Thread Safety
Each thread has its own root fiber with isolated fiber-local storage, ensuring transactions don't interfere across threads.
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/familia/connection/transaction_core.rb', line 147 def self.execute_normal_transaction(dbclient_proc) # Check for existing transaction context return yield(Fiber[:familia_transaction]) if Fiber[:familia_transaction] command_return_values = dbclient_proc.call.multi do |conn| Fiber[:familia_transaction] = conn begin yield(conn) ensure Fiber[:familia_transaction] = nil end end # Return same MultiResult format as other methods MultiResult.new(command_return_values) end |
.execute_transaction(dbclient_proc) {|Redis| ... } ⇒ MultiResult
Executes a transaction with configurable fallback behavior
Handles three transaction scenarios based on connection handler capabilities:
- Normal transaction (MULTI/EXEC) when handler supports transactions
- Reentrant transaction when already within a transaction context
- Individual command execution with configurable error/warn/silent modes
Safety Mechanisms
- Fiber-local storage tracks transaction state across nested calls
- Connection handler validation prevents unsafe transaction usage
- Automatic cleanup ensures proper state management even on exceptions
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/familia/connection/transaction_core.rb', line 95 def self.execute_transaction(dbclient_proc, &) # First, get the connection to populate the handler class dbclient_proc.call handler_class = Fiber[:familia_connection_handler_class] # Check transaction capability transaction_capability = handler_class&.allows_transaction if transaction_capability == false handle_transaction_fallback(dbclient_proc, handler_class, &) elsif transaction_capability == :reentrant # Already in transaction, just yield the connection yield(Fiber[:familia_transaction]) else # Normal transaction flow (includes nil, true, and other values) execute_normal_transaction(dbclient_proc, &) end end |
.handle_transaction_fallback(dbclient_proc, handler_class) ⇒ MultiResult
Handles transaction fallback based on configured transaction mode
Delegates to OperationCore.handle_fallback for consistent behavior across transaction and pipeline operations.
124 125 126 |
# File 'lib/familia/connection/transaction_core.rb', line 124 def self.handle_transaction_fallback(dbclient_proc, handler_class, &) OperationCore.handle_fallback(:transaction, dbclient_proc, handler_class, &) end |