7. Storage, Displays and Distribution
This section discusses where the data goes once a logger has it, including:
- the cached data server that feeds everything live
- any external databases that keep history
- the displays people actually look at
- mirroring it all ashore, and
- spreading the load across machines when one stops being enough.
The Cached Data Server
The cached data server (CDS) is the live hub of a running
installation: an in-memory cache of the recent values of every named
field, served to subscribers over websockets. The standard
installation runs one under supervisor on port 8766, with a disk
cache that re-warms the memory cache across restarts and retention
limits (--back_seconds, --max_records) that keep it from growing
without bound. It is deliberately not long-term or archival storage — the logfiles and
any external database are the records — but everything live flows through it:
console status, display widgets, derived-data loggers, and your own
subscribers.
Data gets in through a logger’s CachedDataWriter, which forwards
each record as a websocket publish message (and buffers locally if
the connection drops). Data gets out through subscriptions, and one
piece of subscription vocabulary pays for itself everywhere, because
CachedDataReader, the display widgets and listen --cached_data
all share it — the per-field seconds value:
seconds |
First response contains |
|---|---|
0 |
nothing — only values that arrive after subscribing |
-1 |
the single most recent value, then new values as they arrive |
N |
up to N seconds of history, then new values as they arrive |
Subscriptions can also name fields with * wildcards, guarantee a
minimum number of historical records (back_records), slow the push
rate for low-bandwidth clients (interval), and ask for output
collated by timestamp (format: record_list) instead of the default
per-field lists.
For one-off queries — a shell script, an elog entry, anything that doesn’t want a websocket — the server also answers plain HTTP GET on the same port:
# List every field the server currently has
curl http://localhost:8766/fields
# Get the latest value and timestamp of particular fields
curl http://localhost:8766/latest/S330CourseTrue,S330SpeedKt
Keep HTTP for occasional queries: the requests share the event loop
with every websocket client, so polling it hard delays data delivery
to everyone else. Anything continuous belongs on a subscription —
via listen --cached_data at the terminal, a CachedDataReader in a
logger, the websockets library from external Python, or a raw
browser WebSocket. The
Cached Data Server
page documents the full protocol, every client pattern, and the
server’s flags.
External Databases
OpenRVDAS has no database of its own. The cached data server holds only recent values, and the logfiles hold raw records, so for history you can query — a week of heading, a cruise’s worth of sea surface temperature — loggers write their parsed fields to an external database. Which database is your choice. To OpenRVDAS it is just another destination, reached through a writer.
The writers available:
InfluxDBWriter, for InfluxDB, a time-series database. This is the most common choice, and the one the rest of this section uses as its example.DatabaseWriter, a generic writer with connectors for MySQL, MariaDB, PostgreSQL and MongoDB. Each connector has a setup script underdatabase/.RedisWriter, for publishing to Redis.- Contributed writers in the openrvdas_contrib repository, such as a TimescaleDB writer.
- Your own. A writer is any class with a
write(record)method (see Creating and Testing Loggers), so if your institution already runs a database, a writer for it is a short piece of code.
Whichever you use, the pattern is the same: the database writer sits
in a parsing logger next to its CachedDataWriter, so each parsed
record goes both to the live hub and into history.
InfluxDB
Most OpenRVDAS installations use InfluxDB, mostly because it pairs
well with Grafana for displays. Each parsed field is stored as a time
series, and InfluxDB’s query language, Flux, can select, aggregate
and transform them. Installation and credentials are covered in
section 3.
Once database/influxdb/settings.py is filled in, feeding the
database takes one writer:
writers:
- class: CachedDataWriter
kwargs:
data_server: localhost:8766
- class: InfluxDBWriter
kwargs:
bucket_name: openrvdas
measurement_name: gyr1
measurement_name groups fields under the instrument they came from,
which keeps the database browsable. The sample cruise’s +influx
modes do exactly this.
Three working tips. First, generate Flux queries instead of writing them: build the query in InfluxDB’s Data Explorer (pick fields, set aggregation and range, Submit), then open its Script Editor and paste the resulting Flux into a Grafana panel. Second, InfluxDB tasks can compute: a task is a Flux query run on a schedule, writing its results to another bucket. Tasks are the mechanism behind both the feed-status monitoring dashboard and the shoreside replication below. Third, give the bucket a retention policy. By default InfluxDB keeps everything forever, and on a ship that means the database, not the raw logfiles, is what eventually fills the disk. A bucket’s settings (in the web console, under Load Data, then Buckets) set how long it keeps data; the raw logfiles remain the permanent record, so the database only needs to hold as much history as the displays use.
Displays
Grafana dashboards are the recommended display route: open source, drag-and-drop, maintained by a large community that isn’t us. Grafana reads from most common databases, InfluxDB included. Once it has a data source for yours (for InfluxDB, see section 3), a dashboard is a set of panels, each backed by a query — time series, gauges, stats, maps. Like OpenRVDAS, Grafana takes plugins, and purpose-built marine panels exist: the OpenRVDAS compass panel draws an integrated wind/compass rose from heading and wind fields:

