Implement held transfers with accept/cancel
Company: Meta
Role: Software Engineer
Category: System Design
Difficulty: hard
Interview Round: Take-home Project
##### Question
Design and implement a two-phase ("pull-style") money-transfer system in which funds are first **held** on the source account and then either captured or released. The transfer is created by the source; the target decides whether to **accept** it; either party may **cancel** while it is still pending.
Implement the following operations:
1. `String transfer(long timestamp, String targetAccountId, int amount)` — create a transfer, place a **hold** on the source account's funds, and return a unique `transferId`.
2. `boolean accept(long timestamp, String accountId, String transferId)` — finalize the transfer: move the held funds from the source to the target (`accountId` must be the target).
3. `boolean cancel(long timestamp, String accountId, String transferId)` — release the hold and fully restore the source account's availability.
In your design and write-up, address:
- **Balance semantics:** clearly distinguish the **ledger (posted) balance** from the **available balance**, and show how a hold affects each on the source and target accounts.
- **State machine:** define the legal transfer states (e.g. `PENDING → ACCEPTED | CANCELED`, plus an optional `EXPIRED`) and the allowed transitions; terminal states are final.
- **Data structures:** maintain `allTransfers` and `pendingTransfers` in **hash maps (not lists)** so that lookups and state changes are O(1). Track per-account hold totals incrementally instead of scanning lists.
- **Authorization:** only the target may `accept`; the source or the target may `cancel` while pending.
- **Idempotency & duplicate requests:** define behavior for repeated `accept`/`cancel`, for an optional client idempotency key on `transfer`, and for an unknown/invalid `transferId`.
- **Atomicity & concurrency:** how balance, hold, and transfer-state updates are made atomic (per-account locks, deterministic lock ordering to avoid deadlock).
- **Expiration (TTL):** optional hold expiry using `timestamp` — how an expired pending transfer is auto-released and how `accept`/`cancel` behave against it.
- **Failure recovery & durability:** how to keep the system consistent across crashes (ACID transaction, WAL/outbox, background reconciler/sweeper).
- **Error handling:** invalid `transferId`, wrong actor, insufficient funds, non-positive amount, self-transfer.
- **Complexity:** give the time and space complexity of each operation.
Quick Answer: A Meta software-engineer system-design take-home: implement a two-phase money-transfer system with hold, accept, and cancel operations. It tests ledger-vs-available balance semantics, an O(1) hash-map data model, state-machine transitions, idempotency, authorization, atomicity/locking, hold expiration, and crash-safe failure recovery, with time and space complexity for each operation.