ReferenceAsk
Remy Reference/Architecture/Architecture/App Databases
10Architecture

App Databases

The file an app's data lives in, the pod that owns it while it is in use, and where the durable copy ends up at rest.

Each Remy app has its own database: a SQLite file, durable in S3 in the workspace's region, owned by one app database pod at a time while it is in use. There is no shared schema, no cross-tenant query surface, and no row-level security to misconfigure. This chapter covers the file, the ownership model that makes it fast, the flush model that makes it durable, and how the platform keeps it in the workspace's chosen region.


#One File Per Database

An app's tables are defined as TypeScript files. At build time the platform parses them, diffs them against the live schema, and applies DDL to a clone before promoting. See Build and Deploy. The result is a SQLite database file whose tables are exactly the app's tables plus the system columns the platform adds to every table: an id, created and updated timestamps, and the identity of the last writer, set from the auth context of the method that wrote the row.

The file is the isolation unit. An app's queries run against its file and no other. A method reaches its database through the SDK, which sends SQL to the platform with the execution's hook token; the platform resolves the token to a release and an environment, and the environment's release id is the data-plane key that names the file. A live request, a preview, and a dev session each name different files.

The file is also the portable unit. Exporting a database is downloading it.


#Ownership

The app database role is a Deployment of several pods. At any moment, each database has at most one owner pod, recorded in Redis at a key for that database with the owner's address and a TTL. The owner holds the SQLite connection open for the life of its ownership; reads and writes hit that connection directly.

When a query arrives at a pod:

  1. It re-checks ownership against its own address. The caller's lookup is a routing hint; this check is the correctness gate.
  2. If it owns the database, it runs the query.
  3. If nobody owns it, it claims ownership atomically and hydrates the file from S3.
  4. If another pod owns it, it proxies the request to that pod.
  5. If the proxy is refused because the owner is gone, it force-releases and claims.

Owners heartbeat their TTLs from a background loop. A pod that dies without releasing loses its ownership when the TTL expires, and a reverse index of what each pod owned lets the surviving pods reclaim its databases without scanning the keyspace. Every live pod also registers in a Redis hash with a heartbeat, so a caller can pick a live pod for a fresh claim.

SQLite execution runs in a worker-thread pool inside the pod, so the connection layer never blocks the request thread.


#Flushing

Every write schedules a flush. After two seconds of quiet the file is uploaded to S3; a ten-second ceiling guarantees that sustained writes still flush regularly. A database is held warm for an hour after its last query, then released: connection closed, ownership deleted, disk freed.

Rollouts drain cleanly. On shutdown a pod first refuses new ownership claims, then stops accepting connections, then flushes every dirty database and releases ownership one by one, and only then deregisters. The ordering closes the deploy-window race where a dying pod could re-claim a database it released a moment earlier. The pod keeps its registry entry alive until the drain is done, so if it is killed mid-drain, the surviving pods' sweep can still reclaim what it held.


#The Working-Copy Model

A workspace can choose a storage region, and its app's database lives at rest in that region's bucket. Compute runs in one region. For a database whose home is elsewhere, a flush every two seconds across regions would be slow and costly, so the platform keeps a working copy.

TierWhereRole
Hot working setThe owner pod's memory and local diskEvery read and write
Working copyThe compute region's bucketEvery debounced flush lands here while the database is hot
Durable copyThe home region's bucketThe authoritative at-rest copy

The working copy is always at least as fresh as the durable copy. While a database is continuously hot, it is checkpointed to the durable copy at least hourly. When it goes cold, it is finalized: the durable copy is written and the working copy deleted. A sweep finalizes anything a dying pod left behind. Nothing lives only in the compute region for more than about an hour past its last write. A database whose home is the compute region has no working copy at all; working and durable are the same object.

The durable object is created exactly once, when the database is created, and a missing object on read is an error rather than an empty file. That invariant is what makes a misplaced file fail loudly at first touch instead of silently serving empty tables.


#Durability and Recovery

The private bucket that holds database files is versioned, so recent prior versions of a file remain available after an overwrite. The bucket is encrypted at rest and blocks all public access. Reads are HTTPS only.

A release build never applies DDL to the live file. It clones the file, applies the schema change to the clone, and promotes the clone on success. The previous release's file is left in place, which is what makes rollback to a prior release a pointer change.


#Snapshots for Shadow Runs

A shadow run must see the world as it was when the human's invocation began. Before dispatching such a method, the platform snapshots each of the release's databases on its owner pod into a local mirror, and every query tagged with that run's snapshot id is served from the mirror instead of the live handle. Mirrors are local only, never flushed, and disposed when the run settles. See Self-Hosted Models and Jewels.

What This Gives an App
Its own database, structurally separate from every other app's.
Reads and writes against an open connection on one pod, not a hydrate per query.
Durability within seconds of every write, and clean handoff on every rollout.
Its data at rest in the region its workspace chose.
Export as a file, and schema changes that never touch live data until they have succeeded.