Pebellslearn by building
0%
RoadmapsMatrixCoursesSandboxSign in
Durable Objects  ›  Lesson

Persistence — Memory Is a Cache, Storage Is the Truth

Persistence 12 minEach Durable Object has private durable storage, with key-value and SQL APIs and alarms, while its in-memory fields are temporary and may be lost on eviction.

Persistence — Memory Is a Cache, Storage Is the Truth

The first LinkCounter kept its count in a class field, and one quiet night it was evicted: cat went from 4,812 clicks back to 0. Every Durable Object comes with its own private, durable storage attached. Using it turns the counter from a nice demo into something the owner can trust.

The rule

Write to storage; treat memory as a copy you may lose.

Each object has storage at this.ctx.storage, private to that object and kept in the same place it runs, so reads and writes are fast. New classes use the SQLite-backed storage backend (declared with new_sqlite_classes in the migration), which offers two ways in:

A key-value API, handy for a few values:

export class LinkCounter extends DurableObject {
  async increment() {
    const count = ((await this.ctx.storage.get("count")) ?? 0) + 1;
    await this.ctx.storage.put("count", count);
    return count;
  }
}

A SQL API, for anything with rows, filters or history:

constructor(ctx, env) {
  super(ctx, env);
  ctx.storage.sql.exec(
    "CREATE TABLE IF NOT EXISTS hits (at INTEGER, country TEXT)"
  );
}

async record(country) {
  this.ctx.storage.sql.exec(
    "INSERT INTO hits (at, country) VALUES (?, ?)", Date.now(), country
  );
}

Each object has its own little SQLite database. It is separate from D1: D1 is one shared database for the whole app, while this storage belongs to one object and only that object can read it.

Storage also powers alarms. await this.ctx.storage.setAlarm(Date.now() + 60_000) asks the platform to wake the object in one minute and call its alarm() method, even if nothing else is happening. Hoplink uses an alarm to roll the last hour's hits up into a summary and delete the raw rows.

Memory is still useful — as a cache. You may keep a copy of count in a field to avoid a read, as long as every change also goes to storage and the field is reloaded from storage when the object starts.

The shortcut

For any field in a Durable Object, ask "If the object vanished right now, would losing this hurt?" If yes, it must be written to storage before you reply. If no (a cached copy, a WebSocket list, a timer handle), memory is fine.

Deciding what LinkCounter persistsStep 1 of 4
  1. The total count

    Losing it breaks the owner's statistics. Storage.

Trap: writing to storage but reading only from a field that was never reloaded after eviction — the object then reports stale or zero values while the correct number sits in storage.

Drills

Answer fast — trust the rule.

quick drill

Where should LinkCounter keep the total count so it survives eviction?

quick drill

How does a Durable Object's SQLite storage differ from D1?

quick drill

The hourly roll-up must run even when no one visits the link. What should you use?

quick drill

Which of these is safe to keep only in memory?

Remember

  • Write to storage; treat memory as a copy you may lose.
  • Shortcut: "if the object vanished now, would losing this hurt?" Yes → storage.
  • Trap: cached fields that are never reloaded from storage.

Practice — level up

0/1 solved

Write your answer, then compare it with a rubric and a model answer.

ExplainWhat survives eviction?

Hoplink's LinkCounter lost its count overnight. Explain the cause and redesign it: what goes in storage, what may stay in memory, and how an hourly roll-up runs with no traffic.