MediumStateful DesignPython 3
Idempotent Ledger
Apply in-memory account mutations once per successful request ID while preserving the original retry result.
30m3 sample tests5 hidden tests
Implement a small account ledger that applies deposits and withdrawals exactly once per request ID. Idempotency means a retry returns the first successful result without repeating its mutation, even when the account balance changes afterward.
Requirements
- Define
Ledger. apply(request_id, account_id, amount)appliesamountto the account and returns the resulting balance.- Positive amounts are deposits. Negative amounts are withdrawals.
- Amounts are integer units. A zero amount is a successful request and must also be recorded.
- Request IDs are global to the ledger (not scoped per account).
- A duplicate
request_idreturns the same result as the first successful application, not the account's current balance, and must not apply twice. - On a duplicate
request_id, ignore the retry payload (account_id/amount) and return the stored result. - Only successful applications are recorded for idempotency. Failed overdrafts are not recorded, so a later retry with the same id may still succeed after the balance changes.
- Withdrawals that would make the balance negative raise
ValueError. balance(account_id)returns the current account balance, defaulting to zero.
Example
python
ledger = Ledger()
assert ledger.apply("d1", "acct", 100) == 100
assert ledger.apply("d1", "acct", 100) == 100
assert ledger.apply("w1", "acct", -30) == 70
assert ledger.apply("d1", "other-acct", -999) == 100 # original receipt, despite changed payload
assert ledger.balance("acct") == 70
assert ledger.balance("other-acct") == 0Constraints
- Use standard-library Python only.
- Keep all state in memory.
- Calls are sequential, and idempotency lasts for this ledger instance's lifetime. Restart recovery and concurrency are follow-ups.
- For the Java version, assume amounts and resulting balances fit signed 32-bit integers.
Editor