Helios

Persistence

Memory and Redis stores, key layout, and ownership.

By default the source uses MemoryOutputStore. That is fine for tests and apps that intentionally start empty. State disappears when the process exits.

For durable targets, use the Redis subpath:

npm install @helioslx/core redis
import { RedisOutputStore } from "@helioslx/core/redis";

const store = new RedisOutputStore({
  url: process.env.REDIS_URL,
  namespace: "my-lighting-app",
  version: 1,
});

Pass Redis URLs through a secret manager or protected environment. Do not hard-code or log credentials.

What is stored

Output records keep:

  • universe and priority
  • stable CID
  • source-name suffix
  • idle frame rate
  • 512-slot target frame
  • update timestamp

Not stored: in-progress interpolation, sequence numbers, last-sent frames, scheduler deadlines, or transient errors. On restore, the engine resumes toward the persisted target.

Viewer persistence stores the selected universe list and an update timestamp.

Keys

<namespace>:v<version>:output:<universe>:<priority>
<namespace>:v<version>:viewer

Defaults are namespace helioslx and version 1. Treat the key format as an adapter detail — do not write these keys yourself.

A namespace/version pair is one schema. When stored record shape changes, migrate or bump version. Old keys are not deleted automatically; keep them for rollback, then remove them under an explicit retention policy.

Corrupt or incompatible records raise PersistenceError on load. They are not silently coerced into live output.

Ordering and failures

Mutations and store writes are ordered. A successful mutation returns only after its target is saved. A store failure rejects with PersistenceError — do not assume durable state changed.

Redis connectivity is not application HA. Configure Redis persistence, replication, backups, and failover for the venue, and rehearse startup/shutdown with that setup.

Client ownership

When the store creates a client from url, it connects lazily and closes that client when the store closes.

When you inject a client, it must already be connected and stays caller-owned by default:

const client = createClient({ url: process.env.REDIS_URL });
await client.connect();

const outputStore = new RedisOutputStore({ client, closeClient: false });
const viewerStore = new RedisViewerStore({ client, closeClient: false });

// Close services first, then:
await client.quit();

Set closeClient: true only when exactly one store owns the client. Two stores both closing the same client will race.

Operations checklist

  • Dedicated namespace per environment and application
  • Credentials scoped to the needed key prefix when possible
  • Encrypt remote Redis; keep it on trusted networks
  • Monitor connection, latency, memory, persistence, and replication
  • Back up before schema migrations; rehearse rollback
  • Never treat Redis as an emergency-stop or safety interlock

On this page