PracHub
QuestionsLearningGuidesInterview Prep

Quick Overview

This question evaluates proficiency in designing and implementing mutable in-memory data structures, field-level CRUD and scanning, timestamped operations with TTL semantics, and backup/restore snapshot management, focusing on data structures, temporal reasoning, and state management in the Coding & Algorithms domain.

  • easy
  • xAI
  • Coding & Algorithms
  • Software Engineer

Implement an in-memory database with TTL and backup

Company: xAI

Role: Software Engineer

Category: Coding & Algorithms

Difficulty: easy

Interview Round: Take-home Project

## In-Memory Database (Levels 1–4: TTL and Backup/Restore) Implement an in-memory database that stores **records** identified by a **string key**. Each record contains multiple **string field → string value** pairs. You must support a set of operations that progressively add features. ### Data model - `key` is a string. - Each `key` maps to a set of fields. - Each `field` maps to a `value` (both strings). If an operation refers to a missing `key` or `field`, treat it as absent. > Assumption to make outputs well-defined (typical for OAs): > - `get*` returns `""` (empty string) when absent. > - `delete*` returns `true` if something was deleted, else `false`. > - `scan*` returns an empty list when nothing matches. --- ## Level 1: Basic CRUD on fields Implement: - `set(key, field, value)` - `get(key, field) -> string` - `delete(key, field) -> bool` `set` inserts or overwrites the field’s value. --- ## Level 2: Read-only listing Implement: - `scan(key) -> list[string]` - `scan_by_prefix(key, prefix) -> list[string]` Return format: - Each returned element is formatted as `"field(value)"`. - Results are sorted **lexicographically by `field`**. - `scan_by_prefix` returns only fields whose name starts with `prefix`. --- ## Level 3: Timestamped operations + TTL Add timestamped variants of the above operations. Tests will use **either** timestamped APIs **or** non-timestamped APIs, but **never mix them**. All timestamped operations accept an integer `timestamp`. Implement: - `set_at(key, field, value, timestamp)` - `set_at_with_ttl(key, field, value, timestamp, ttl)` - `get_at(key, field, timestamp) -> string` - `delete_at(key, field, timestamp) -> bool` - `scan_at(key, timestamp) -> list[string]` - `scan_by_prefix_at(key, prefix, timestamp) -> list[string]` TTL semantics: - `set_at_with_ttl` makes the field valid over the half-open interval: - **valid in** `[timestamp, timestamp + ttl)` - Expired fields must **not** appear in `get_at`, `scan_at`, or prefix scans. - Time always moves forward: timestamps provided to operations are **non-decreasing**. --- ## Level 4: Backup and restore Implement: - `backup(timestamp)` - `restore(timestamp, timestamp_to_restore)` Backup requirements: - `backup(t)` stores a snapshot of the database state at time `t`. - For fields with TTL, the backup must capture **remaining TTL** at backup time (i.e., how much lifetime is left at `t`). Restore requirements: - `restore(now, timestamp_to_restore)` restores the database from the **latest** backup whose backup time is **≤ `timestamp_to_restore`**. - After restoring at current time `now`, TTL expiration must be **recalculated** based on remaining TTL stored in the backup: - If a field had remaining TTL `r` in the backup, then after restore at time `now` it should expire at `now + r`. - Fields that were already expired at the moment of backup should not be present in that backup. Your implementation should correctly handle overwrites, deletions, scans, TTL expiry, and backup/restore interactions under the monotonic-time guarantee.

Quick Answer: This question evaluates proficiency in designing and implementing mutable in-memory data structures, field-level CRUD and scanning, timestamped operations with TTL semantics, and backup/restore snapshot management, focusing on data structures, temporal reasoning, and state management in the Coding & Algorithms domain.

Part 1: Basic CRUD on In-Memory Database Fields

