This is old and out-dated. Please see https://doc.replicache.dev/strategies/overview instead.

🌁 Overview

The Last-Modified Strategy is one of the easiest diff strategies to implement. It's also the strategy that comes to mind first for most developers when attempting to implement the pull endpoint.

⚠️ Warning

This strategy cannot be implemented correctly on most servers due to unreliable clocks. Thus, we do not recommend it for production systems. It's presented here because it's frequently useful for early development, kicking the tires with Replicache, and as a basis for comparison to other strategies.

👩🏻‍🏫 How it Works

Setup

  1. Add a LastModified field to each entity in your backend database if one doesn't already exist. You may already have one — many developers include it as a matter of course in all entities.
  2. Update the LastModified field each time an entity is changed. If possible, use a database trigger or similar to ensure the update to LastModfied is always performed.
  3. Use Soft Deletes for each entity in your backend database: add an IsDeleted field and set it to true when deleting an item rather than actually deleting it. Take this field into account when reading data.

On Pull

😓 Challenges

Correctness

This strategy requires a monotonically increasing clock for correctness, which is impossible to implement in most servers. Modern backend systems run on multiple physical application servers. Time can skew between these servers. Even on a single computer, time can jump forward or backward for a variety of reasons.

If this strategy observes time jump forward, nothing bad happens. However, if time jumps backward, then updates written to backend in that window can fail to sync to clients permanently.

The most straightforward solution to this problem is to enforce clock monotonicity. This is the The Global Version Strategy.

Soft Deletes

Soft Deletes are annoying to maintain. All queries to the database need to be aware of the IsDeleted column and filter appropriately. There are other ways to track deletes however, see below.

Selection Changes

Imagine you are syncing a set of documents, each of which is an entity in your DB with a LastModified column. The selection of documents a user syncs is controlled by what they have access to. If the set of documents a user has access to changes, pull needs to reflect that, but that won't be captured in the LastModified column because the document itself didn't change — only whether the user had access to it did.

To correctly handle this kind of change, you also need to track changes to the selection itself — either using the LastModified pattern again or some other pattern on the relevant entities. If the selection has changes since the last pull, then the results of the new and old selection must be compared. And any new or changed docs must be sent, and any removed docs must be deleted.

🌈 Variations

Deleted Table

There are alternative mechanisms to implement Soft Deletes. For example, you can maintain a separate Deleted collection/table in the database. This removes the special case for writing queries at the cost of extra schema complexity in the database.

Detecting Deletes using the Cookie

A different way to detect deletes is to store all known entity keys in the Replicache cookie. Then during pull, you can compare keys that were present last time with keys that are present now, and delete any that have been removed. This reduces server-side complexity at the cost of increasing request and response bandwidth during pull.

✍🏽 Examples

The Replidraw Sample uses The Last-Modified Strategy. See: https://github.com/rocicorp/replidraw/blob/master/backend/rds.ts#L104.