Katkat

Design a token manager with lazy expiration

CitadelSoftware Engineer · Technical Screen

Last updated

This problem tests your ability to design a token management system that supports efficient retrieval, updates, and lazy removal of expired tokens using a combination of a hash map and a priority queue.

Token Manager Design

Design a TokenManager class that handles session tokens with a Time-To-Live (TTL). Each token is associated with a specific expiration timestamp. The system should be able to handle millions of tokens efficiently.

Your class needs to support the following operations:

  1. upsert(token_id, ttl_seconds): Adds a new token or updates the expiration of an existing token. The new expiration time is the current time plus ttl_seconds.
  2. get(token_id): Returns the token's expiration timestamp if it exists and hasn't expired. If the token has expired or does not exist, return -1. This operation must perform lazy deletion—if you encounter an expired token during a lookup, remove it immediately.
  3. count_active(): Returns the number of tokens that have not yet expired. You must provide an efficient way to track this, but you are not required to actively purge all expired tokens in the background; only purge them when accessed or when requested.

Constraints

  • token_id: String (unique identifier)
  • ttl_seconds: Integer (positive)
  • The current time can be simulated using time.time().

Example

# Assume current time = 1000
tm = TokenManager()
tm.upsert("user_a", 10)  # Expires at 1010
tm.upsert("user_b", 5)   # Expires at 1005
print(tm.get("user_b"))  # Returns 1005
# Time moves to 1007
print(tm.get("user_b"))  # Returns -1, lazily removes "user_b"
print(tm.count_active()) # Returns 1

Edge Cases

  • Accessing a token exactly at its expiration time.
  • Repeatedly updating the same token ID.
  • Calling count_active when many tokens are expired but haven't been accessed.

Hints

Hint 1

Use a hash map to store the mapping between token IDs and their absolute expiration timestamps.

Hint 2

Consider using a min-heap to keep track of tokens ordered by their expiration time, allowing you to identify expired tokens quickly.

Hint 3

For count_active, keep a running counter that decrements only when a token is explicitly proven to be expired during a 'get' or 'upsert' operation.

Follow-up questions

  • How would you modify this design if you needed to support an 'evict all expired tokens' operation that runs in O(k log n) where k is the number of expired tokens?
  • If the system experiences a massive burst of upserts, how could you prevent the heap from becoming a bottleneck?
  • How would this design change if you needed to support distributed access across multiple server nodes?