Skip to content

Commit

Permalink
refactor: Rename ReplicacheOptions.userID and Replicache.userID back …
Browse files Browse the repository at this point in the history
…to name. (#835)

This reverts commit d95d8bf.

We realized we will need both a userID, and another identifier to support multiple Replicache instance for the same user (e.g. roomID).  We will do this api change in v10 rather than v9.  

Added details to documentation for `name` around Replicache bootsrapping and mutation recovery.
  • Loading branch information
grgbkr authored Feb 10, 2022
1 parent addf5ff commit b40de26
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 123 deletions.
12 changes: 10 additions & 2 deletions doc/docs/launch-checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,16 @@ Before you launch with Replicache in your product, it's a good idea to double-ch
cookie values across clients, resulting in new clients being able to startup
from previous clients' state with minimal download at startup.
- The `name` property of `ReplicacheOptions` is required to differentiate
Replicache instances for different users; otherwise Replicache may try to fork
state from a different user at startup.
Replicache instances for different users. This is important for the following
reasons:
- For efficiency and performance, a new [[Replicache]] instance will
initialize its state from the persisted state of an existing [[Replicache]]
instance with the same `name`, domain and browser profile.
- Mutations from one [[Replicache]] instance may be pushed using the
[[ReplicacheOptions.auth]], [[ReplicacheOptions.pushURL]],
[[ReplicacheOptions.pullURL]], [[ReplicacheOptions.pusher]], and
[[ReplicacheOptions.puller]] of another Replicache instance with the same
`name`, domain and browser profile.

## All endpoints

Expand Down
26 changes: 13 additions & 13 deletions perf/replicache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class ReplicacheWithPersist<MD extends MutatorDefs> extends Replicache {
}

async function setupPersistedData(
userID: string,
replicacheName: string,
numKeys: number,
): Promise<void> {
const randomValues = jsonArrayTestData(numKeys, valSize);
Expand All @@ -92,7 +92,7 @@ async function setupPersistedData(
// so that a snapshot commit is created, which new clients
// can use to bootstrap.
const rep = (repToClose = new ReplicacheWithPersist({
userID,
name: replicacheName,
pullInterval: null,
puller: async (_: Request) => {
return {
Expand Down Expand Up @@ -124,14 +124,14 @@ export function benchmarkStartupUsingBasicReadsFromPersistedData(opts: {
numKeysPersisted: number;
numKeysToRead: number;
}): Benchmark {
const userID = makeUserID();
const repName = makeRepName();
let repToClose: Replicache | undefined;
return {
name: `startup read ${valSize}x${opts.numKeysToRead} from ${valSize}x${opts.numKeysPersisted} stored`,
group: 'replicache',
byteSize: opts.numKeysToRead * valSize,
async setup() {
await setupPersistedData(userID, opts.numKeysPersisted);
await setupPersistedData(repName, opts.numKeysPersisted);
},
async setupEach() {
setupIDBDatabasesStoreForTest();
Expand All @@ -150,7 +150,7 @@ export function benchmarkStartupUsingBasicReadsFromPersistedData(opts: {
).map(i => `key${i}`);
bencher.reset();
const rep = (repToClose = new Replicache({
userID,
name: repName,
pullInterval: null,
}));
let getCount = 0;
Expand All @@ -172,14 +172,14 @@ export function benchmarkStartupUsingScanFromPersistedData(opts: {
numKeysPersisted: number;
numKeysToRead: number;
}): Benchmark {
const userID = makeUserID();
const repName = makeRepName();
let repToClose: Replicache | undefined;
return {
name: `startup scan ${valSize}x${opts.numKeysToRead} from ${valSize}x${opts.numKeysPersisted} stored`,
group: 'replicache',
byteSize: opts.numKeysToRead * valSize,
async setup() {
await setupPersistedData(userID, opts.numKeysPersisted);
await setupPersistedData(repName, opts.numKeysPersisted);
},
async setupEach() {
setupIDBDatabasesStoreForTest();
Expand All @@ -203,7 +203,7 @@ export function benchmarkStartupUsingScanFromPersistedData(opts: {
const randomStartKey = sortedKeys[randomIndex];
bencher.reset();
const rep = (repToClose = new Replicache({
userID,
name: repName,
pullInterval: null,
}));
await rep.query(async (tx: ReadTransaction) => {
Expand Down Expand Up @@ -441,16 +441,16 @@ export function benchmarkWriteSubRead(opts: {
};
}

function makeUserID(): string {
return `user-${uuid()}`;
function makeRepName(): string {
return `bench${uuid()}`;
}

function makeRep<MD extends MutatorDefs>(
options: Omit<ReplicacheOptions<MD>, 'userID'> = {},
options: Omit<ReplicacheOptions<MD>, 'name'> = {},
) {
const userID = makeUserID();
const name = makeRepName();
return new Replicache<MD>({
userID,
name,
pullInterval: null,
...options,
});
Expand Down
10 changes: 5 additions & 5 deletions src/persist/idb-databases-store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ test('putDatabase with no existing record in db', async () => {
const store = new IDBDatabasesStore(_ => new TestMemStore());
const testDB = {
name: 'testName',
userID: 'testUserID',
replicacheName: 'testReplicacheName',
replicacheFormatVersion: 1,
schemaVersion: 'testSchemaVersion',
};
Expand All @@ -27,7 +27,7 @@ test('putDatabase sequence', async () => {
const store = new IDBDatabasesStore(_ => new TestMemStore());
const testDB1 = {
name: 'testName1',
userID: 'testUserID1',
replicacheName: 'testReplicacheName1',
replicacheFormatVersion: 1,
schemaVersion: 'testSchemaVersion1',
};
Expand All @@ -41,7 +41,7 @@ test('putDatabase sequence', async () => {

const testDB2 = {
name: 'testName2',
userID: 'testUserID2',
replicacheName: 'testReplicacheName2',
replicacheFormatVersion: 2,
schemaVersion: 'testSchemaVersion2',
};
Expand All @@ -68,7 +68,7 @@ test('clear', async () => {
const store = new IDBDatabasesStore(_ => new TestMemStore());
const testDB1 = {
name: 'testName1',
userID: 'testUserID1',
replicacheName: 'testReplicacheName1',
replicacheFormatVersion: 1,
schemaVersion: 'testSchemaVersion1',
};
Expand All @@ -86,7 +86,7 @@ test('clear', async () => {

const testDB2 = {
name: 'testName2',
userID: 'testUserID2',
replicacheName: 'testReplicacheName2',
replicacheFormatVersion: 2,
schemaVersion: 'testSchemaVersion2',
};
Expand Down
4 changes: 2 additions & 2 deletions src/persist/idb-databases-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export type IndexedDBName = string;

export type IndexedDBDatabase = {
name: IndexedDBName;
userID: string;
replicacheName: string;
replicacheFormatVersion: number;
schemaVersion: string;
};
Expand All @@ -50,7 +50,7 @@ function assertIndexedDBDatabase(
): asserts value is IndexedDBDatabase {
assertObject(value);
assertString(value.name);
assertString(value.userID);
assertString(value.replicacheName);
assertNumber(value.replicacheFormatVersion);
assertString(value.schemaVersion);
}
Expand Down
30 changes: 16 additions & 14 deletions src/replicache-mutation-recovery.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
tickAFewTimes,
dbsToDrop,
clock,
createUserIDForTest,
createReplicacheNameForTest,
} from './test-util';
import {makeIdbName, REPLICACHE_FORMAT_VERSION} from './replicache';
import {addGenesis, addLocal, addSnapshot, Chain} from './db/test-helpers';
Expand Down Expand Up @@ -38,19 +38,19 @@ teardown(async () => {
});

async function createPerdag(args: {
userID: string;
replicacheName: string;
schemaVersion: string;
}): Promise<dag.Store> {
const {userID, schemaVersion} = args;
const idbName = makeIdbName(userID, schemaVersion);
const {replicacheName, schemaVersion} = args;
const idbName = makeIdbName(replicacheName, schemaVersion);
dbsToDrop.add(idbName);
const idb = new kv.IDBStore(idbName);

const idbDatabases = new persist.IDBDatabasesStore();
try {
await idbDatabases.putDatabase({
name: idbName,
userID,
replicacheName,
schemaVersion,
replicacheFormatVersion: REPLICACHE_FORMAT_VERSION,
});
Expand Down Expand Up @@ -137,7 +137,7 @@ async function testRecoveringMutationsOfClient(args: {
await tickAFewTimes();

const testPerdag = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion: schemaVersionOfClientWPendingMutations,
});

Expand Down Expand Up @@ -250,7 +250,9 @@ test('client does not attempt to recover mutations from IndexedDB with different
await tickAFewTimes();

const testPerdag = await createPerdag({
userID: createUserIDForTest(replicachePartialNameOfClientWPendingMutations),
replicacheName: createReplicacheNameForTest(
replicachePartialNameOfClientWPendingMutations,
),
schemaVersion,
});

Expand Down Expand Up @@ -305,7 +307,7 @@ test('successfully recovering mutations of multiple clients with mix of schema v
await tickAFewTimes();

const testPerdagForClients1Thru3 = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion: schemaVersionOfClients1Thru3AndClientRecoveringMutations,
});

Expand All @@ -327,7 +329,7 @@ test('successfully recovering mutations of multiple clients with mix of schema v
);

const testPerdagForClient4 = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion: schemaVersionOfClient4,
});
const client4PendingLocalMetas = await createAndPersistClientWithPendingLocal(
Expand Down Expand Up @@ -493,7 +495,7 @@ test('if a push error occurs, continues to try to recover other clients', async
await tickAFewTimes();

const testPerdag = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion,
});

Expand Down Expand Up @@ -645,7 +647,7 @@ test('if an error occurs recovering one client, continues to try to recover othe
await tickAFewTimes();

const testPerdag = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion,
});

Expand Down Expand Up @@ -785,7 +787,7 @@ test('if an error occurs recovering one db, continues to try to recover clients
await tickAFewTimes();

const testPerdagForClient1 = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion: schemaVersionOfClient1,
});
await createAndPersistClientWithPendingLocal(
Expand All @@ -795,7 +797,7 @@ test('if an error occurs recovering one db, continues to try to recover clients
);

const testPerdagForClient2 = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion: schemaVersionOfClient2,
});
const client2PendingLocalMetas = await createAndPersistClientWithPendingLocal(
Expand Down Expand Up @@ -907,7 +909,7 @@ test('mutation recovery exits early if Replicache is closed', async () => {
await tickAFewTimes();

const testPerdag = await createPerdag({
userID: rep.userID,
replicacheName: rep.name,
schemaVersion,
});

Expand Down
25 changes: 15 additions & 10 deletions src/replicache-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,25 @@ export interface ReplicacheOptions<MD extends MutatorDefs> {
pullURL?: string;

/**
* A unique identifier for the user authenticated by
* [[ReplicacheOptions.auth]]. Must be non-empty.
* The name of the Replicache database.
*
* This is used to keep different user's state separate.
* It is important to use user specific names so that if there are multiple
* tabs open for different distinct users their data is kept separate.
*
* For efficiency, a new Replicache instance will initialize its state from
* the persisted state of an existing Replicache instance with the same
* `userID`, domain and browser profile.
* For efficiency and performance, a new [[Replicache]] instance will
* initialize its state from the persisted state of an existing [[Replicache]]
* instance with the same `name`, domain and browser profile.
*
* Mutations from one Replicache instance may be pushed using the
* [[ReplicacheOptions.auth]] of another Replicache instance with the same
* `userID`, domain and browser profile.
* Mutations from one [[Replicache]] instance may be pushed using the
* [[ReplicacheOptions.auth]], [[ReplicacheOptions.pushURL]],
* [[ReplicacheOptions.pullURL]], [[ReplicacheOptions.pusher]], and
* [[ReplicacheOptions.puller]] of another Replicache instance with the same
* `name`, domain and browser profile.
*
* You can use multiple Replicache instances for the same user as long as the
* names are unique. e.g. `name: `$userID:$roomID`
*/
userID: string;
name: string;

/**
* The schema version of the data understood by this application. This enables
Expand Down
Loading

0 comments on commit b40de26

Please sign in to comment.