Katkat

Design a transaction class for deposits/withdrawals

Capital OneSoftware Engineer · Technical Screen

Last updated

This problem tests your ability to design a robust, thread-safe class for managing bank account transactions, focusing on atomicity and data consistency.

Problem Statement

Design a BankAccount class that supports deposits, withdrawals, and retrieving a transaction history. The class must be thread-safe, ensuring that multiple concurrent operations do not lead to race conditions or inconsistent states.

Requirements

  1. deposit(amount): Adds funds to the balance. Must be a positive value.
  2. withdraw(amount): Subtracts funds if the balance is sufficient. Must be a positive value.
  3. get_balance(): Returns the current balance.
  4. get_transaction_history(): Returns a chronological list of all successful transactions.

Constraints

  • The system must handle concurrent calls from multiple threads.
  • Withdrawals must fail (or return an error) if the amount exceeds the get_balance().
  • You must maintain a record of all operations for the history.

Example

account = BankAccount(initial_balance=100)
account.deposit(50)    # Balance: 150
account.withdraw(20)   # Balance: 130
account.withdraw(200)  # Should handle gracefully (e.g., return False or raise Exception)
print(account.get_transaction_history())
# Output: [{'type': 'deposit', 'amount': 50}, {'type': 'withdrawal', 'amount': 20}]

Edge Cases to Consider

  • Negative input values.
  • Concurrent withdrawals attempting to drain the account simultaneously.
  • Ensuring the balance update and history entry are atomic.

Hints

Hint 1

Use a reentrant lock or a primitive mutex to protect the shared state during read-modify-write operations.

Hint 2

Consider using an immutable data structure for each transaction record to prevent external modification of the history.

Hint 3

Think about the difference between locking the entire class instance versus using atomic primitives like Compare-And-Swap (CAS).

Follow-up questions

  • How would you modify your design if you needed to support atomic transfers between two different BankAccount instances?
  • How would this design change if you needed to persist the transaction history to a database while ensuring zero data loss during system crashes?
  • If the system expects thousands of concurrent transactions per second, how would you optimize to reduce lock contention?