Design an In‑Memory Bank System (Technical Screen)
You are designing an in‑memory bank ledger that supports real‑time withdrawals and scheduled payments. The system runs in a single process (single‑threaded by default) and does not persist to disk. All balances and amounts are integers.
APIs
-
createAccount(accountId, initialBalance = 0) -> void
-
Create a new account with the given initialBalance.
-
deposit(accountId, amount) -> bool
-
Increase balance by amount. Return false if accountId does not exist or amount <= 0.
-
withdraw(accountId, amount) -> bool
-
Decrease balance by amount. Reject if insufficient funds or invalid input. On success, increase the account’s cumulativeTransferredOut.
-
getTopSpenders(n) -> List
<accountId>
-
Return the n accounts with the highest cumulativeTransferredOut. Break ties by smaller accountId. Results must reflect all successful withdraws and executed scheduled payments at the time of the call.
-
schedulePayment(accountId, amount, timestamp, delay) -> paymentId
-
Schedule a withdrawal to execute at executeAt = timestamp + delay (timestamps are integers; delay >= 0). The payment counts toward cumulativeTransferredOut only upon successful execution.
-
cancel(paymentId) -> bool
-
Cancel a not‑yet‑executed payment.
-
process(nowTimestamp) -> string
-
Execute all due scheduled payments with executeAt <= nowTimestamp in ascending executeAt order; when executeAt ties, order by increasing paymentId. For each successfully executed payment, update balances and cumulativeTransferredOut. Return a single string that concatenates executed accounts’ IDs in that exact order, separated by commas (return an empty string if none executed).
Edge Cases and Rules
-
Amounts are positive integers; balances cannot go negative.
-
Failed executions (e.g., insufficient funds) do not affect cumulativeTransferredOut and are not included in process()’s return string.
-
Repeated operations may interleave arbitrarily. Assume single‑threaded execution unless you choose to discuss concurrency controls.
Complexity Targets
-
create/deposit/withdraw: O(1) average time for lookups and updates.
-
schedule/cancel/execute due payments: O(log M), where M is the number of pending payments.
-
getTopSpenders(n) should be efficient for variable n queries; propose and justify a data structure and trade‑offs.
Deliverables
-
Specify data structures, method signatures, and invariants.
-
Explain algorithms and their complexities.
-
Describe how you would test correctness, handle idempotency (e.g., duplicate payment scheduling), and recover from partial failures (optional).