Timestamped Parcel Attribute Store With Compare-and-Set, Prefix Listing and TTL

Read the full interview experience this question came from →

Quick Overview

Implement a timestamped in-memory store of parcel attributes with overwriting set, get and a compare-and-set update, then list attributes as name(value) sorted by name with an optional prefix filter, and finally add per-attribute TTLs that hide expired values. It tests precise operation semantics, expiry rules and incremental class design.

Timestamped Parcel Attribute Store With Compare-and-Set, Prefix Listing and TTL

Company: Airbnb

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: medium

Interview Round: Online Assessment

Implement an in-memory store of parcels. Each parcel is identified by a parcel ID and can hold many attributes with different names, each attribute being a name with a value. Every operation is called with a timestamp. The exercise is a timed, level-based coding assessment checked by automated tests, with four levels in 90 minutes, and each level extends the same class. The three reported levels are below. ### Constraints and Clarifications - An attribute holds exactly one value. Writing it again replaces the value; no history of earlier values is kept. - Every method takes a timestamp, including at the first two levels, where the timestamp affects no result. - The method names and argument orders below are one consistent reconstruction of the reported operations; return values for missing data are to be confirmed (see the questions). - Correctness is what is scored: a straightforward, brute-force implementation that passes the tests is acceptable. ### Clarifying Questions - What does `get` return for an unknown parcel or attribute: `None`, an empty string, or something else? What do `set` and the conditional update return? - Are values strings, and is a "match" exact, case-sensitive equality? - Can the conditional update create an attribute that does not exist yet? - Do timestamps increase from call to call? ### Part 1 — Set, get and conditional update Implement: - `set(timestamp, parcel_id, attribute, value)`: store `value` under `attribute` for the parcel, overwriting any existing value. - `get(timestamp, parcel_id, attribute)`: return the attribute's current value. - `update_if_match(timestamp, parcel_id, attribute, expected, new_value)`: set the attribute to `new_value` only if its current value equals `expected` exactly; otherwise change nothing. ```hint Shape of the store A parcel ID leads to a set of named attributes with one value each. Choose the nesting that makes "does this parcel have this attribute" a single lookup. ``` ```hint Match means exact Decide what the conditional update does when the attribute is missing, and make sure a failed match leaves the store untouched. ``` #### What This Part Should Cover - A nested store keyed by parcel ID and attribute name - Overwrite semantics, without unnecessary history or time ordering - Compare-then-write behavior that changes nothing on a mismatch or a missing attribute ### Part 2 — Listing attributes Add: - `list_attributes(timestamp, parcel_id)`: return every attribute of the parcel formatted as `name(value)`, ordered lexicographically by attribute name. - `list_attributes_with_prefix(timestamp, parcel_id, prefix)`: the same, restricted to attributes whose names start with `prefix`. ```hint One listing, two callers The two operations differ only in a filter on the name. Share the filtering, sorting and formatting so they can never disagree. ``` #### Clarifying Questions for this Part - Is the result a list of strings or a single joined string, and with what separator? - What is returned for an unknown parcel, or for a parcel with no matching attributes? - Is the order plain, case-sensitive string comparison of the names? #### What This Part Should Cover - Ordering by attribute name and the exact `name(value)` output format - A prefix filter on the attribute name that reuses the unfiltered listing's logic - Empty and unknown-parcel results ### Part 3 — Time-to-live Each attribute now has a time-to-live. `set` and `update_if_match` gain a `ttl` argument. A `ttl` of 0 means the attribute never expires; otherwise it expires `ttl` time units after it is written. `get` and both listing operations must ignore expired attributes. `update_if_match` must not update an expired attribute, but it can update an unexpired one, including setting its `ttl` to 0 so that it never expires. ```hint What a stored value must carry Expiry should be decidable from the stored entry and the query timestamp alone. Work out what the entry must record at write time for that to hold. ``` ```hint One liveness test The get, both listings and the conditional update all need the same "is this attribute alive at this timestamp" check. Put it in one place. ``` #### Clarifying Questions for this Part - Is an attribute written at time `w` with ttl `d` still visible at exactly `w + d`? - Does a successful write restart the lifetime from its own timestamp, using the ttl passed in that call? - What ttl applies when a call omits it, for example the level 1 calls that existing tests still make? #### What This Part Should Cover - What each stored value records so that expiry can be decided - One liveness rule (ttl 0, the boundary, restart on write) applied to `get`, both listings and the conditional update - Whether expired entries are hidden or removed ### What a Strong Answer Covers - A store layout that makes per-attribute lookups and per-parcel listings simple - Precise semantics for missing data, failed matches, ordering and prefix filtering - A single expiry rule applied consistently to every read and to the conditional update - Incremental code in which each level is a small extension, without overbuilding for the timestamp the first two levels ignore ### Follow-up Questions - How would you reclaim the memory held by expired attributes without slowing down reads? - How would you support reading an attribute's value as of a past timestamp? - If parcels have many attributes and prefix listings are frequent, what structure avoids sorting on every call? - How would you keep `update_if_match` correct if several threads used the store at once?

