Local-first data

How big can a local-first document get?

A local-first document can grow until its edit history, its sync cost or the device's storage becomes the constraint, and each library sets its own ceiling if it states one. Because merging data structures keep information about past edits and deletions, the stored size can exceed the content people see.

Learning objectives

After reading this article you will be able to:

  • Identify the parts of a CRDT document's stored size beyond its visible content
  • Explain why history and tombstones can grow when visible content does not
  • List habits that keep a local-first document well below its size limit

What a document’s size is made of

A plain JSON file is about as large as its content. A document built on a CRDT carries more, because it has to merge with copies that changed elsewhere. Its stored size has several parts:

  • The current content that people see.
  • History. Automerge describes a document as a combination of a JSON object and a git repository: every change adds to its history, and that history is what lets any two copies merge.
  • Deletion markers. Removed items can leave a trace so a late-arriving change cannot bring them back. Shapiro and colleagues call the set of removed elements in their two-phase set the tombstone set.
  • Metadata that identifies who made each change and in what order, such as the client ID and clock that Yjs uses.

How much of each a library keeps is a design choice. Automerge says its compact binary format makes it feasible to store all the editing history of a document, for example every keystroke in a large text document. Yjs, by contrast, discards the content of deleted objects when garbage collection is on, keeping only a lightweight marker that records the length of what was removed.

History grows when content does not

A document can grow while its visible content stays the same size. Typing a sentence and deleting it leaves the text unchanged but adds to the history.

The Ink & Switch essay reports this from real use. On one of their prototypes, performance and memory and disk usage quickly became a problem because the CRDTs stored all history, including character-by-character text edits. That history could not easily be truncated, because there is no way to know when someone might reconnect after months away and need to merge from that point.

Shapiro’s report makes the same point in general terms: CRDTs tend to become inefficient over time as tombstones accumulate, and safely removing tombstones needs agreement among all replicas, which must be known and reachable. Devices that are rarely online are exactly what makes that hard.

Sync cost

Size matters most when a document moves. A device that has been away needs whatever changes it missed, and a device joining for the first time needs the whole document. On a fast connection that may go unnoticed; on a slow or intermittent link, such as a short-range radio between phones, a large document can take a long time to arrive or fail to finish before the devices move apart.

Larger documents also cost memory and processing time on every device that loads and merges them, which is the problem the Ink & Switch team ran into.

Device storage and backups

The document has to live somewhere on the phone, in a database or a file in the app’s private storage. Two nearby limits apply:

  • Databases. If the app stores a whole document as a single SQLite value, SQLite’s default maximum length for a string or BLOB is 1,000,000,000 bytes. SQLite’s own documentation suggests that security-sensitive applications lower that limit rather than raise it.
  • Backups. Android’s Auto Backup allows up to 25 MB per app. If the app’s data goes over that, Android calls onQuotaExceeded() and does not back up the data to the cloud until it falls back under the threshold.

Limits that libraries state

Some sync libraries publish a ceiling; others leave it to you. Where one is stated, design to it rather than to the device’s free space. Offline Protocol’s limits page, for example, lists a document limit of “1 MiB; warning at 768 KiB” and a document sync frame of 32 KiB for Mesh SDK v0.27.0, and at most 1,024 peer-named documents per space.

Keeping documents small

Whatever the ceiling, a document that stays well below it syncs faster and leaves room to grow. A few habits help:

  1. Split by unit of work. One document per job, inspection or conversation, not one document for everything.
  2. Start fresh documents for new periods. A new document for each week or project keeps old history out of daily sync.
  3. Keep binaries out. Store photos and files separately, moved as files, and keep only a reference in the document.
  4. Use the library’s compaction or garbage collection where it offers one, and check what it actually discards.
  5. Watch the warning before the limit. Surface size warnings to the app so it can split or archive a document before a write fails.

Frequently asked questions

Does deleting content make a CRDT document smaller?

Not necessarily. Deletions can be recorded so that every copy applies them, and some libraries keep the deleted content in history. Whether the bytes shrink depends on the library's garbage collection or compaction.

Should photos and files go inside a document?

It is better not to. Keep large binary content as separate files and store a reference to each file in the document, so the document stays small and quick to sync.

Sources

Build it with Offline Protocol

Offline Protocol's limits page lists the Mesh SDK's defaults and ceilings, including document size, document sync frames and documents per space, and says how capacity exhaustion should reach your application.

Read the limits page