🛸 Per-Space Version Strategy
The Per-Space Version Strategy is the same as the The Global Version Strategy except it has more than one space.
This increases throughput of the server. Instead of approximately 50 pushes per second across your entire server, you can get 50 pushes per space.
A common example of how people partition by space is along organizational boundaries in a SaaS application. Each customer org would be its own space and you'd thereby get 50 pushes per second per organization.
The tradeoffs to keep in mind is that you lose consistency guarantees across spaces. Replicache mutations are atomic: you can move data within a space, rename, copy, etc., and you have a guarantee that the entire change happens or none of it does. But this guarantee does not apply across spaces.
Imagine moving data from one space to another. Because there is no transactional guarantees across spaces, during the move, the user might see the data exist in both spaces, or neither.
While this might just seem like a minor UI annoyance, keep in mind that it means that if you have IDs that refer to data across spaces, there is no guarantee that the data actually exists at the moment you render. You'll have to defensively guard against invalid pointers into other spaces.
This is why partitioning makes most sense at very high-level boundaries, like organizations, so that it will be uncommon in your application to want to have data from two spaces interact.
Schema​
The schema generalizes the schema from the Global Version Strategy:
type ReplicacheSpace = {
id: string;
// Same as Global Version Strategy.
version: number;
};
type ReplicacheClientGroup = {
// Same as Global Version Strategy.
id: string;
userID: any;
spaceID: string;
};
type ReplicacheClient = {
// Same as Global Version Strategy.
id: string;
clientGroupID: string;
lastMutationID: number;
lastModifiedVersion: number;
};
// Each of your domain entities will have one additional fields.
type Todo = {
// ... fields needed for your application (id, title, complete, etc)
// Same as Global Version Strategy.
lastModifiedVersion: number;
deleted: boolean;
spaceID: string;
};
Push​
The push handler should receive the spaceID
being operated on as an HTTP parameter. The logic is otherwise almost identical to the Global Version Strategy, with minor changes to deal with spaces.
- Create a new
ReplicacheClientGroup
if necessary. - Verify that the requesting user owns the specified
ReplicacheClientGroup
. - Verify that the
ReplicacheClientGroup
is in the requested space.
Then, for each mutation described in the PushRequest
:
- Create the
ReplicacheClient
if necessary. - Validate that the
ReplicacheClient
is part of the requestedReplicacheClientGroup
. - Validate that the received mutation ID is the next expected mutation ID from this client.
- Increment the per-space version.
- Run the applicable business logic to apply the mutation.
- For each domain entity that is changed or deleted, update its
lastModifiedVersion
to the current per-space version. - For each domain entity that is deleted, set its
deleted
field to true.
- For each domain entity that is changed or deleted, update its
- Update the
lastMutationID
of the client to store that the mutation was processed. - Update the
lastModifiedVersion
of the client to the current per-space version.
Pull​
The pull handler should also receive the spaceID
being operated on as an HTTP parameter.
- Verify that requesting user owns the requested
ReplicacheClientGroup
. - Verify that the requested
ReplicacheClientGroup
is within the requested space. - Return a
PullResponse
with:- The current per-space version as the cookie.
- The
lastMutatationID
for each client that has changed since the requesting cookie. - A patch with:
put
ops for every entity created or changed since the request cookie.del
ops for every entity deleted since the request cookie.
Example​
Todo-WC is a simple example of per-space versioning. Repliear is a more involved example.
Challenges​
- Like the Global Version strategy, soft deletes can be annoying.
- Also like the Global Version strategy, it is difficult to implement features like read authentication and partial sync.
- It can be hard in some applications to find a way to partition spaces naturally.
- 50 pushes per second per space can still be insufficient for some applications.
Variations​
The same variations available to The Global Version Strategy apply here.