Skip to content

Redis State Keys

Canonical reference for every Redis key used by Databús. No other module should hardcode these key strings — they are all defined in backend/runs/domain/telemetry/keys.py and imported from there.

Namespace rule

vehicle:<id>:* — written by the edge (MQTT consumer). run:<id>:* and runs:* — written by the server (lifecycle actions, progression step).


Vehicle-keyed keys (vehicle:<id>:*)

These keys hold data that originated from the vehicle's on-board equipment and arrived via MQTT. The realtime-engine writes them; the schedule-engine reads them.

vehicle:<vehicle_id>:position

Attribute Value
Redis type Hash
Function keys.position_key(vehicle_id)
Writer MQTT consumer (realtime_engine/mqtt.py)
Reader schedule_engine/tasks.py::build_vehicle_positions, build_trip_updates
TTL None (lives until run cleanup)

Fields:

Field Type Required Notes
latitude float Yes WGS-84 decimal degrees
longitude float Yes WGS-84 decimal degrees
bearing float No Degrees clockwise from north
speed float No Metres per second
odometer float No Metres
timestamp int No Unix epoch seconds; lifted to VP-level by the feed builder

vehicle:<vehicle_id>:occupancy

Attribute Value
Redis type Hash
Function keys.occupancy_key(vehicle_id)
Writer MQTT consumer (realtime_engine/mqtt.py)
Reader schedule_engine/tasks.py::build_vehicle_positions
TTL None

Fields:

Field Type Required Notes
occupancy_status str (enum) Yes Server-bucketed; edge value discarded (see below)
occupancy_percentage int No Raw percentage from edge
occupancy_count int No Raw passenger count from edge

Server policy: occupancy_status is recomputed

The edge device may send an occupancy_status value, but the MQTT consumer discards it and recomputes it server-side using occupancy.classify_status(occupancy_percentage). Only the percentage is trusted from the edge; the enum assignment is a server policy decision.

Thresholds (backend/runs/domain/telemetry/occupancy.py):

Percentage occupancy_status
None NO_DATA_AVAILABLE
< 20 MANY_SEATS_AVAILABLE
< 50 FEW_SEATS_AVAILABLE
< 80 STANDING_ROOM_ONLY
>= 80 FULL

The remaining GTFS values (EMPTY, CRUSHED_STANDING_ROOM_ONLY, NOT_ACCEPTING_PASSENGERS, NOT_BOARDABLE) require product input before threshold ranges can be assigned.


vehicle:<vehicle_id>:metadata

Attribute Value
Redis type Hash
Function keys.metadata_key(vehicle_id)
Writer Lifecycle action (runs/domain/lifecycle/actions.py) on run start
Reader schedule_engine/tasks.py::build_vehicle_positions, build_trip_updates
TTL None

Fields:

Field Type Required Notes
id str Yes Vehicle identifier
label str Yes Human-readable label
license_plate str No
wheelchair_accessible str No GTFS wheelchair_accessible enum

vehicle:<vehicle_id>:current_run

Attribute Value
Redis type String
Function keys.current_run_key(vehicle_id)
Writer Lifecycle action on run start
Reader MQTT consumer (to route incoming telemetry to the correct run)
TTL None (cleared by run cleanup)

Holds the run_id (UUID string) of the run currently assigned to this vehicle. The MQTT consumer reads this on every incoming message to determine which run-keyed keys to update.


Run-keyed keys (run:<id>:*)

These keys hold data that the server computes and maintains. They are the canonical source for GTFS-RT feed building.

run:<run_id>

Attribute Value
Redis type Hash
Function keys.run_key(run_id)
Writer Lifecycle action
Reader Feed builders, detection layer
TTL None (explicit cleanup on run end)

Fields (non-exhaustive):

Field Notes
trip_id GTFS trip_id
route_id GTFS route_id
direction_id GTFS direction_id
shape_id GTFS shape_id (used for map-matching geometry)
schedule_relationship GTFS schedule_relationship enum
start_time HH:MM:SS
start_date YYYYMMDD
vehicle vehicle_id
operator operator_id
run_lifecycle_state Current FSM state (see lifecycle states)

The run:<id>:trip hash is the GTFS-RT-shaped projection of the trip subset of this hash.


run:<run_id>:trip

Attribute Value
Redis type Hash
Function keys.trip_key(run_id)
Writer Lifecycle action
Reader Feed builders
TTL None

