2Brute2Best
πŸ”₯0
P6

Design an Extensible Cache (LRU / LFU Eviction)

Medium High-frequency interview topic Pro OnlyStrategyProgram to an Interface std::flat_map (C++23)
⚑ 30-Second Interview Cheat SheetP6 · 45-Min Design Plan
2 Patterns Combined
1. Scope Boundaries

In a 45-minute LLD interview, the key challenge is separation of concerns: cleanly decoupling raw key-value storage (Cache<K, V>) from the key eviction sequence (EvictionPolicy<K>). The interviewer expects strict O(1) average time complexity for get, put, and eviction. Scope the solution to generic key-value types, capacity enforcement, LRU eviction using a doubly-linked list + hash map, and a swappable FIFO policy. Mention C++23 std::flat_map trade-offs, thread-safe sharded caches, and TTL expiration as extensions.

2. Core Noun Entities
Cache<K, V>stateful-class
EvictionPolicy<K>interface
LruEvictionPolicy<K>stateful-class
FifoEvictionPolicy<K>stateful-class
3. Hotspot β†’ Pattern
Eviction algorithm selection (LRU vs FIFO vs LFU) β†’ Strategy (EvictionPolicy<K> interface)
Decoupling eviction bookkeeping from value storage β†’ Single Responsibility Principle (EvictionPolicy tracks keys only; Cache owns values)
01

Problem statement & clarifying requirements

Design an extensible, in-memory generic cache Cache<K, V> that supports fast lookups, insertions, updates, and removals with configurable capacity. The cache must cleanly separate value storage from eviction order so that different eviction algorithms (Least Recently Used (LRU), First-In First-Out (FIFO), and future algorithms like LFU) can be swapped in without modifying the core cache storage logic.

Questions you should ask the interviewer first
β€œWhat is the time complexity requirement for cache read, write, and eviction?”

All core operations (get, put, and eviction) must run in O(1) average time.

β€œWhat happens when an existing key is updated with `put(key, newValue)`?”

The value is overwritten, the cache size does not increase, and the key is marked as most recently accessed in the eviction policy.

β€œDoes reading a non-existent key trigger an eviction or policy update?”

No. A cache miss returns an empty optional (std::nullopt) and does not modify the eviction policy order.

β€œHow should zero-capacity or empty eviction requests be handled?”

A zero-capacity cache rejects all insertions without error. Evicting from an empty policy safely returns std::nullopt.

02

First, sketch the design yourself

Spend 5 minutes listing the classes, who owns whom, and which interface hides the part that changes.

  1. 1.

    Decouple storage from eviction order: If the hash map holds list iterators directly and the eviction logic is hardcoded inside Cache, switching to FIFO or LFU requires rewriting the entire cache class.

  2. 2.

    Use a doubly-linked list combined with a hash table: A linked list gives O(1) node removal and head/tail splicing, while a hash map maps keys to list node iterators for O(1) lookup.

  3. 3.

    Maintain ownership boundaries: Cache<K, V> owns the eviction policy via std::unique_ptr<EvictionPolicy<K>>, while the policy only tracks keys (K), never values (V).

03

How to think through this problem (5-step walkthrough)

Step 1 Β· Scope the 45-minute target & core boundary

In a 45-minute LLD interview, the key challenge is separation of concerns: cleanly decoupling raw key-value storage (Cache<K, V>) from the key eviction sequence (EvictionPolicy<K>). The interviewer expects strict O(1) average time complexity for get, put, and eviction. Scope the solution to generic key-value types, capacity enforcement, LRU eviction using a doubly-linked list + hash map, and a swappable FIFO policy. Mention C++23 std::flat_map trade-offs, thread-safe sharded caches, and TTL expiration as extensions.

Step 2 Β· Extract candidate nouns (and reject false classes)

Not every noun in the prompt deserves its own class. Separate dumb data structs from stateful managers, and turn trivial nouns into fields or enums.

Cache<K, V>
Stateful Class / Manager

Generic context managing value storage (std::unordered_map<K, V>), capacity bounds, and delegating eviction notifications to the injected policy.

EvictionPolicy<K>
Abstract Interface

Strategy interface tracking key access order (keyAccessed), selecting victim keys (evictKey), and updating on explicit removal (keyRemoved). Tracks keys only, never values.

LruEvictionPolicy<K>
Stateful Class / Manager

Implements O(1) LRU policy using a doubly-linked list (std::list<K>) and an iterator hash map (std::unordered_map<K, std::list<K>::iterator>).

FifoEvictionPolicy<K>
Stateful Class / Manager

Implements First-In First-Out eviction, appending only newly inserted keys to arrival order without repositioning on cache reads.

CacheHitCounter / TimestampEntry
Reject as Class β†’ Field / Method

Trap noun β€” beginners often introduce TimestampEntry (storing epoch timestamps to sort for LRU, taking O(N) or O(log N) heap time) or CacheHitCounter as standalone classes. LRU requires access ordering, not wall-clock timestamps; and hit/miss metrics are simply integer member counters on Cache.

Step 3 Β· Draft the naive design & spot what breaks

A beginner embeds a doubly-linked list directly inside the Cache class, storing both keys and values in list nodes. When asked to switch to FIFO or LFU eviction, or to benchmark eviction strategies under different workloads, the entire cache implementation must be duplicated or rewritten. Alternatively, beginners store timestamps on entries and perform O(N) scans to find the oldest entry, failing the O(1) performance requirement.

Step 4 Β· Map change hotspots to design patterns
Eviction algorithm selection (LRU vs FIFO vs LFU)β†’ Strategy (EvictionPolicy<K> interface)

Hardcoding list node splicing directly inside Cache<K, V> tightly couples storage to a single eviction heuristic and forces code duplication for alternative policies.

Decoupling eviction bookkeeping from value storage→ Single Responsibility Principle (EvictionPolicy tracks keys only; Cache owns values)

Passing values into the eviction policy bloats memory and prevents reusing the policy for key-only sets or metadata indexes.

Step 5 Β· Lock down C++23 ownership, invariants & concurrency
  • βœ“

    O(1) Access and Splicing invariant: LruEvictionPolicy maintains nodeMap_ pointing directly to list nodes, ensuring keyAccessed relocates the node to the front via std::list::splice in O(1) time without memory reallocation.

  • βœ“

    Capacity and Synchronization invariant: storage_.size() must never exceed capacity_. When inserting a new key at capacity, policy_->evictKey() must return an existing key, which is erased from storage_ before the new entry is inserted.

  • βœ“

    Ownership invariant: Cache<K, V> exclusively owns std::unique_ptr<EvictionPolicy<K>>. The policy maintains key identity order but holds no pointers or references to V.

04

Entities & design patterns used

Brute2Best Pro Content

Unlock Full UML Diagram & C++23 Solution

The complete UML diagram, production-ready C++23 code, follow-up extensions, and design pitfall analysis are part of Brute2Best Pro. All 7 foundation chapters are 100% free.

All 440 visual dry runs & algorithms
Interactive step-by-step playback with pointer & array state transitions
52 comprehensive LLD chapters
Interactive UML diagrams, real-world stories (Food Delivery, Trading, RPG), and practice solutions
40 C++ basics labs & hardware memory models
Vtables, byte layouts, assembly insights, and multi-threading race conditions
Production-ready C++23 source code
Modern idioms, clean contracts, time/space complexity derivations, and interview takeaways

First anchor question per pattern, Part 1 LLD & Module 1 C++ basics are 100% Free forever.

Systems Integration

Related C++ Systems & Algorithmic Foundations

Deep dive into underlying layers