Implement Expiration-Aware Retrieval Across Storage Levels
OptiverSoftware Engineer · HR ScreenLast updated
Design and implement a multi-level storage system that purges expired items, filters for under-utilized storage tiers, and deterministically retrieves the highest-weight item.
Problem Statement
In high-frequency trading infrastructure, multi-tiered staging buffers store pending outbound messages or cached market-state snapshots across different memory tiers.
You are tasked with implementing an expiration-aware multi-level storage manager, MultiLevelStorage.
The system is initialized with an ordered list of storage levels. Levels defined earlier in the list have strictly higher priority than those defined later. Each level has a fixed maximum capacity representing the maximum number of items it can hold.
Implement the following methods:
-
__init__(levels: list[tuple[int, int]]): Initializes the storage with a list of(level_id, capacity)pairs. Order inlevelsdefines priority (index0is highest priority). Alllevel_idvalues are unique andcapacity >= 1. -
insert(level_id: int, item_id: str, weight: int, expiry: int) -> bool: Inserts an item with unique identifieritem_id, positive integerweight, and expiration timestampexpiryintolevel_id. Iflevel_iddoes not exist, or the level is currently at full capacity, the insertion fails and returnsFalse. Otherwise, stores the item and returnsTrue. -
retrieve(current_time: int) -> str | None: Executes a retrieval operation following these deterministic steps:- Purge Expired: Across all levels, discard any item where
expiry <= current_time. - Filter Eligible Levels: A level is eligible if and only if it is at least half free after purging:
current_item_count <= capacity / 2. - Select Best Item: Among all items residing in eligible levels, select the item with the maximum
weight.- Tie-breaker 1: Choose the item in the higher-priority level (smaller initial index in
levels). - Tie-breaker 2: Choose the item inserted earlier (earliest insertion order).
- Tie-breaker 1: Choose the item in the higher-priority level (smaller initial index in
- Remove and Return: Remove the selected item from its level and return its
item_id. If no items exist in eligible levels, returnNone.
- Purge Expired: Across all levels, discard any item where
Example
storage = MultiLevelStorage([(1, 4), (2, 2)])
# Level 1: capacity 4, highest priority
# Level 2: capacity 2, lower priority
storage.insert(1, "A", weight=10, expiry=100) # count=1 4/2 (ineligible)
storage.insert(2, "D", weight=25, expiry=100) # count=1 <= 2/2 (eligible)
# At t=40, Level 1 has 3 items (ineligible). Level 2 has 1 item (eligible).
# Only Level 2 is eligible, so "D" is returned.
assert storage.retrieve(40) == "D"
# At t=60, Level 1 item "B" has expired (expiry=50 Level 1 is now eligible.
# Level 2 is empty.
# Heaviest item in Level 1 is "C".
assert storage.retrieve(60) == "C"
Constraints
- Number of levels
L <= 100. - Level capacity
1 <= capacity <= 1,000. 1 <= weight <= 10^9.- Timestamps (
expiry,current_time) are non-negative integers up to10^9.current_timein calls toretrieveis non-decreasing. - Total calls to
insertandretrievecombined<= 50,000.
Hints
Hint 1
Ensure that expired items are purged before evaluating whether a level satisfies the `current_count <= capacity / 2` condition.
Hint 2
Since `current_time` is non-decreasing, maintaining items ordered by expiration time allows for efficient batch eviction at each step.
Hint 3
Keep track of a global sequence counter for each `insert` operation to implement stable tie-breaking for items with identical weights.
Follow-up questions
- How would you optimize `retrieve` if the number of levels and items per level were scaled up to millions?
- If `current_time` could arrive out of order, how would you restructure your eviction and eligibility indexing?
- How could this class be made thread-safe for concurrent inserts and retrievals without bottlenecking on a single global lock?
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.