Redis / Basic: keys and bounded data structures / Lesson 6
TTL, expiration and validity
Separate a key’s expiration deadline from application validity, eviction and persistence. Model time explicitly rather than claiming precise physical deletion from a short example.
On this page
Lesson goals
- Interpret TTL sentinel replies and expiry-changing writes.
- Design validity checks and cache-lifetime policies from requirements.
Expiry is one lifecycle mechanism
A key expiry gives Redis a deadline after which the key is logically expired. Redis uses passive and active mechanisms to discover expired keys, so physical memory cleanup is not a universal exact-time scheduling service. A cache validity window may need an explicit timestamp or version in the value, especially when the business decision requires immediate rejection. Eviction removes keys for memory policy, not because their TTL elapsed, while explicit deletion and persistence recovery are other causes of absence. Diagnose which mechanism occurred instead of calling every miss an expiry.
Read special TTL values correctly
TTL returns remaining seconds, while PTTL uses milliseconds. On the stated modern baseline, -2 means the key does not exist and -1 means it exists without an expiry. Positive values change with time and scheduling, so the example deliberately fixes only the no-expiry and missing outcomes. It assumes a disposable namespace with no other writer. A successful value write does not imply that an expiration was attached. Managed service policies and concurrent clients can affect observation, so compare the write options and key identity before blaming the clock.
SET lab:ttl:persistent value
TTL lab:ttl:persistent
TTL lab:ttl:never-created
-1
-2
Assuming the second key is absent, expected TTL replies are -1 and -2. No exact countdown is fabricated.
Create value and deadline without a two-command gap
SET with EX or PX can attach expiration to the value in one operation. A separate SET then EXPIRE can leave a persistent key if the client fails between commands. Some mutations preserve an existing expiry, while plain SET normally removes it unless supported options say otherwise. Expiring an entire hash differs from newer field-expiration features. Define which writer owns renewal and whether a read may extend the deadline; silently sliding expiry can keep inactive or revoked state alive longer than intended. Test renewal, replacement and deletion separately.
Model validity with a declared clock
The offline function checks a synthetic absolute deadline with an inclusive expiry boundary: at the deadline, the item is no longer valid. It does not simulate Redis’s active expiry cycle, operating-system time or distributed clock uncertainty. Real applications need a chosen time source and acceptable skew policy, and should not use client-controlled timestamps as unquestioned authority. Keeping the model explicit exposes off-by-one boundary mistakes and separates authorization validity from eventual storage cleanup. This pure computation reads no real token, connects to no service and makes no real-time deletion claim.
def valid_at(now_ms, expires_at_ms):
return now_ms < expires_at_ms
assert valid_at(999, 1000)
assert not valid_at(1000, 1000)
assert not valid_at(1001, 1000)
The item is valid at 999 and invalid at 1000 and 1001. This is an application rule, not a measurement of Redis cleanup.
Choose freshness and load behavior together
A short TTL limits some stale-cache duration but increases misses and source load; a long TTL reduces reloads but extends potential stale use. Jitter can spread synchronized expirations but must stay within the allowed freshness policy. Negative caching, refresh-ahead and stale serving require explicit handling of revocation-sensitive or nonexistent records. Expiry is not a distributed lock correctness proof, and persistence or replica failover can introduce further lifecycle questions. Record the intended deadline, maximum acceptable staleness and reload behavior, then test them against the authoritative data and the system’s failure scenarios.
Common errors and fixes
TTL -1 is read as a missing key
- Why it happens
- No-expiry and nonexistent sentinel replies were confused.
- How to fix it
- Interpret -1 as persistent and -2 as absent on the stated baseline.
- Prevent it next time
- Test both sentinel cases separately.
A client crash leaves a value without expiration
- Why it happens
- Value and expiry were written in separate operations.
- How to fix it
- Use an appropriate atomic value-plus-expiry command form.
- Prevent it next time
- Include the between-commands failure case in lifecycle tests.
A token remains usable because it still occupies memory
- Why it happens
- Physical presence was confused with business validity.
- How to fix it
- Enforce the declared validity condition at the decision boundary.
- Prevent it next time
- Separate access expiry from asynchronous cleanup and eviction.
Check your understanding
1. The key exists and TTL returns -1. What is the supported conclusion?
Show worked solution
The key currently has no expiration under the modern command semantics. It is not a missing key and does not prove intended persistence. Inspect which writer created or replaced it and compare that behavior with the key’s lifecycle policy. A plain SET may have removed an earlier deadline, so payload correctness and expiry correctness require separate verification.
2. Can random expiry jitter extend a permission cache beyond its maximum allowed freshness window?
Show worked solution
Not merely to smooth load. Choose jitter bounds that satisfy the business freshness and revocation requirements, or use another refresh/admission strategy. A performance technique cannot silently weaken the authorization contract. Test earliest and latest resulting deadlines and the source behavior when many entries still miss together.
Key takeaways
- Logical expiry, physical cleanup, eviction and validity are distinct.
- Value writes and renewal must preserve the declared lifecycle.