Overview
ValKeyper implements Redis-compatible transactions using the MULTI/EXEC/DISCARD command sequence. Transactions provide atomic execution of multiple commands, ensuring all commands execute sequentially without interleaving from other clients.Transaction State Management
Each client connection maintains its own transaction state:Fields
TxnStarted: Boolean flag indicating if a transaction is activeTxnQueue: Slice of commands queued for execution- Each queued command is represented as
[]string(command and arguments)
MULTI Command
TheMULTI command initiates a new transaction.
Syntax
Implementation
Behavior
- Sets
TxnStartedflag totrue - Returns
+OKimmediately - Subsequent commands are queued instead of executed
Example
Command Queueing
Once a transaction is started, all commands (except EXEC and DISCARD) are queued:Queueing Logic
- Check if transaction is active (
TxnStarted == true) - Verify command is not EXEC or DISCARD
- Append command to
TxnQueue - Return
+QUEUEDto client - Continue to next command without execution
Example
EXEC Command
TheEXEC command executes all queued commands atomically.
Syntax
Implementation
Execution Flow
-
Validation: Check if transaction is active
- If no active transaction: Return error
- If active: Proceed to execution
-
Sequential Execution: Process each queued command
- Response Collection: Collect all command responses in order
- Array Response: Return all responses as a RESP array
-
State Reset: Set
TxnStarted = false
The transaction queue is NOT explicitly cleared after EXEC. The next MULTI command will create a fresh transaction context.
Example
DISCARD Command
TheDISCARD command cancels a transaction and clears the queue.
Syntax
Implementation
Behavior
- Validation: Verify transaction is active
- Queue Reset: Clear
TxnQueueto empty slice - State Reset: Set
TxnStarted = false - No Execution: Queued commands are discarded without execution
Example
Atomicity Guarantees
ValKeyper provides the following transaction guarantees:1. Sequential Execution
All commands in a transaction execute sequentially without interruption:2. Isolation
Each connection has its own transaction state. Commands from other clients cannot interleave during EXEC.3. No Rollback
4. All-or-Nothing Queueing
Commands are either:- Successfully queued (return
+QUEUED), OR - The transaction hasn’t started (error)
Error Handling
EXEC Without MULTI
DISCARD Without MULTI
Command Errors During EXEC
Errors are returned in the response array at the corresponding position:Transaction Patterns
Counter Increment
Multi-Key Update
Conditional Execution Alternative
Since ValKeyper doesn’t support WATCH, use transactions for simple atomic updates:Limitations
No WATCH
ValKeyper does not implement the WATCH command for optimistic locking.
No Rollback
Failed commands don’t stop execution or rollback changes.
Single-Threaded
Transaction execution is single-threaded per connection but not globally.
No Persistence
Transactions are not logged for crash recovery.
Performance Considerations
Memory Usage
The transaction queue stores full command arrays in memory:- Number of commands
- Size of command arguments
Execution Time
All commands execute synchronously during EXEC:- Long-running commands block the connection
- No parallelization within a transaction
- Other clients can still execute on their connections
Best Practices
- Keep Transactions Short: Minimize the number of queued commands
- Avoid Large Arguments: Large values increase memory usage
- Handle Errors: Check each response in the EXEC array
- Use for Atomicity: Only use transactions when atomicity is required
Implementation Reference
Key source locations instore.go:
- Connection struct: Lines 20-24
- MULTI handler: Lines 523-525
- Command queueing: Lines 151-158
- EXEC handler: Lines 526-539
- DISCARD handler: Lines 540-547