Grafana Live covers the case a database can’t: sub-second,
lag-free display. The GrafanaLiveWriter posts records straight to
Grafana’s live-streaming endpoint — no database in the path, 20 Hz
and up — for the helm display or winch readout where a
store-then-query round trip is too slow. Streamed data isn’t
retained, so loggers that feed Grafana Live usually also write to a
database for the record. Setup is on the
Grafana Live page.
The built-in display widgets predate all of this: Highcharts- and
D3-based dial, timeline, text and map widgets on web pages served by
the console under /display/, fed by direct websocket subscription
to the CDS — the tutorial’s nbp_dashboard.html is one. They still
work, many vessels still run them, and the raw browser-websocket
route they use remains supported; but the widget framework itself is
deprecated, and new display work belongs in Grafana. Their
construction is documented on the legacy
Displays and Widgets
page.
Shoreside Replication
The goal: the same Grafana dashboards ashore that the ship uses at sea, fed over a satellite link that comes and goes — with control over which sensors shore is allowed to see. Most databases have some form of replication. The recipe here is for InfluxDB, and its pieces are all standard InfluxDB machinery driven by OpenRVDAS modes:
- Tag at write time. The parsing logger’s
InfluxDBWritertags every measurementrestricted: 'no'— except in a+influx_restrictedvariant of the config, which tags the sensitive instruments’ measurementsrestricted: 'yes'. Which variant runs is just a cruise mode, so restricting a sensor is a mode change in the console, logged in the journal like any other. - Filter and thin with a task. An InfluxDB task runs every
second, selects where
restricted == 'no', averages the numeric fields over the second, and writes the result to anon_restrictedbucket with a short retention — a smaller, cleaner stream sized for the link. - Replicate the bucket. InfluxDB’s native replication streams
the
non_restrictedbucket to the shore server, queueing across outages and catching up when the link returns. Shore lags the ship; it never breaks. - Point shore Grafana at the replica. The dashboards are the same ones used aboard.
The complete worked configuration — modes, logger configs, the Flux task and the replication commands — is in the InfluxDB Ship-to-Shore Replication cookbook. (And the standing rule it opens with: replicate non-restricted data only.)
Distribution and Load Balancing
One machine runs most installations comfortably. The reason to use more than one isn’t usually capacity — it’s isolation. Logging is the part whose failure loses data forever; databases and displays can crash and catch up. So the aim of splitting an installation across machines is to keep everything else from interfering with the loggers.
Two machines: isolate the database and displays. The first and most valuable split moves the database and displays off the machine doing the logging:

The logging machine runs all of OpenRVDAS: the front-line loggers
(read, timestamp, write raw to disk, broadcast) and the loggers that
parse and compute derived values. The second machine runs the
database and Grafana. The parsing loggers reach it simply by pointing
their database writer at the second machine’s address instead of
localhost.
This guards against the two most common ways a busy installation hurts its own logging. A database that grows until it fills its disk fills the database machine’s disk, not the one the raw logfiles are being written to. And a heavy display load — a room full of scientists browsing dashboards, a runaway query, a Grafana upgrade gone wrong — competes for the second machine’s CPU and memory, not the loggers’.
Three machines: isolate the front-line loggers too. For truly robust operation, also separate the front-line loggers from everything downstream of them — parsing, derived data and the rest:

The first machine now does nothing but read the instruments, write the raw records to disk and broadcast them. A second runs the parsing and derived-data loggers, and a third the database and displays. A malformed record that crashes the parser, a derived-data logger that spins out of control, or a configuration change to the computations then can’t touch the machine holding the serial ports. That machine is so simple that there’s little left to go wrong with it.
The relay from the front-line machine to the parsing machine uses the same mechanisms the loggers already use, reaching across machines instead of across processes:
- UDP broadcast is the lightweight default: the front-line
loggers’
UDPWriters already broadcast to the subnet, and the parsing machine’sparse_datalogger just runs itsUDPReaderthere instead. If a firewall sits between them, the data ports (6221–6226 by convention) must be open, per section 3. - The cached data server is the alternative when you want
delivery guarantees or to cross subnets: writers point their
data_serverathost:portrather thanlocalhost, andCachedDataWriterbuffers through interruptions.
In either arrangement, where the pieces land is pure configuration: the cruise definition’s variables name the UDP destination, data server and database URL, so moving a tier to another machine is a variables change, not a redesign. The shoreside replica above is the same idea stretched over a satellite link — and a second OpenRVDAS installation elsewhere on the ship can always subscribe to the first one’s CDS, which is how systems like CORIOLIX build on OpenRVDAS without touching its loggers.