Concurrent Transaction Processing System
This challenge asks you to design and implement a simplified transaction processing system in Go. The system should handle concurrent transactions safely, ensuring data consistency even when multiple transactions attempt to modify the same data simultaneously. This is a fundamental problem in distributed systems and databases, and understanding its implementation is crucial for building robust applications.
Problem Description
You are tasked with building a system that manages a bank account balance. The system should allow concurrent transactions (deposits and withdrawals) to be processed safely. The core requirement is to ensure that the balance remains consistent even with multiple concurrent operations.
Specifically, you need to implement the following:
BankAccountstruct: This struct represents a bank account and contains a single field:balance(float64).Deposit(amount float64)method: This method adds the givenamountto the account balance. It must be thread-safe.Withdraw(amount float64)method: This method subtracts the givenamountfrom the account balance. It must be thread-safe and should return an error if the withdrawal would result in a negative balance.GetBalance() float64method: This method returns the current account balance. It should be thread-safe.TransactionProcessorstruct: This struct encapsulates theBankAccountand provides a thread-safe interface for processing transactions. It should handle concurrent access to the account balance.ProcessTransaction(transactionType string, amount float64) float64method: This method takes a transaction type ("deposit" or "withdrawal") and an amount as input. It performs the corresponding operation on the bank account and returns the updated balance. It should return an error if the transaction type is invalid.
Examples
Example 1:
Input:
BankAccount: balance = 100.0
Concurrent Transactions:
- Deposit(50.0)
- Withdrawal(25.0)
Output:
BankAccount: balance = 125.0
Explanation: The deposit adds 50.0 to the balance (100.0 + 50.0 = 150.0). The withdrawal then subtracts 25.0 (150.0 - 25.0 = 125.0). The order of operations is not guaranteed, but the final balance must be 125.0.
Example 2:
Input:
BankAccount: balance = 50.0
Transaction: Withdrawal(75.0)
Output:
BankAccount: balance = 50.0
Error: "insufficient funds"
Explanation: The withdrawal amount (75.0) is greater than the current balance (50.0). The withdrawal is rejected, and the balance remains unchanged. An error is returned.
Example 3: (Concurrent Scenario)
Input:
BankAccount: balance = 200.0
Concurrent Transactions:
- Withdrawal(100.0)
- Deposit(75.0)
- Withdrawal(50.0)
Output:
BankAccount: balance = 125.0
Explanation: Even with concurrent operations, the final balance must be 125.0. The system must prevent race conditions and ensure that the balance is updated correctly.
Constraints
balancemust always be a non-negative number.amountinDepositandWithdrawmust be positive numbers.- The system must handle at least 10 concurrent transactions without data corruption or deadlocks. (This is a functional requirement, not a performance benchmark).
- Transaction types must be case-insensitive ("deposit" or "withdrawal").
- Error messages should be descriptive and informative.
Notes
- Use Go's built-in
syncpackage (e.g.,sync.Mutex) to ensure thread safety. - Consider using a
WaitGroupto wait for all transactions to complete. - Focus on correctness and thread safety. Performance optimization is not the primary goal.
- Think about how to handle potential race conditions when multiple goroutines are accessing and modifying the
balanceconcurrently. A mutex is a good starting point. - Error handling is important. Return meaningful errors to indicate failures.
- The
ProcessTransactionmethod should not directly modify theBankAccount's balance. It should call theDepositorWithdrawmethods.