Redis key structure
Three key prefixes are used, each serving a different purpose:| Prefix | Default value | Purpose |
|---|---|---|
indicator_data: | CACHE_KEY_PREFIX | Cached arrays of data points |
indicator_miss: | CACHE_COUNTER_PREFIX | Miss counters per query signature |
indicator_stats: | STATS_CACHE_PREFIX (hardcoded) | Cached statistics objects |
Data cache keys
Data cache keys encode the full query signature so that different parameter combinations produce different cache entries:Extra parameters (skip, limit, sort, start_date, end_date) are always sorted alphabetically before being appended to the key. This guarantees that the same logical query always produces the same cache key regardless of the order query parameters arrive.
Miss counter keys
Miss counter keys track how many times a given base query (indicator + granularity + aggregator) has missed the cache:Statistics cache keys
Statistics cache keys follow a simpler structure — there are no query parameters because statistics are always computed over the full dataset:Cache TTLs
| Setting | Environment variable | Default | Notes |
|---|---|---|---|
| Data cache TTL | CACHE_TTL_SECONDS | 3600 s (1 hour) | Applied to both specific and full data cache entries |
| Miss counter TTL | MISS_COUNTER_TTL | 90 s | Counter expires if no further misses occur within the window |
| Statistics cache TTL | STATS_CACHE_TTL | 15 s | Short TTL because statistics change with every new data segment |
Miss-threshold promotion
The service uses a two-tier caching strategy. The first tier caches the exact result for a specific query (including pagination and date filters). The second tier caches the full, unpaginated dataset for a given indicator + granularity + aggregator combination so that slice queries can be served from cache. Promotion to the second tier is triggered by the miss counter:Cache miss
A request arrives for
indicator_data:{id}:1h:avg:limit:100:skip:0:sort:asc. Neither the specific key nor the full-dataset key exists in Redis. The query runs against MongoDB.Result cached
The query result is stored under the specific cache key with a TTL of
CACHE_TTL_SECONDS (3600 s). The miss counter for indicator_miss:{id}:1h:avg:counter is incremented.Threshold check
If the miss counter reaches
MISS_THRESHOLD (default: 5), a background task is scheduled to cache the full (unpaginated) dataset under indicator_data:{id}:1h:avg with the same TTL.The miss counter itself expires after
MISS_COUNTER_TTL (90 s). If the pattern of requests stops before reaching the threshold, the counter resets and no full-dataset cache is created. This avoids caching datasets for one-off queries.Cache invalidation
Cache invalidation happens automatically whenever a newDataSegment is stored or a resource deletion event is processed. The service scans Redis for all keys matching each prefix and deletes them:
SCAN command with a cursor loop to avoid blocking the server.
Granularity and aggregator parameters
Granularity and aggregator values are part of the cache key, so different aggregation settings produce completely independent cache entries. Two requests for the same indicator with differentgranularity or aggregator values are cached separately and do not share a miss counter.
Granularity format
Granularity is expressed as{amount}{unit}. Supported units:
| Unit | Meaning |
|---|---|
s | seconds |
m | minutes |
h | hours |
d | days |
w | weeks |
M | months |
y | years |
0 (or omit the parameter) for raw data with no time-bucketing.
Aggregator values
| Aggregator | Description |
|---|---|
last (default) | Last value in each time bucket |
first | First value in each time bucket |
sum | Sum of all values in each bucket |
avg | Arithmetic mean |
median | Median value |
max | Maximum value |
min | Minimum value |
p{N} | Nth percentile (e.g. p95 for the 95th percentile) |