Process a Timestamped Workspace Credit Ledger
AirbnbSoftware Engineer · Online AssessmentLast updated
Design and implement an in-memory timestamped credit ledger that supports deposits, transfers, spending with delayed cashback rewards, account merges, balance queries, and spend rankings.
Problem Statement
Design an in-memory credit ledger for corporate workspaces. Workspaces hold integer credit balances and perform various financial transactions over time. Events arrive in non-decreasing order of their timestamp.
Implement the WorkspaceLedger class:
-
deposit(workspace_id: str, amount: int, timestamp: int) -> bool: Addsamountcredits toworkspace_id. If the workspace does not exist, it is created. ReturnsTrue. -
spend(workspace_id: str, amount: int, cashback_percent: int, delay: int, timestamp: int) -> bool: Deductsamountcredits fromworkspace_idif its available balance attimestampis at leastamount. If successful, schedules a delayed cashback offloor(amount * cashback_percent / 100)credits to mature attimestamp + delay. ReturnsTrueon success, orFalseif the workspace does not exist or has insufficient funds. -
transfer(from_id: str, to_id: str, amount: int, timestamp: int) -> bool: Transfersamountfromfrom_idtoto_idiffrom_idhas sufficient balance attimestampand both IDs are valid and resolve to different workspaces. ReturnsTrueon success,Falseotherwise. -
merge(source_id: str, target_id: str, timestamp: int) -> bool: Mergessource_idintotarget_id. All current balance, pending cashback rewards, and historical cumulative spend fromsource_idare permanently transferred totarget_id. Future operations targetingsource_idautomatically resolve totarget_id. ReturnsFalseif either workspace does not exist or if both IDs resolve to the same workspace; otherwise returnsTrue. -
get_balance(workspace_id: str, timestamp: int) -> int: Returns the available credit balance ofworkspace_idattimestamp(including all cashbacks that matured on or beforetimestamp). Returns-1if the workspace does not exist. -
get_top_spenders(k: int, timestamp: int) -> list[str]: Returns the canonical IDs of the topkworkspaces sorted by cumulative amount spent up to and includingtimestampin descending order. Break ties by canonical workspace ID in lexicographical ascending order. If fewer thankworkspaces have spent credits, return all of them.
Constraints
- Timestamps across all method calls are non-decreasing:
1 <= timestamp <= 10^9. 1 <= amount <= 10^60 <= cashback_percent <= 1001 <= delay <= 10^61 <= k <= 100- Up to
50,000total method calls. - All workspace IDs consist of alphanumeric characters (length 1 to 32).
Example
ledger = WorkspaceLedger()
ledger.deposit("team-a", 100, 1) # True, team-a balance = 100
ledger.spend("team-a", 40, 10, 5, 2) # True, balance = 60; 4 cashback at t=7
ledger.get_balance("team-a", 4) # 60 (cashback not yet matured)
ledger.get_balance("team-a", 7) # 64 (cashback matured)
ledger.deposit("team-b", 50, 8) # True
ledger.merge("team-a", "team-b", 9) # True, team-b balance = 64 + 50 = 114
ledger.get_balance("team-a", 10) # 114 (redirected to team-b)
ledger.get_top_spenders(2, 10) # ["team-b"]
Hints
Hint 1
Use a Disjoint Set Union (DSU) or alias map to handle account merges and redirect operations to canonical workspace IDs.
Hint 2
Maintain a min-heap or priority queue for pending cashbacks ordered by maturity timestamp, processing matured cashbacks lazily up to the query timestamp.
Hint 3
Track cumulative spending per canonical workspace so that merges combine spend totals in O(1) time while keeping top-k extraction efficient via sorting or a heap.
Follow-up questions
- How would you support out-of-order event ingestion where timestamps are not strictly non-decreasing?
- If historical queries could ask for `get_balance` at an arbitrary past timestamp `t < current_time`, how would you restructure your storage model?
- How would you make this ledger crash-resilient and idempotent in a distributed environment with duplicate event deliveries?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.