Implement a simple **in-memory key-value store** that supports basic CRUD operations on record fields, then return the results of all read/delete queries. Each **record** is identified by a string `key`, and every key owns a collection of **`field -> value`** pairs, where both `field` and `value` are strings. ## Function ```python def solution(queries): ``` `queries` is a list of operations. Each operation is itself a list whose first element is the operation name. Process the operations **in order** and return a list containing the output of every `GET` and `DELETE` operation, in the order they occur. ## Operations - **`['SET', key, field, value]`** — Insert the `field -> value` pair under `key`. If the key does not yet exist, create it. If the field already exists, **overwrite** its value. Produces **no output**. - **`['GET', key, field]`** — Return the stored value for `field` under `key`. If the key does not exist, or the key exists but the field does not, return the **empty string `''`**. Appends one value to the output. - **`['DELETE', key, field]`** — Remove `field` from `key`. Return `True` if the field existed and was removed; otherwise return `False`. Appends one boolean to the output. ## Output Return a list with one entry per `GET` and per `DELETE`, in query order. `SET` operations contribute nothing, so a run containing only `SET` operations returns an empty list `[]`. ## Examples **Example 1** ``` queries = [ ['SET', 'user1', 'name', 'alice'], ['GET', 'user1', 'name'], ['SET', 'user1', 'name', 'bob'], ['GET', 'user1', 'name'], ['DELETE', 'user1', 'name'], ['GET', 'user1', 'name'], ['DELETE', 'user1', 'name'], ] ``` Output: `['alice', 'bob', True, '', False]` - `GET user1.name` → `'alice'` - After the second `SET`, `GET user1.name` → `'bob'` (overwritten) - `DELETE user1.name` → `True` (field existed) - `GET user1.name` → `''` (field no longer exists) - `DELETE user1.name` → `False` (already gone) **Example 2** ``` queries = [['GET', 'missing', 'x'], ['DELETE', 'missing', 'x']] ``` Output: `['', False]` — reading or deleting from a key that was never set returns `''` and `False` respectively. **Example 3** ``` queries = [ ['SET', 'a', 'x', '1'], ['SET', 'a', 'y', '2'], ['DELETE', 'a', 'x'], ['GET', 'a', 'x'], ['GET', 'a', 'y'], ] ``` Output: `[True, '', '2']` — deleting one field of a key leaves the key's other fields intact. ## Constraints - `0 <= len(queries) <= 100000` - Each `key`, `field`, and `value` is a string of length 1 to 100. - Only the operations `'SET'`, `'GET'`, and `'DELETE'` appear. - You may assume average **O(1)** hash-map operations.

Constraints

  • 0 <= len(queries) <= 100000
  • Each key, field, and value is a string of length 1 to 100
  • Only the operations 'SET', 'GET', and 'DELETE' appear
  • Average O(1) hash-map operations may be assumed

Examples

Input: [['SET', 'user1', 'name', 'alice'], ['GET', 'user1', 'name'], ['SET', 'user1', 'name', 'bob'], ['GET', 'user1', 'name'], ['DELETE', 'user1', 'name'], ['GET', 'user1', 'name'], ['DELETE', 'user1', 'name']]

Expected Output: ['alice', 'bob', True, '', False]

Explanation: The value is overwritten from 'alice' to 'bob'. The first delete succeeds, and the second delete fails because the field is already gone.

Input: [['GET', 'missing', 'x'], ['DELETE', 'missing', 'x']]

Expected Output: ['', False]

Explanation: Missing keys and fields are treated as absent.

Hints

  1. A nested dictionary works well: one map from key to another map of field to value.
  2. After deleting a field, you may remove the key entirely if it has no fields left.

Part 2: Sorted Scan and Prefix Scan in an In-Memory Database

