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 redisimport { 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>:viewerDefaults 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