SWE / Group Hiring · Second Round Coding

Microsoft TTL Cache:get/put、过期端点与懒清理

从组招缓存题的 get/put API 出发,明确半开有效期、覆盖写、空值区分和闲置键回收。

题目重点:从组招缓存题的 get/put API 出发,明确半开有效期、覆盖写、空值区分和闲置键回收。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练 API 为 put(key,value,ttl,now) 和 get(key,now),调用方传单调时间;ttl≥0。
  • 在 put 的 now 写入后有效区间为 [now,now+ttl),恰好到期时 miss;同 key put 覆盖旧值和旧到期时刻。
  • get 返回 (found,value),允许 value=None 而不会与 miss 混淆;容量无限,暂不做 LRU 淘汰。
  • 示例假定同一调用序列的 now 非递减;倒流时间对已懒删的键不提供历史查询。

解题路线

  1. 哈希表 key→(value,expiresAt)。put 根据 TTL 计算到期时间并覆盖;get 读取条目,若 now≥expiresAt 就删掉并返回 miss,否则返回值。
  2. 半开区间让 ttl=0 的写入立即不可读,也避免到期瞬间同时有效和无效两种口径。
  3. 懒清理保证访问到过期键时释放内存,却不能释放永不再访问的键;生产版可加小根堆按到期时刻后台回收,覆盖写时用版本号跳过旧堆记录。
  4. 并发下 put/get 与删除必须按键原子化,否则旧 get 可能删除刚覆盖的新值;单线程代码不冒充并发安全。

手动走一遍

put(a,4,ttl=3,now=10) 后 get(a,12) 命中 4,get(a,13) miss;再 put(a,None,ttl=5,now=20),get(a,21) 返回 (True,None),可与 (False,None) 区分。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

class TTLCache:
    """Training API with caller-supplied monotonic time and lazy expiration."""

    def __init__(self) -> None:
        self.data: dict[object, tuple[object, float]] = {}

    def put(self, key: object, value: object, ttl: float, now: float) -> None:
        if ttl < 0:
            raise ValueError("negative TTL")
        self.data[key] = (value, now + ttl)

    def get(self, key: object, now: float) -> tuple[bool, object | None]:
        entry = self.data.get(key)
        if entry is None:
            return False, None
        value, expires_at = entry
        if now >= expires_at:
            del self.data[key]
            return False, None
        return True, value


if __name__ == "__main__":
    cache = TTLCache()
    cache.put("a", 4, 3, 10)
    assert cache.get("a", 12) == (True, 4) and cache.get("a", 13) == (False, None)

复杂度

哈希表 put/get 预期 O(1) 时间;空间 O(历史写入过但尚未被访问清理的不同 key 数)。没有后台清理时,逻辑上过期的闲置键仍占内存;堆清理可使写入/回收 O(log n)。

易错点与追问

容易失分的边界

  • 在 t=expiresAt 仍返回旧值,与训练版半开边界相反。
  • 把值 None 当未命中,无法缓存空值。
  • 以 wall-clock 直接做 TTL 且不考虑时钟回拨;训练代码显式由调用方提供单调时间。
  • 声称懒删除能自动回收所有闲置过期键。
  • 把 LRU 逐出规则补成原题要求;题目只说 TTL cache。

继续深挖

  • 容量也有限时,TTL 与 LRU 的逐出优先级如何定义?
  • 覆盖 put 后旧过期事件如何避免删掉新值?
  • 多线程或多机共享 cache 时,时钟源和原子更新怎样处理?
✓已复制微信号!