Chapter 12 · Time Series and Geospatial Workloads
Model Metrics/Telemetry and Location Data with Bounded Cardinality and Retention
Design telemetry and location schemas that bound key, label, member, and retention cardinality before operational cost becomes the failure mode.
Learning outcomes
AtlasMart is ready to move from toy sensor and courier keys to thousands of devices. At this point the hardest failure mode is no longer command syntax: it is uncontrolled series count, label cardinality, location-member growth, retention, and hot-key concentration.
Estimate series cardinality from stable dimensions before creating keys.
Separate low-cardinality indexed labels from high-cardinality identities embedded in key names or application state.
Use raw/rollup retention tiers without claiming wall-clock deletion guarantees.
Model current location separately from historical location events.
Build an evidence sheet for memory, selectivity, write rate, query width, and p50/p95/p99 latency.
All Chapter 12 mandatory labs reuse the disposable Chapter 01
environment: Redis Open Source 8.10.1 from Docker
Official Image redis:8.10.1, container
atlasmart-redis-ch01, standalone topology, host
publication 127.0.0.1:6379, TLS disabled only
because traffic stays on loopback, default ACL user disabled,
named users atlasmart-app and
academy-admin, logical database 0, AOF with
appendfsync everysec plus RDB snapshots,
persistent /data, and no explicit
maxmemory limit or eviction policy. Redis 8
integrates Time Series into Redis Open Source; geospatial
commands are core Redis commands. Fixtures stay under
atlasmart:ch12:*. Mandatory work is local and
uses synthetic telemetry/coordinates only; no paid service,
production endpoint, or real credential is required.
1. Cardinality is multiplication, not a vague “lots of metrics” problem
If AtlasMart stores 20 sites × 4 zones × 500 sensors × 6 metrics, the upper bound is 240,000 time-series keys before replicas, rollups, or environments. Adding a label with 10 million request IDs does not merely add text—it changes metadata-index scale and query selectivity.
| Dimension | Good role | Risk |
|---|---|---|
| site | Bounded label | Small controlled vocabulary |
| zone | Bounded label | Useful aggregation grouping |
| metric | Bounded label | Core query dimension |
| sensor ID | Usually key identity; optional label only when needed | High but stable cardinality |
| request/session/trace ID | Do not make TS label by default | Unbounded metadata cardinality |
| tenant | Use key/ACL boundary plus bounded label for query convenience | Label alone is not security isolation |
2. Use a key schema that carries stable identity and bounded labels for query dimensions
A practical pattern is
atlasmart:ch12:telemetry:{tenant}:site:metric:sensor, with labels repeating only the dimensions you actually query
across. Duplication between key text and labels is sometimes
intentional: keys support ACL/routing/debugging while labels
support Time Series selection.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s01 RETENTION 86400000 LABELS tenant atlasmart site baku-01 zone cold-a metric temperature unit Cdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s02 RETENTION 86400000 LABELS tenant atlasmart site baku-01 zone cold-a metric temperature unit Cdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:telemetry:atlasmart:baku-01:humidity:s01 RETENTION 86400000 LABELS tenant atlasmart site baku-01 zone cold-a metric humidity unit pctdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.QUERYINDEX tenant=atlasmart site=baku-01
The lab horizon is illustrative, not a production recommendation. Choose retention from audit/investigation/business requirements and measured cost.
3. Wrong approach: put unique request IDs into labels
A telemetry pipeline adds request_id as a label
because “labels make filtering easy.” Every series now has
effectively unique metadata, and the label index becomes less
reusable and more expensive.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:badlabel:1 RETENTION 60000 LABELS tenant atlasmart metric temperature request_id req-000001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:badlabel:2 RETENTION 60000 LABELS tenant atlasmart metric temperature request_id req-000002docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.QUERYINDEX request_id=req-000001docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch12:badlabel:1
Two keys do not prove a memory crisis; they prove the modeling direction. The repair is to keep request/trace IDs in a logging/event system or Stream payload, and reserve Time Series labels for bounded dimensions that provide meaningful selection/grouping.
4. Retention tiering: raw detail and derived summaries answer different horizons
| Tier | Example purpose | Design question |
|---|---|---|
| Raw telemetry | Incident/debug horizon | How long must individual samples remain queryable? |
| 1-minute rollup | Dashboard/operations | Which reducers preserve the operational signal? |
| Hourly/daily rollup | Long-term planning | What precision is acceptable and what rebuild path exists? |
| Archive outside Redis | Compliance/cold analytics | Does Redis need to hold this horizon at all? |
Do not use Time Series retention as an exact legal erasure clock. It is driven by reported timestamps and subsequent writes. If a privacy/compliance workflow requires deletion by a deadline, use explicit data-management controls and verify final state.
5. Current GEO state and historical location need separate cardinality plans
For 50,000 active couriers, one GEO member per courier is 50,000 current positions—not every position ever reported. Historical location samples should be written to a time-series/stream model with bounded retention. Reusing a courier member name updates current location and naturally bounds GEO member cardinality to active entities.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:couriers:atlasmart 49.8671 40.4093 courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:location-history:atlasmart:courier-42:lon RETENTION 3600000 LABELS tenant atlasmart entity courier-42 axis londocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.CREATE atlasmart:ch12:location-history:atlasmart:courier-42:lat RETENTION 3600000 LABELS tenant atlasmart entity courier-42 axis latdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.MADD atlasmart:ch12:location-history:atlasmart:courier-42:lon 1700000000000 49.8671 atlasmart:ch12:location-history:atlasmart:courier-42:lat 1700000000000 40.4093docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app ZCARD atlasmart:ch12:geo:couriers:atlasmart
Two separate numeric series are simple for teaching but can create many keys. In production, evaluate whether a Stream/event document, external telemetry store, or application encoding fits multi-dimensional location history better.
6. Memory budget must include more than sample payloads
A capacity sheet should include key names, Time Series
metadata/labels, chunks, compaction destinations, GEO sorted
sets, client/output buffers, persistence/replication buffers,
allocator fragmentation, and fork headroom.
maxmemory is not process RSS and is not configured
in this lab.
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO memorydocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s01docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s02docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app MEMORY USAGE atlasmart:ch12:geo:couriers:atlasmartdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.INFO atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s01
Record the exact server build and dataset shape beside every byte count. Memory measurements on three tiny keys are a method demonstration, not a production sizing result.
7. Query selectivity is a first-class metric
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.QUERYINDEX tenant=atlasmart site=baku-01 metric=temperaturedocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.QUERYINDEX tenant=atlasmartdocker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app TS.MRANGE - + COUNT 2 FILTER tenant=atlasmart site=baku-01 metric=temperature
Track “series matched” before “samples returned.” A narrow time window can still be expensive if the label predicate selects hundreds of thousands of series. Conversely, one hot series with an enormous range can be expensive even with perfect metadata selectivity.
8. Measure latency distributions without fabricating benchmark numbers
The academy does not have a Redis runtime in this generation environment, so Chapter 12 does not publish invented p50/p95/p99 values. The lab records server-side command latency statistics after you run representative workloads.
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO commandstatsdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin INFO latencystatsdocker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin LATENCY DOCTOR
For production acceptance, pair server metrics with client end-to-end histograms and record payload size, concurrency, pipeline depth, network placement, persistence settings, and CPU/memory pressure. Tail latency is workload-specific.
9. Hot keys can dominate otherwise acceptable cardinality
A single global GEO set receiving every courier update or one global telemetry series receiving every tenant event can concentrate CPU and network load. Partitioning can help, but only when the query contract survives partitioning. A city/tenant partition that forces fan-out across every shard may merely move cost.
Redis Cluster uses 16,384 hash slots and multi-key operations have slot-locality constraints. Chapter 21 covers routing/resharding in depth. For now, include hash-tag/key-placement assumptions in every design review rather than assuming standalone behavior scales unchanged.
10. Failure injection: retention and location staleness are different failure modes
If telemetry producers stop, retention may stop advancing because no new timestamps arrive; “old” samples do not disappear merely because wall-clock time passes. If location producers stop, the GEO member can remain indefinitely as a stale current position unless the application also tracks freshness. Model freshness explicitly.
docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOADD atlasmart:ch12:geo:couriers:atlasmart 49.8750 40.4150 courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app SET atlasmart:ch12:geo-freshness:atlasmart:courier-42 1700000060000 EX 300docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GEOPOS atlasmart:ch12:geo:couriers:atlasmart courier-42docker exec -e REDISCLI_AUTH=AtlasMart-App-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user atlasmart-app GET atlasmart:ch12:geo-freshness:atlasmart:courier-42
The TTL key is illustrative freshness metadata. If it expires, the GEO member still exists; the application must remove or ignore stale location entries according to policy. Do not mistake a side freshness key for atomic coupling.
11. Cleanup
docker exec -e REDISCLI_AUTH=AtlasMart-Admin-Lab-Only-2026 atlasmart-redis-ch01 redis-cli --user academy-admin UNLINK atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s01 atlasmart:ch12:telemetry:atlasmart:baku-01:temperature:s02 atlasmart:ch12:telemetry:atlasmart:baku-01:humidity:s01 atlasmart:ch12:badlabel:1 atlasmart:ch12:badlabel:2 atlasmart:ch12:geo:couriers:atlasmart atlasmart:ch12:location-history:atlasmart:courier-42:lon atlasmart:ch12:location-history:atlasmart:courier-42:lat atlasmart:ch12:geo-freshness:atlasmart:courier-42
UNLINK targets only the named Chapter 12 fixtures.
Do not replace this with FLUSHDB,
FLUSHALL, or broad pattern deletion on a shared
Redis instance.
12. Production judgment
A robust telemetry/location model has explicit formulas for key count, label cardinality, samples per second, bytes/sample measured on representative data, retention tiers, derived-series multiplier, active GEO members, update frequency, query selectivity, and failure freshness. It documents AOF/replication headroom, Cluster slot strategy, ACL/TLS boundaries, retry/idempotency rules, deletion/privacy workflows, and rollback/rebuild plans. Avoid universal thresholds such as “one million series is fine”; benchmark your topology and budget.
Check your understanding
- Why is series count often multiplicative?
- Why is request_id usually a bad Time Series label?
- Why does a GEO member update help bound current-state cardinality?
- Can retention remove stale GEO members?
- What should p99 latency be compared with?
Review the answers
Each combination of identity dimensions and metrics can create a separate key, so stable dimensions multiply.
It has very high/unbounded cardinality and rarely creates reusable query groups.
The same member name replaces its coordinate instead of appending another member.
No. Time Series retention applies to Time Series samples; GEO freshness/removal needs separate policy.
A workload-specific service-level objective measured under representative payload, concurrency, network, persistence, and resource pressure—not a universal number.
13. Summary and next step
You now have a capacity-aware model for Time Series and GEO rather than a command demo. Lesson 5 composes these structures with Streams, Search, and application-side processing while preserving data lineage, authorization, retries, and consistency boundaries.
Authoritative references
- Redis Open Source 8.10 release notes
- Redis 8.10 command reference
- Redis Time Series data type
- Time Series configuration
- TS.CREATE
- TS.ADD
- TS.INFO
- TS.RANGE
- TS.MRANGE
- TS.CREATERULE
- TS.QUERYINDEX
- Redis geospatial data type
- GEOADD
- GEOSEARCH
- GEODIST
- MEMORY USAGE
- INFO
- LATENCY DOCTOR
- Redis memory optimization