Read Current and Historical Field Values with Expiration
MetaMachine Learning Engineer · Online AssessmentLast updated
Design an in-memory key-field-value store that supports historical queries, time-to-live (TTL) expiration, and field deletions without corrupting past states.
Problem Statement
Implement an in-memory database that stores values associated with a composite key of (key, field). The store must support write operations with optional time-to-live (TTL) expiration, deletions, and point-in-time historical reads.
Historical reads should accurately reflect the state of any (key, field) at the queried timestamp: if a field was active at that timestamp, its value should be returned; if it had expired, had been deleted, or had not yet been set, the query should indicate it was not present.
Implement the HistoricalFieldStore class with the following methods:
__init__(): Initializes the data store.set_field(key: str, field: str, value: str, timestamp: int, ttl: int | None = None) -> None: Sets the field tovalueat the giventimestamp. Ifttlis provided (wherettl > 0), the value is valid for the half-open interval[timestamp, timestamp + ttl). IfttlisNone, the value does not expire automatically. If a previous value was active attimestamp, its validity ends attimestamp.delete_field(key: str, field: str, timestamp: int) -> bool: Deletes the specified field attimestamp. ReturnsTrueif the field was active immediately prior totimestamp, orFalseif the field did not exist, was already deleted, or had already expired.get_field(key: str, field: str, timestamp: int) -> str | None: Returns the value offieldunderkeyat the specifiedtimestamp. If the field was not set, had expired, or was deleted as oftimestamp, returnNone.
Constraints
- Calls to
set_fieldanddelete_fieldare guaranteed to have strictly increasingtimestampvalues across all operations. - Calls to
get_fieldmay query any integertimestamp >= 1in any order. 1 <= timestamp, ttl <= 10^9key,field, andvalueconsist of alphanumeric characters and have lengths between1and50.- At most
10^5total calls will be made across all methods.
Examples
Example 1
store = HistoricalFieldStore()
# Set user:1 role to 'engineer' at t=10 with TTL=10 (valid for t in [10, 20))
store.set_field("user:1", "role", "engineer", timestamp=10, ttl=10)
# Query before set
assert store.get_field("user:1", "role", timestamp=5) is None
# Query during validity
assert store.get_field("user:1", "role", timestamp=10) == "engineer"
assert store.get_field("user:1", "role", timestamp=19) == "engineer"
# Query at and after expiration
assert store.get_field("user:1", "role", timestamp=20) is None
assert store.get_field("user:1", "role", timestamp=25) is None
Example 2
store = HistoricalFieldStore()
store.set_field("doc:42", "status", "draft", timestamp=100) # No TTL
store.set_field("doc:42", "status", "published", timestamp=200, ttl=50) # valid [200, 250)
assert store.get_field("doc:42", "status", timestamp=150) == "draft"
assert store.get_field("doc:42", "status", timestamp=200) == "published"
assert store.get_field("doc:42", "status", timestamp=249) == "published"
assert store.get_field("doc:42", "status", timestamp=250) is None
# Historical draft is still readable in its original active window
assert store.get_field("doc:42", "status", timestamp=199) == "draft"
# Deleting after expiration returns False
assert store.delete_field("doc:42", "status", timestamp=300) is False
Edge Cases
- Querying at exactly
timestamp + ttl: The entry should be considered expired (exclusive upper bound). - Overwriting an entry before its TTL expires: The previous entry's expiration must be capped at the new entry's start timestamp.
- Deleting a field that was set without a TTL vs. deleting one that already expired naturally.
Hints
Hint 1
Model each write as an interval `[start_time, end_time, value]` where `end_time` is determined by TTL, deletion, or a subsequent write.
Hint 2
Because mutation timestamps strictly increase, you can maintain a sorted list of version intervals for each `(key, field)` and close the previous interval upon a new write or delete.
Hint 3
Use binary search (`bisect_right`) on the version list of the target field to find the relevant interval in `O(log V)` time for `get_field`.
Follow-up questions
- How would your data structure and locking strategy change if mutations could arrive out of chronological order?
- If the dataset exceeds memory and old historical versions are queried infrequently, how would you design a tiered storage compaction policy?
- How would you efficiently implement a `get_all_fields(key: str, timestamp: int) -> dict[str, str]` operation without scanning every field ever registered?
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.