Implement an **in-memory database** that stores fields per record and supports point operations plus two ordered scans. Implement the function: ```python def solution(queries): ``` ## Data model The database holds **records**. Each record is identified by a string **key** and stores a collection of **field → value** pairs, where both `field` and `value` are strings. Setting an existing field overwrites its value (last write wins). ## Input - **`queries`** — a list of operations, processed in order. Each operation is itself a list of strings whose first element is the operation name, followed by its arguments: | Operation | Form | Behavior | Output | |---|---|---|---| | **SET** | `['SET', key, field, value]` | Create the record if needed, then set/overwrite `field` to `value`. | *(none — produces no output)* | | **GET** | `['GET', key, field]` | Look up the value of `field` in `key`. | The stored value string, or `''` if the key or field does not exist. | | **DELETE** | `['DELETE', key, field]` | If both the key and field exist, remove that field (and remove the record entirely once it has no remaining fields). | `True` if a field was removed, otherwise `False`. | | **SCAN** | `['SCAN', key]` | Read all fields of `key`. | A list of strings `'field(value)'`, **sorted lexicographically by field name**. | | **SCAN_BY_PREFIX** | `['SCAN_BY_PREFIX', key, prefix]` | Same as SCAN, but only include fields whose names **start with** `prefix`. | A list of strings `'field(value)'`, **sorted lexicographically by field name**. | ## Output Return a list containing the outputs of **every query except `SET`**, in the order the queries were processed. Each non-`SET` query contributes exactly one entry: - `GET` → a value string (`''` when absent) - `DELETE` → a boolean (`True`/`False`) - `SCAN` / `SCAN_BY_PREFIX` → a (possibly empty) list of `'field(value)'` strings ## Scan formatting and edge cases - Each matched field is formatted as the literal `field` name, followed by its `value` wrapped in parentheses: **`'field(value)'`**. - Results of `SCAN` and `SCAN_BY_PREFIX` are always **sorted lexicographically by field name** before formatting. - A scan on a **missing key**, or a `SCAN_BY_PREFIX` where **no field matches** the prefix, returns an **empty list** `[]`. ## Example ```text queries = [ ['SET', 'user', 'b', '2'], ['SET', 'user', 'a', '1'], ['SCAN', 'user'], ['SCAN_BY_PREFIX', 'user', 'a'], ] ``` Output: `[['a(1)', 'b(2)'], ['a(1)']]` The two `SET`s produce no output. `SCAN 'user'` lists both fields sorted by name. `SCAN_BY_PREFIX 'user' 'a'` lists only the field starting with `'a'`. ## Constraints - `0 <= len(queries) <= 100000` - Each key, field, value, and prefix is a string of length `0` to `100`. - Results for scan operations must be sorted lexicographically by field name. - Average **O(1)** hash-map operations may be assumed.

Constraints

  • 0 <= len(queries) <= 100000
  • Each key, field, value, and prefix is a string of length 0 to 100
  • Results for scan operations must be sorted lexicographically by field name
  • Average O(1) hash-map operations may be assumed

Examples

Input: [['SET', 'user', 'b', '2'], ['SET', 'user', 'a', '1'], ['SCAN', 'user'], ['SCAN_BY_PREFIX', 'user', 'a']]

Expected Output: [['a(1)', 'b(2)'], ['a(1)']]

Explanation: Fields are returned in lexicographic order, and the prefix scan keeps only fields starting with 'a'.

Input: [['SET', 'k', 'name', 'Ann'], ['DELETE', 'k', 'age'], ['GET', 'k', 'age'], ['SCAN_BY_PREFIX', 'k', 'z'], ['SCAN', 'missing']]

Expected Output: [False, '', [], []]

Explanation: Deleting a missing field returns False. A missing field returns '', and scans with no matches return empty lists.

Hints

  1. Keep the same nested dictionary structure from basic CRUD.
  2. For a scan, collect matching field names first, sort them, then build the 'field(value)' strings.

Part 3: Timestamped In-Memory Database with TTL