Fields:

Field Type Required
trip_id str Yes
route_id str Yes
direction_id int No
schedule_relationship str No
start_time str (HH:MM:SS) No
start_date str (YYYYMMDD) No

This is a GTFS-RT TripDescriptor projection. The feed builder defaults schedule_relationship to SCHEDULED when the field is absent.


run:<run_id>:vehicle_stop_status

Attribute Value
Redis type Hash
Function keys.stop_status_key(run_id)
Writer Server progression step (runs/domain/progression/compute.py)
Reader Feed builders, RunCompletedDetector
TTL None

Fields:

Field Type Required Notes
current_status str (enum) Yes INCOMING_AT, STOPPED_AT, IN_TRANSIT_TO
current_stop_sequence int No GTFS stop_sequence
stop_id str No GTFS stop_id

Run completion is detected when current_status == STOPPED_AT at a terminal stop. See Run lifecycle › Detection layer.


run:<run_id>:congestion_level

Attribute Value
Redis type Hash
Function keys.congestion_key(run_id)
Writer Deferred — no producer yet; key reserved
Reader Feed builders (tolerant: skipped when absent)
TTL None

Fields:

Field Type Notes
congestion_level str (enum) UNKNOWN_CONGESTION_LEVEL, RUNNING_SMOOTHLY, STOP_AND_GO, CONGESTION, SEVERE_CONGESTION

Deferred

A single bus's speed is a weak signal for congestion. An honest estimate requires fleet aggregation or a traffic feed. The key is reserved and the contract defined in runs/domain/telemetry/congestion_level.py, but no producer writes it yet. Feed builders read it tolerantly — absent hash means no congestion_level field in the GTFS-RT entity.


run:<run_id>:stop_time_updates

Attribute Value
Redis type String (JSON-encoded array)
Function keys.stop_time_updates_key(run_id)
Writer Stop-times producer (runs/domain/progression/stop_times.py)
Reader schedule_engine/builders.py::build_trip_update_entity
TTL Staleness TTL (set by producer; absent key = skip stop_time_update in feed)

Not a hash

This key is a Redis string holding a JSON-encoded array. Do not use HGETALL on it — use GET. The other run:<id>:* keys are hashes; this one is the exception.

Per-entry schema:

{
    "stop_sequence": 42,
    "stop_id": "STOP_ID",
    "arrival_time": 1718800000,
    "departure_time": 1718800000,
    "uncertainty": 60
}
Field Type Notes
stop_sequence int GTFS stop_sequence
stop_id str GTFS stop_id
arrival_time int POSIX seconds
departure_time int POSIX seconds (equals arrival_time currently)
uncertainty int Seconds of uncertainty

The feed builder treats a missing or empty value as "no stop_time_update entries in this TripUpdate entity."


Tracking and set keys (runs:*)

runs:last_seen:<run_id>

Attribute Value
Redis type String
Function keys.last_seen_key(run_id)
Writer MQTT consumer — written synchronously on every message
Reader scan_stale_runs task
TTL None

Holds an ISO-8601 timestamp of the last telemetry received for this run. Written synchronously (not via the Celery task queue) so staleness detection is never delayed by queue latency.

Used by scan_stale_runs to trigger run_tracking_lost (> 60 s staleness while IN_PROGRESS) and run_tracking_expired (> 300 s while NO_SIGNAL).


runs:tracking

Attribute Value
Redis type Set
Writer Lifecycle action
Reader scan_stale_runs, RunTrackingStartedDetector
TTL None

Set of run_id values for runs that have started receiving telemetry (i.e., have reached Tracking state or beyond). Used as the scan target for stale-run detection.


runs:in_progress

Attribute Value
Redis type Set
Writer Lifecycle action
Reader Feed builders (build_vehicle_positions_feed, build_trip_updates_feed)
TTL None

Set of run_id values for runs currently in In Progress state. The feed builders iterate this set to determine which runs to include in the GTFS-RT output.


Decommissioned keys

The following keys appear in older documentation and must not be used:

Old key Replaced by
vehicle:<id>:progression run:<id>:vehicle_stop_status (stop fields) + run:<id>:congestion_level
vehicle:<id>:data vehicle:<id>:metadata
run:{id} (curly brace form) run:<id> (angle bracket form)

See AGENTS.md §State Management for the historical list (now superseded by this page).