Implement a Key-Value Cache with Fixed TTL, Renewal and a Live-Entry Count

Read the full interview experience this question came from →

Quick Overview

Implement an in-memory key-value cache whose entries expire a fixed time-to-live after their last write, with put, get, renew and a count of live entries, and write your own tests for it. It tests expiry boundary semantics, renewal rules, efficient cleanup of expired entries and test design.

Implement a Key-Value Cache with Fixed TTL, Renewal and a Live-Entry Count

Company: Oracle

Role: Software Engineer

Category: Software Engineering Fundamentals

Difficulty: easy

Interview Round: Onsite

Implement an in-memory key-value cache in which every entry expires a fixed time-to-live (TTL) after it was last written. The exercise follows the pattern of a common token-expiry design problem: the TTL is set once when the cache is created, the current time is passed into every operation, a live entry can be renewed, and the cache can report how many entries are still live. You are expected to write your own test cases. A reasonable interface: ```python class TTLCache: def __init__(self, ttl: int) -> None: ... def put(self, key: str, value: object, now: int) -> None: ... def get(self, key: str, now: int) -> object | None: ... def renew(self, key: str, now: int) -> bool: ... def count_live(self, now: int) -> int: ... ``` - `put` inserts a new entry or overwrites an existing one; the entry expires at `now + ttl`. - `get` returns the value of a live entry, or `None` if the key is missing or has expired. - `renew` moves the expiry of a live entry to `now + ttl` and reports whether it did so. An expired entry is not revived. - `count_live` returns how many entries are live at time `now`. ```hint Make the boundary testable Your tests have to land exactly on the moment an entry expires, and on either side of it. Keep time an explicit input, and decide in writing what happens at that exact moment. ``` ```hint Look for an order you get for free `count_live` may be called far more often than entries change. With one TTL shared by every entry, think about which entries must expire first. ``` ### Constraints and Clarifications - The TTL is a positive integer, fixed for the lifetime of the cache. - Time values are integers in the same unit as the TTL. - The tests are part of the answer: you are expected to design them, not only the class. ### Clarifying Questions - Is an entry that expires exactly at time `now` still live at `now`? - Are the `now` values passed across calls guaranteed never to decrease? - Does `get` extend an entry's lifetime, or only `put` and `renew`? - Does `put` on a key whose entry has already expired behave like a fresh insert? - Is there a capacity limit, and if so, what happens when the cache is full? - Can a stored value be `None`, which would make a `None` result from `get` ambiguous? ### What a Strong Answer Covers - A precise expiry boundary, applied identically in `get`, `renew` and `count_live`. - Renewal that refuses expired entries, and clear overwrite semantics for `put`. - An efficient way to discard expired entries and count live ones, with the complexity of every operation. - A self-written test suite that covers the exact expiry moment, renewal before and at expiry, overwrites, counting after mixed writes, and invalid time input. ### Follow-up Questions - How would the design change if each entry could have its own TTL? - How would you add a maximum capacity with least-recently-used eviction on top of expiry? - How would you make the cache safe for concurrent callers, and would lazy cleanup still be enough? - If time came from a system clock instead of a parameter, how would you keep the tests deterministic?

Overview: Implement an in-memory key-value cache whose entries expire a fixed time-to-live after their last write, with put, get, renew and a count of live entries, and write your own tests for it. It tests expiry boundary semantics, renewal rules, efficient cleanup of expired entries and test design.

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

|Home/Software Engineering Fundamentals/Oracle
Oracle logo
Oracle
Sep 25, 2026
easySoftware EngineerOnsiteSoftware Engineering Fundamentals
0
0

Implement an in-memory key-value cache in which every entry expires a fixed time-to-live (TTL) after it was last written. The exercise follows the pattern of a common token-expiry design problem: the TTL is set once when the cache is created, the current time is passed into every operation, a live entry can be renewed, and the cache can report how many entries are still live. You are expected to write your own test cases.

A reasonable interface:

class TTLCache:
    def __init__(self, ttl: int) -> None: ...
    def put(self, key: str, value: object, now: int) -> None: ...
    def get(self, key: str, now: int) -> object | None: ...
    def renew(self, key: str, now: int) -> bool: ...
    def count_live(self, now: int) -> int: ...
  • put inserts a new entry or overwrites an existing one; the entry expires at now + ttl .
  • get returns the value of a live entry, or None if the key is missing or has expired.
  • renew moves the expiry of a live entry to now + ttl and reports whether it did so. An expired entry is not revived.
  • count_live returns how many entries are live at time now .

Constraints and Clarifications

  • The TTL is a positive integer, fixed for the lifetime of the cache.
  • Time values are integers in the same unit as the TTL.
  • The tests are part of the answer: you are expected to design them, not only the class.

Clarifying Questions Guidance

  • Is an entry that expires exactly at time now still live at now ?
  • Are the now values passed across calls guaranteed never to decrease?
  • Does get extend an entry's lifetime, or only put and renew ?
  • Does put on a key whose entry has already expired behave like a fresh insert?
  • Is there a capacity limit, and if so, what happens when the cache is full?
  • Can a stored value be None , which would make a None result from get ambiguous?

What a Strong Answer Covers Guidance

  • A precise expiry boundary, applied identically in get , renew and count_live .
  • Renewal that refuses expired entries, and clear overwrite semantics for put .
  • An efficient way to discard expired entries and count live ones, with the complexity of every operation.
  • A self-written test suite that covers the exact expiry moment, renewal before and at expiry, overwrites, counting after mixed writes, and invalid time input.

Follow-up Questions Guidance

  • How would the design change if each entry could have its own TTL?
  • How would you add a maximum capacity with least-recently-used eviction on top of expiry?
  • How would you make the cache safe for concurrent callers, and would lazy cleanup still be enough?
  • If time came from a system clock instead of a parameter, how would you keep the tests deterministic?
Loading comments...