Implement a **timestamped in-memory key-value database** that supports an optional **time-to-live (TTL)** on individual fields. Each top-level **key** maps to a set of **field → value** pairs, where both fields and values are strings. Every operation carries an explicit timestamp, and you must answer each query as of that timestamp. ## Function Implement: ```python def solution(queries): ... ``` `queries` is a list of operations. Each operation is itself a list of strings whose first element is the operation name. Process the operations **in the given order** and return a single list containing the results of the operations that produce output (defined below), in the same order they occur. ## Operations All numeric arguments (timestamps and TTLs) are given as **decimal strings** and should be interpreted as integers. | Operation | Argument layout | Returns | |-----------|-----------------|---------| | `SET_AT` | `["SET_AT", key, field, value, timestamp]` | nothing | | `SET_AT_WITH_TTL` | `["SET_AT_WITH_TTL", key, field, value, timestamp, ttl]` | nothing | | `GET_AT` | `["GET_AT", key, field, timestamp]` | the value string, or `""` | | `DELETE_AT` | `["DELETE_AT", key, field, timestamp]` | `True` or `False` | | `SCAN_AT` | `["SCAN_AT", key, timestamp]` | a list of strings | | `SCAN_BY_PREFIX_AT` | `["SCAN_BY_PREFIX_AT", key, prefix, timestamp]` | a list of strings | Only `GET_AT`, `DELETE_AT`, `SCAN_AT`, and `SCAN_BY_PREFIX_AT` contribute to the returned list. `SET_AT` and `SET_AT_WITH_TTL` produce no output. ### Semantics - **`SET_AT`** — Set `field = value` under `key` at `timestamp`, with **no expiry**. If the field already exists (including one previously set with a TTL), it is overwritten and any prior TTL is removed. - **`SET_AT_WITH_TTL`** — Set `field = value` under `key` at `timestamp` with the given `ttl`. The field is **valid during the half-open interval `[timestamp, timestamp + ttl)`** — i.e. it exists at and after `timestamp` but is treated as **absent once the current time reaches `timestamp + ttl`**. As with `SET_AT`, this overwrites any existing value/TTL for that field. - **`GET_AT`** — Return the value of `field` under `key` as of `timestamp`. Return `""` (empty string) if the key or field does not exist or the field has expired. - **`DELETE_AT`** — Delete `field` under `key` as of `timestamp`. Return `True` if a **live (non-expired) field was actually present and removed**; return `False` if the key/field does not exist or the field has already expired. - **`SCAN_AT`** — Return all live (non-expired) fields under `key` as of `timestamp`, formatted as `"field(value)"`, **sorted lexicographically by field name**. Return an empty list if the key does not exist or has no live fields. - **`SCAN_BY_PREFIX_AT`** — Same as `SCAN_AT`, but include only fields whose name **starts with `prefix`**. Results are likewise formatted as `"field(value)"` and sorted lexicographically by field name. ## Expiry rules - An expired field behaves exactly as if it were **absent** for all subsequent gets, deletes, and scans. - The timestamps supplied across the queries are **non-decreasing**, so once a field has expired it never becomes valid again. ## Output Return the list of results for the output-producing operations, in query order. For example, a `GET_AT` that finds a live value contributes that value string; a `SCAN_AT` contributes its (possibly empty) sorted list; a `DELETE_AT` contributes `True` or `False`. ## Constraints - `0 <= len(queries) <= 100000` - All timestamps are decimal strings representing integers in `[0, 10^9]`. - All TTL values are positive decimal strings representing integers in `[1, 10^9]`. - The current timestamps in the queries are non-decreasing.

Constraints

  • 0 <= len(queries) <= 100000
  • All timestamps are decimal strings representing integers in [0, 10^9]
  • All ttl values are positive decimal strings representing integers in [1, 10^9]
  • The current timestamps in the queries are non-decreasing

Examples

