Level 1
The basic level of the in-memory database contains records. Each record can be accessed with a unique identifier key of string type. A record may contain several field–value pairs, both of which are of string type.
set(self, key: str, field: str, value: str) -> Noneβ should insert afield–valuepair to the record associated withkey. If thefieldin the record already exists, replace the existing value with the specifiedvalue. If the record does not exist, create a new one.get(self, key: str, field: str) -> str | Noneβ should return the value contained withinfieldof the record associated withkey. If the record or thefielddoesn’t exist, should returnNone.delete(self, key: str, field: str) -> boolβ should remove thefieldfrom the record associated withkey. ReturnsTrueif the field was successfully deleted, andFalseif thekeyor thefielddo not exist in the database.
Examples
The example below shows how these operations should work (the section is scrollable to the right):
Level 2
The database should support displaying data based on filters. Introduce an operation to support printing some fields of a record.
scan(self, key: str) -> list[str]β should return a list of strings representing the fields of a record associated withkey. The returned list should be in the following format["<field_1>(<value_1>)", ...], where fields are sorted lexicographically. If the specified record does not exist, returns an empty list.scan_by_prefix(self, key: str, prefix: str) -> list[str]β should return a list of strings representing some fields of a record associated withkey. Specifically, only fields that start withprefixshould be included. The returned list should be in the same format as in thescanoperation with fields sorted in lexicographical order.
Examples
The example below shows how these operations should work (the section is scrollable to the right):
Level 3
Support the timeline of operations and TTL (Time-To-Live) settings for records and fields. Each operation from previous levels now has an alternative version with a timestamp parameter to represent when the operation was executed. For each field-value pair in the database, the TTL determines how long that value will persist before being removed.
Notes:
- Time should always flow forward, so timestamps are guaranteed to strictly increase as operations are executed.
- Each test cannot contain both versions of operations (with and without timestamp). However, you should maintain backward compatibility, so all previously defined methods should work in the same way as before.
set_at(self, key: str, field: str, value: str, timestamp: int) -> Noneβ should insert a field-value pair or updates the value of the field in the record associated withkey.set_at_with_ttl(self, key: str, field: str, value: str, timestamp: int, ttl: int) -> Noneβ should insert a field-value pair or update the value of the field in the record associated withkey. Also sets its Time-To-Live starting attimestampto bettl. Thettlis the amount of time that this field-value pair should exist in the database, meaning it will be available during this interval:[timestamp, timestamp + ttl).delete_at(self, key: str, field: str, timestamp: int) -> boolβ the same asdelete, but with timestamp of the operation specified. Should returnTrueif the field existed and was successfully deleted andFalseif the key didn’t exist.get_at(self, key: str, field: str, timestamp: int) -> str | Noneβ the same asget, but with timestamp of the operation specified.scan_at(self, key: str, timestamp: int) -> list[str]β the same asscan, but with timestamp of the operation specified.scan_by_prefix_at(self, key: str, prefix: str, timestamp: int) -> list[str]β the same asscan_by_prefix, but with timestamp of the operation specified.
Examples
The examples below show how these operations should work (the section is scrollable to the right):
Level 4
The database should be backed up from time to time. Introduce operations to support backing up and restoring the database state based on timestamps. When restoring, ttl expiration times should be recalculated accordingly.
backup(self, timestamp: int) -> intβ should save the database state at the specified timestamp, including the remainingttlfor all records and fields. Remainingttlis the difference between their initialttland their current lifespan (the duration between the timestamp of this operation and their initial timestamp). Returns the number of non-empty non-expired records in the database.restore(self, timestamp: int, timestamp_to_restore: int) -> Noneβ should restore the database from the latest backup before or attimestamp_to_restore. It’s guaranteed that a backup before or attimestamp_to_restorewill exist. Expiration times for restored records and fields should be recalculated according to the timestamp of this operation β since the database timeline always flows forward, restored records and fields should expire after thetimestampof this operation, depending on their remainingttlat backup.
Examples
The examples below show how these operations should work (the section is scrollable to the right):
Execution time limit: 3 seconds
Memory limit: 4g
This Coinbase OA problem builds an in-memory database in four stages: basic set/get/delete by key and field, sorted scan operations, time-aware updates with TTL expiration, and finally backup/restore with recalculated lifetimes. A robust solution typically uses hash maps for records and fields, tracks expiration timestamps for each value, and filters out expired entries before answering queries. For the backup and restore layer, the important part is preserving enough state in snapshots to rebuild the database consistently at a later timestamp.