Skip to main content

ðŸĪŠ The Reset Strategy

The Reset Strategy is the easiest possible strategy: it sends the entire client view on every pull response, so no patch calculation is necessary at all.

Sending the entire client view on each pull this way is very inefficient, so this approach is not usually recommended. That said, we do have customers that use this strategy in production, and it works if your data changes infrequently or is very small.

Schema​

In addition to your own normal domain data, your backend database will need to store two additional entities to support Replicache:

// A group of related ReplicacheClients. Typically there is one per browser
// profile.
type ReplicacheClientGroup = {
// Globally unique ID, generated by Replicache.
id: string;

// Optional, but required if the application is authenticated. The userID
// that created this ReplicacheClientGroup.
userID: any;
};

// An instance of the Replicache JS class that has ever synced with the server.
type ReplicacheClient = {
// Globally unique ID, generated by Replicache.
id: string;

// The ClientGroup this client is part of.
clientGroupID: string;

// Last mutation the server has processed from this client.
lastMutationID: number;
};

Push​

Replicache sends a PushRequest to the push endpoint. For each mutation described in the request body, the push endpoint should:

  1. let errorMode = false
  2. Begin transaction
  3. Read the ReplicacheClientGroup for body.clientGroupID from the database, or default to:
{
id: body.clientGroupID,
userID
}
  1. Verify the requesting user owns the specified client group.
  2. Read the ReplicacheClient for mutation.clientID or default to:
{
id: mutation.clientID,
clientGroupID: body.clientGroupID,
lastMutationID: 0,
}
  1. Verify the requesting client group owns the requested client.
  2. let nextMutationID = client.lastMutationID + 1
  3. Rollback transaction and skip this mutation if already processed (mutation.id < nextMutationID)
  4. Rollback transaction and error if mutation from the future (mutation.id > nextMutationID)
  5. If errorMode != true then:
    1. Try to run business logic for mutation
    2. If error:
      1. Log error
      2. set errorMode = true
      3. Abort transaction
      4. Repeat these steps at the beginning
  6. Write ReplicacheClientGroup:
{
id: body.clientGroupID,
userID,
}
  1. Write ReplicacheClient:
{
id: mutation.clientID,
clientGroupID: body.clientGroupID,
lastMutationID: nextMutationID,
}
  1. Commit transaction

After the loop is complete, poke clients to cause them to pull.

Pull​

Replicache sends a PullRequest to the pull endpoint. The endpoint should:

  1. Begin transaction
  2. Read the ReplicacheClientGroup for body.clientGroupID from the database, or default to:
{
id: body.clientGroupID,
userID
}
  1. Verify the requesting client group owns the requested client.
  2. Read all rows from the database that should be in the client view.
  3. Read all ReplicacheClient records for the requested client group.
  4. Create a PullResponse with:
    1. cookie set to the server's current timestamp as an integer.
    2. lastMutationIDChanges set to the lastMutationID for every client in the client group.
    3. patch set to op:clear followed by op:put for every row in the view.

Example​

We do not currently have an example of this strategy.

Variations​

Read Authorization​

Because of the fact that this returns a reset patch, read authorizaton works naturally. Just update the query used to build the patch in the pull response to obey whatever auth rules you like.

Early Exit​

There is no need to process every mutation submitted to the push endpoint. You can exit early as long as lastMutationID is set to whatever the last processed mutation was. This can ocassionally be useful if clients can accumulate large amounts of mutations while offline and you want to keep the runtime of the push handler under some limit.

Batching​

You don't need to process each mutation in its own transaction. The entire push can be run inside one transaction, or you can do smaller batches.