Input: [['SET_AT_WITH_TTL', 'k', 'a', 'x', '10', '5'], ['GET_AT', 'k', 'a', '10'], ['GET_AT', 'k', 'a', '14'], ['GET_AT', 'k', 'a', '15'], ['DELETE_AT', 'k', 'a', '15']]

Expected Output: ['x', 'x', '', False]

Explanation: The field is valid at times 10 through 14, but not at 15 because the TTL interval is [10, 15).

Input: [['SET_AT', 'u', 'name', 'Ann', '1'], ['SET_AT_WITH_TTL', 'u', 'age', '20', '2', '3'], ['SCAN_AT', 'u', '3'], ['SET_AT', 'u', 'age', '21', '4'], ['GET_AT', 'u', 'age', '6'], ['SCAN_BY_PREFIX_AT', 'u', 'a', '6']]

Expected Output: [['age(20)', 'name(Ann)'], '21', ['age(21)']]

Explanation: The TTL version of 'age' is alive at time 3. It is overwritten at time 4 by a non-TTL value, so it still exists at time 6.

Hints

  1. Store an absolute expiration time like expire_at = timestamp + ttl instead of storing ttl directly.
  2. Because time never goes backward, you can lazily remove expired fields whenever a key is touched.

Part 4: Backup and Restore in a Timestamped Database with TTL

Implement a **timestamped, in-memory key–value database** that supports **per-field TTL (time-to-live)** plus **backup** and **restore** operations. The database is a two-level store: each **key** maps to a set of **fields**, and each field holds a string **value**. A field may optionally have a TTL, after which it expires and is treated as if it never existed. All operations carry an explicit timestamp, so behavior is fully deterministic. ## Function ```python def solution(queries): ... ``` - **`queries`** is a list of operations. Each operation is a list of strings: the operation name followed by its arguments. - Return a list containing the result of every operation that produces output (`GET_AT`, `DELETE_AT`, `SCAN_AT`, `SCAN_BY_PREFIX_AT`), in the order those operations appear. `SET_AT`, `SET_AT_WITH_TTL`, `BACKUP`, and `RESTORE` produce **no** output. All numeric arguments (timestamps, TTLs, restore arguments) arrive as decimal strings and should be parsed as integers. ## Operations **`["SET_AT", key, field, value, timestamp]`** Set `field` of `key` to `value` with no expiry. Overwrites any existing value for that field. No output. **`["SET_AT_WITH_TTL", key, field, value, timestamp, ttl]`** Set `field` of `key` to `value` with a TTL of `ttl`, so the field expires at `timestamp + ttl`. Overwrites any existing value. No output. **`["GET_AT", key, field, timestamp]`** Return the current `value` of `field` under `key` as a string. If the field does not exist or has expired at `timestamp`, return the empty string `""`. **`["DELETE_AT", key, field, timestamp]`** Delete `field` from `key`. Return `True` if a live (non-expired) field was present and removed at `timestamp`; otherwise return `False`. **`["SCAN_AT", key, timestamp]`** Return a list of strings for all live fields under `key`, **sorted ascending by field name**, each formatted as `"field(value)"`. If the key has no live fields, return an empty list. **`["SCAN_BY_PREFIX_AT", key, prefix, timestamp]`** Same as `SCAN_AT`, but only include fields whose name **starts with** `prefix`. Results are sorted ascending by field name and formatted as `"field(value)"`. **`["BACKUP", timestamp]`** Take a snapshot of the entire live database at `timestamp`. No output. **`["RESTORE", now, timestamp_to_restore]`** Replace the entire database with a previously saved snapshot. No output. ## Expiry rule A field with expiry time `E` is **live** while `current_time < E` and **expired** once `current_time >= E`. Expired fields must never be returned by `GET_AT`, counted by `DELETE_AT`, or listed by either scan operation, and must never be included in a backup. ## Backup semantics - A `BACKUP` at time `t` snapshots only the fields that are **live** at `t`; already-expired fields are excluded. - For each TTL field, the snapshot stores the field's **remaining** TTL (`E - t`), **not** its absolute expiry time. Fields without a TTL are stored as non-expiring. ## Restore semantics `RESTORE` is given two timestamps: the current time `now`, and a `timestamp_to_restore` (which may point to any past time). - Choose the **latest backup whose backup time is `<= timestamp_to_restore`**. - Restore that snapshot as the live database, treating `now` as the moment of restoration. Each stored field's remaining TTL `r` is converted back into an absolute expiry of `now + r`, so the field expires `r` units after the restore. Fields without a TTL remain non-expiring. - If **no** backup has a time `<= timestamp_to_restore`, restore to an **empty** database. ## Ordering guarantees The current-time argument used by `SET_AT`, `SET_AT_WITH_TTL`, `GET_AT`, `DELETE_AT`, `SCAN_AT`, `SCAN_BY_PREFIX_AT`, `BACKUP`, and the `now` argument of `RESTORE` are **non-decreasing** across the query sequence. Only `timestamp_to_restore` may refer to an arbitrary past time. ## Constraints - `0 <= len(queries) <= 100000` - All timestamps, TTL values, and restore arguments are decimal strings representing integers in `[0, 10^9]`. - All TTL values are positive.

