Skip to main content

Memory and Persistence

The network and instance state model that Lyquid developers use through state! is not a key-value object model. A running Lyquid is WASM bytecode backed by LyteMemory, the virtual memory layer behind Direct Memory Architecture (DMA).

The useful mental model is a suspendable cloud instance with a preserved memory image: Rust objects live in a fixed guest address space, while the host loads memory pages as they are touched and writes changed pages back later. This is the mechanism underneath both network and instance state variables.

Because LyteMemory is transparently exposed as normal linear memory to the WASM runtime, JIT-compiled Lyquid code can access state with ordinary load/store instructions. The result is close to native in-memory data-structure performance on the host, without a serialization or storage-lookup step on every access. That is the main developer benefit: shape state as in-memory Rust data types rooted in state!, and let LyteMemory preserve them across calls.

Segments

LyteMemory has three logical segments:

SegmentUseLifetimeScope
VolatileScratch space and host I/O buffersOne callCurrent call
NetworkShared state for sequenced executionAcross calls and versionsAll nodes at one LyquidNumber
InstanceNode-local stateAcross callsOne node's hosted Lyquid

Network memory is versioned by sequencing. Instance memory is node-local and not versioned. Both use the same DMA model: stable guest addresses, lazy page loading, and dirty-page persistence.

The Lyquor VM and generated LDK code handle segment selection for you. State declared in state! becomes rooted, long-lived data in the matching segment, effectively 'static for that deployed Lyquid layout. Local stack values and heap allocations made during a call remain ordinary Rust temporaries and are dropped normally unless their contents are moved into declared state. For Lyquid-level globals, use state!; do not try to create persistence by leaking pointers or relying on mutable static values.

Under the hood

Lyquid developers should not call state_set or state_get directly. The LDK uses low-level host APIs for its own state bookkeeping, and the Lyquor VM uses the same underlying stores for LyteMemory paging. Page keys identify LyteMemory pages; values are chunked page contents.

Atomicity and Durability

Network and instance state are both in persistent LyteMemory, but they have different durability boundaries.

Network state is versioned. Atomicity happens at the version commit boundary, and that boundary is deterministically defined by the sequence backend. Once a call is sequenced, its durability is already determined by the sequence backend, even before Lyquor VM execution: the VM must eventually execute that sequenced call. The developer mental model is similar to a replicated log in cloud systems, and also to blocks in a blockchain: each committed LyquidNumber is a clear, well-defined state boundary.

Instance state has call-level atomicity, but durability is best effort. A completed instance call may update node-local memory, but a crash can still revert it to an earlier flushed state. There is no block-like cutoff where you can say the instance state is guaranteed durable.

Treat instance state as a persistent cache: it is long-lived and useful across calls, but not the right surface for data that needs explicit commit/abort durability.

Planned API

The LDK is expected to expose a dedicated key-value style host API for instance functions that need explicit data commits. That API would be separate from state! memory.

Current Layout Constants

These constants are defined by the current LDK in the lyquid crate:

ConstantCurrent ValueDescription
LYTESTACK_BASE0x30000000Base address for Lyquid call stacks
LYTEMEM_BASE0x80000000Base address for persistent LyteMemory
LYTEMEM_SIZE_IN_MB4096Total WASM linear-memory cap
NETWORK_MEMSIZE_IN_MB1024Network segment cap
INSTANCE_MEMSIZE_IN_MB1024Instance segment cap
VOLATILE_MEMSIZE_IN_MB1024Volatile segment cap

Normal Lyquid application code should not need these constants. Treat them as implementation constants, not values to copy into application logic. If you are writing low-level code that genuinely depends on the layout, import them from lyquid instead of duplicating the numbers.

Future LDK versions may improve or change the layout constants for newly built Lyquids. A deployed Lyquid's bytecode is built against one layout, and its persisted pages and pointer catalogs use that layout. That fixed deployed layout is what keeps older working Lyquid bytecode compatible when newer LDKs evolve.

Takeaway

  • Treat variables defined in state! as the Lyquid's global variables. They are the persistent roots your functions read and mutate across calls.
  • Avoid dependencies that hide mutable process-global state, including static mut, mutable static values behind locks, thread-local mutable caches, or singleton registries, when they are directly or indirectly used by the type of a Lyquid state variable.
  • Treat instance state as a node-local persistent cache with best-effort durability.