Katkat

Implement held transfers with accept/cancel

MetaSoftware Engineer · Online Assessment

Last updated

This interview question evaluates requirements, scale assumptions, API/data design, architecture, trade-offs, failure modes, and rollout in a realistic interview setting. A strong answer for Implement held transfers with accept/cancel states assumptions, handles edge cases, explains trade-offs, and shows how to validate the result clearly.

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.