Constraints

  • 0 <= len(queries) <= 100000
  • All timestamps, ttl values, and restore arguments are decimal strings representing integers in [0, 10^9]
  • All ttl values are positive
  • The current time arguments for SET/GET/DELETE/SCAN/BACKUP and RESTORE-now are non-decreasing
  • timestamp_to_restore may point to any past time

Examples

Input: [['SET_AT_WITH_TTL', 'doc', 'a', '1', '10', '5'], ['BACKUP', '12'], ['GET_AT', 'doc', 'a', '13'], ['RESTORE', '20', '12'], ['SCAN_AT', 'doc', '22'], ['GET_AT', 'doc', 'a', '23']]

Expected Output: ['1', ['a(1)'], '']

Explanation: At backup time 12, the field has 3 units of TTL left. After restoring at time 20, it is alive at 22 and expires at 23.

Input: [['SET_AT', 'k', 'x', 'A', '1'], ['BACKUP', '2'], ['SET_AT', 'k', 'x', 'B', '3'], ['BACKUP', '4'], ['DELETE_AT', 'k', 'x', '5'], ['RESTORE', '6', '3'], ['GET_AT', 'k', 'x', '6'], ['RESTORE', '7', '100'], ['GET_AT', 'k', 'x', '7']]

Expected Output: [True, 'A', 'B']

Explanation: Restoring to time 3 uses the backup from time 2, so the value is 'A'. Restoring to time 100 uses the later backup from time 4, so the value is 'B'.

Hints

  1. At backup time t, store remaining_ttl = expire_at - t for live TTL fields.
  2. Since backups are taken in time order, you can keep them in a list and use binary search to find the latest backup with time <= timestamp_to_restore.
Last updated: Jun 12, 2026

Loading coding console...

PracHub

Master your tech interviews with 9,000+ real questions from top companies.

Product

  • Questions
  • Learning Tracks
  • Interview Guides
  • Resources
  • Premium
  • For Universities

Browse

  • By Company
  • By Role
  • By Category
  • Topic Hubs
  • SQL Questions
  • AI Coding Questions
  • Compare Platforms
  • Discord Community

Support

  • support@prachub.com
  • (916) 541-4762

Legal

  • Privacy Policy
  • Terms of Service
  • About Us

© 2026 PracHub. All rights reserved.

Related Coding Questions

  • Flatten and unflatten nested Python structures - xAI (medium)
  • Compute dasher pay from order events - xAI (medium)
  • Compute total active time per Twitter Space - xAI (medium)
  • Design a Recoverable Iterator - xAI (medium)
  • Implement Distributed Matrix Multiplication - xAI (hard)