Recording Response Freshness in Redis Cache

Exposing response freshness with a Redis hot cache, last-success cache, and database fallback

The Hangangjari backend uses Redis to read current values and assembled responses quickly and to find the last successful response when external-source or DB composition fails momentarily.

Cache responses carry source observation time and freshness state. A value read quickly from Redis is still marked stale when its source observation time is old.

Rebuildable Values in Redis

Hangangjari has caches with different purposes.

CacheExamplePurpose
Status cacheLatest parking-lot valueFast lot-level lookup
Forecast cacheForecast overview/timelineReuse computed responses
Home summary hot cacheFirst-screen summaryFast response with short TTL
Home summary stale cacheLast successful summaryFallback when rebuild fails
Source status cacheSource-check resultReduce repeated lookup cost

Reference data and history live in Postgres, while Redis stores query results that can be rebuilt.

When Redis is empty, query results are rebuilt from Postgres. Damage to Postgres reference data or history follows the backup recovery procedure.

flowchart LR
  API["API read"] --> Redis["Redis cache"]
  Redis -->|Hit| Response["Response"]
  Redis -->|Miss| Postgres["Postgres rows"]
  Postgres --> Build["Build payload"]
  Build --> Redis
  Build --> Response

Database Fallback for Parking Cache

Parking values are read frequently by lot. The app, widgets, home summary, and forecast inputs all need the latest parking values.

In code, RedisStatusCache owns the current status cache role. It stores JSON payloads by lot ID key. When a cache payload is malformed or Redis is unavailable, the entry is ignored and the query falls back to DB.

The read path applies two rules.

  • A cache incident falls back to the database.
  • Even a cache hit needs a freshness check.
sequenceDiagram
  autonumber
  participant Query as Parking query
  participant Cache as Status cache
  participant DB as Postgres

  Query->>Cache: Check parking status cache
  alt Cache hit
    Cache-->>Query: Return latest parking status
    Query->>Query: Judge freshness by source time
  else Miss or cache unavailable
    Query->>DB: Check last stored status
    DB-->>Query: Stored status or none
    Query->>Query: Judge stale or empty state
  end

Staleness is judged by the time confirmed at the source, not by the time stored in cache. This preserves the same freshness decision regardless of the Redis TTL.

Fast Cache and Last-Success Cache

home-summary is an expensive response to build. It has to gather parking, forecasts, outing data, and source-check results.

Rebuilding the response uses two kinds of cache.

  • Hot cache: a short-TTL cache for quickly reusing normal responses.
  • Stale cache: a longer-lived backup cache for the last successful payload.

When rebuilding succeeds, both hot and stale cache are updated. If hot cache is empty and rebuilding fails, the stale cache is checked.

flowchart TD
  Request["home-summary request"] --> Hot{"Hot cache hit?"}
  Hot -->|Yes| ReturnHot["Return hot response"]
  Hot -->|No| Rebuild["Rebuild from DB/use cases"]
  Rebuild -->|Success| StoreBoth["Store hot + backup cache"]
  StoreBoth --> ReturnFresh["Return fresh response"]
  Rebuild -->|Failure| Stale{"Backup cache hit?"}
  Stale -->|Yes| ReturnStale["Return backup response"]
  Stale -->|No| Error["Raise error"]

When stale cache is returned, freshness state and source-check results remain in the payload so the UI marks it as “the last confirmed value.” Fallback is not returned without confirmation time or stale state.

Fewer Repeated HTTP Requests

Server-side Redis cache and HTTP cache have different jobs. A home-summary response receives private cache headers and an ETag. This lets the same app instance get 304 when it repeatedly asks for the same first-screen value in a short period.

The response is treated as private cache because it considers app access conditions and request context.

Response Impact of a Source Change

Hangangjari caches are query outputs rebuilt from source and reference data. Invalidation targets patterns of dependent query results.

When forecasts change, forecast cache and home summary cache are affected. When outing source-check results change, home summary is affected too. The parking value cache is refreshed as the polling job writes the latest snapshots.

Invalidation targets forecast and home-summary responses that depend on the changed source.

Freshness Metadata on Fallback Responses

Redis stores rebuildable query results, and source observation time is checked after every cache hit. Hot cache serves fast responses, while stale cache provides the last successful value after rebuild failure. Cache incidents fall back to the DB and remain in metrics, and stale responses preserve freshness state. HTTP private cache separately reduces repeated requests from the same app instance.

Comments

Comments

    Image preview