Normal-Mode Read
This page explains which tiers Get checks in normal mode, when it treats Redis as failed, and how it degrades within the same call.
Flow
graph TB
Start[Get key] --> R1{Memory hit?}
R1 -->|Hit, valid| Hit[Return Data, write back to Redis in background]
R1 -->|Hit, expired| Exp[Delete memory and file, return Not found]
R1 -->|Miss| R2[redis.Get up to MaxRetry times]
R2 -->|OK and parses as Cache| Store[Store in memory and return]
R2 -->|All failed| Switch[Switch to fallback mode]
Switch --> Local[Continue on the fallback read path]
Steps
| Step | Condition | Behavior | Source |
|---|---|---|---|
| 1 | Memory hit, not expired | Return immediately and write back to Redis in a goroutine (Read-Repair) | get.go:24-37 |
| 2 | Memory hit, expired | Delete the memory entry and the local JSON file, return Not found |
get.go:28-33 |
| 3 | Memory miss | Run GET against Redis up to MaxRetry times (default 3); on a parseable result, store in memory and return |
get.go:39-50 |
| 4 | Retries exhausted | Take the write lock, switch to fallback mode (starting the health check), release the lock, then call the Fallback-Mode Read | get.go:52-58 |
Memory First
In normal mode the memory cache takes precedence over Redis. For any key this instance has written or read, Get returns the in-memory value without asking Redis again, so a key changed or deleted externally in Redis still reads as the old value here until it expires.
Retry Behavior
| Item | Behavior |
|---|---|
| Interval | No wait between attempts |
| Per-attempt timeout | go-redis client defaults (the instance only sets Addr / Password / DB) |
| Counted as failure | Connection errors, redis.Nil (key missing), a value that does not parse as the Cache structure |
Which Values Read Back from Redis
Step 3 parses the Redis value into the Cache structure (key / data / type / timestamp / ttl) with json.Unmarshal, but Set writes only the JSON of Data itself to Redis (strings have their quotes trimmed). Observed results:
| Scenario | Result |
|---|---|
Set then Get on the same instance |
Memory hit, returns normally |
Another instance (or after a restart) calls Get for the same key |
Parse fails → switch to fallback → no local file → Not found |
Get for a missing key |
redis.Nil → switch to fallback → Not found |
The last two cases take the instance out of normal mode until the health check's next successful ping completes recovery. See Known Limitations.
Example
value, err := rf.Get("user:1")
if err != nil {
// key missing, expired, or the local file failed to parse
log.Println(err)
return
}
fmt.Println(value)
Get returns only two error messages, Not found and Failed to parse; it never returns a Redis connection error.