Overview: Implement a timestamped in-memory store of parcel attributes with overwriting set, get and a compare-and-set update, then list attributes as name(value) sorted by name with an optional prefix filter, and finally add per-attribute TTLs that hide expired values. It tests precise operation semantics, expiry rules and incremental class design.

Read the full Airbnb Software Engineer interview experience this question came from

|Home/Software Engineering Fundamentals/Airbnb
Airbnb logo
Airbnb
Oct 2, 2026
mediumSoftware EngineerOnline AssessmentSoftware Engineering Fundamentals
1
0

Implement an in-memory store of parcels. Each parcel is identified by a parcel ID and can hold many attributes with different names, each attribute being a name with a value. Every operation is called with a timestamp. The exercise is a timed, level-based coding assessment checked by automated tests, with four levels in 90 minutes, and each level extends the same class. The three reported levels are below.

Constraints and Clarifications

  • An attribute holds exactly one value. Writing it again replaces the value; no history of earlier values is kept.
  • Every method takes a timestamp, including at the first two levels, where the timestamp affects no result.
  • The method names and argument orders below are one consistent reconstruction of the reported operations; return values for missing data are to be confirmed (see the questions).
  • Correctness is what is scored: a straightforward, brute-force implementation that passes the tests is acceptable.

Clarifying Questions Guidance

  • What does get return for an unknown parcel or attribute: None , an empty string, or something else? What do set and the conditional update return?
  • Are values strings, and is a "match" exact, case-sensitive equality?
  • Can the conditional update create an attribute that does not exist yet?
  • Do timestamps increase from call to call?

Part 1 — Set, get and conditional update

Implement:

  • set(timestamp, parcel_id, attribute, value) : store value under attribute for the parcel, overwriting any existing value.
  • get(timestamp, parcel_id, attribute) : return the attribute's current value.
  • update_if_match(timestamp, parcel_id, attribute, expected, new_value) : set the attribute to new_value only if its current value equals expected exactly; otherwise change nothing.

What This Part Should Cover Guidance

  • A nested store keyed by parcel ID and attribute name
  • Overwrite semantics, without unnecessary history or time ordering
  • Compare-then-write behavior that changes nothing on a mismatch or a missing attribute

Part 2 — Listing attributes

Add:

  • list_attributes(timestamp, parcel_id) : return every attribute of the parcel formatted as name(value) , ordered lexicographically by attribute name.
  • list_attributes_with_prefix(timestamp, parcel_id, prefix) : the same, restricted to attributes whose names start with prefix .

Clarifying Questions for this Part Guidance

  • Is the result a list of strings or a single joined string, and with what separator?
  • What is returned for an unknown parcel, or for a parcel with no matching attributes?
  • Is the order plain, case-sensitive string comparison of the names?

What This Part Should Cover Guidance

  • Ordering by attribute name and the exact name(value) output format
  • A prefix filter on the attribute name that reuses the unfiltered listing's logic
  • Empty and unknown-parcel results

Part 3 — Time-to-live

Each attribute now has a time-to-live. set and update_if_match gain a ttl argument. A ttl of 0 means the attribute never expires; otherwise it expires ttl time units after it is written. get and both listing operations must ignore expired attributes. update_if_match must not update an expired attribute, but it can update an unexpired one, including setting its ttl to 0 so that it never expires.

Clarifying Questions for this Part Guidance

  • Is an attribute written at time w with ttl d still visible at exactly w + d ?
  • Does a successful write restart the lifetime from its own timestamp, using the ttl passed in that call?
  • What ttl applies when a call omits it, for example the level 1 calls that existing tests still make?

What This Part Should Cover Guidance

  • What each stored value records so that expiry can be decided
  • One liveness rule (ttl 0, the boundary, restart on write) applied to get , both listings and the conditional update
  • Whether expired entries are hidden or removed

What a Strong Answer Covers Guidance

  • A store layout that makes per-attribute lookups and per-parcel listings simple
  • Precise semantics for missing data, failed matches, ordering and prefix filtering
  • A single expiry rule applied consistently to every read and to the conditional update
  • Incremental code in which each level is a small extension, without overbuilding for the timestamp the first two levels ignore

Follow-up Questions Guidance

  • How would you reclaim the memory held by expired attributes without slowing down reads?
  • How would you support reading an attribute's value as of a past timestamp?
  • If parcels have many attributes and prefix listings are frequent, what structure avoids sorting on every call?
  • How would you keep update_if_match correct if several threads used the store at once?
Loading comments...