Skip to content

Commit

Permalink
Add tokens to drivers. (#3853)
Browse files Browse the repository at this point in the history
  • Loading branch information
shans committed Oct 22, 2019
1 parent 2ad61eb commit 8fec22e
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 7 deletions.
9 changes: 8 additions & 1 deletion src/runtime/storageNG/drivers/driver-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,16 @@ export abstract class Driver<Data> {
this.storageKey = storageKey;
this.exists = exists;
}
abstract registerReceiver(receiver: ReceiveMethod<Data>): void;
abstract registerReceiver(receiver: ReceiveMethod<Data>, token?: string): void;
abstract async send(model: Data, version: number): Promise<boolean>;

// Return a token that represents the current state of the data.
// This can be provided to registerReceiver, and will impact what
// data is delivered on initialization (only "new" data should be
// delivered, though note that this can be satisfied by sending
// a model for merging rather than by remembering a set of ops)
abstract getToken(): string | null;

// these methods only available to Backing Stores and will
// be removed once entity mutation is performed on CRDTs
// tslint:disable-next-line: no-any
Expand Down
17 changes: 14 additions & 3 deletions src/runtime/storageNG/drivers/firebase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,13 +127,20 @@ export class FirebaseDriver<Data> extends Driver<Data> {
this.reference = reference;
}

registerReceiver(receiver: ReceiveMethod<Data>) {
registerReceiver(receiver: ReceiveMethod<Data>, token?: string) {
this.receiver = receiver;
if (token) {
this.seenVersion = Number(token);
}
if (this.pendingModel !== null) {
assert(this.pendingModel);
receiver(this.pendingModel, this.pendingVersion);
if (this.pendingVersion > this.seenVersion) {
receiver(this.pendingModel, this.pendingVersion);
this.seenVersion = this.pendingVersion;
}
this.pendingModel = null;
this.seenVersion = this.pendingVersion;
} else {
assert(this.seenVersion === 0);
}
this.reference.on('value', dataSnapshot => this.remoteStateChanged(dataSnapshot));
}
Expand Down Expand Up @@ -181,6 +188,10 @@ export class FirebaseDriver<Data> extends Driver<Data> {
async read(key: StorageKey) {
throw new Error('Method not implemented.');
}

getToken() {
return this.seenVersion + '';
}
}


Expand Down
17 changes: 14 additions & 3 deletions src/runtime/storageNG/drivers/volatile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ export class VolatileStorageKey extends StorageKey {

export class VolatileMemory {
entries = new Map<string, VolatileEntry<unknown>>();
// Tokens can't just be an incrementing number as VolatileMemory is the basis for RamDiskMemory too;
// if we were to use numbers here then a RamDisk could be reaped, restarted, and end up with the
// same token as a previous iteration.
// When we want to support RamDisk fast-forwarding (e.g. by keeping a rotating window of recent
// operations) then we'll need tokens to be a combination of a per-instance random value and a
// per-operation updating number. For now, just a random value that is updated with each write
// is sufficient.
token = Math.random() + '';
}

export class VolatileDriver<Data> extends Driver<Data> {
Expand Down Expand Up @@ -81,6 +89,7 @@ export class VolatileDriver<Data> extends Driver<Data> {
} else {
this.data = {data: null, version: 0, drivers: []};
this.memory.entries.set(keyAsString, this.data as VolatileEntry<unknown>);
this.memory.token = Math.random() + '';
}
break;
}
Expand All @@ -90,14 +99,16 @@ export class VolatileDriver<Data> extends Driver<Data> {
this.data.drivers.push(this);
}

registerReceiver(receiver: ReceiveMethod<Data>) {
registerReceiver(receiver: ReceiveMethod<Data>, token?: string) {
this.receiver = receiver;
if (this.pendingModel) {
if (this.pendingModel && token !== this.memory.token) {
receiver(this.pendingModel, this.pendingVersion);
this.pendingModel = null;
}
this.pendingModel = null;
}

getToken() { return this.memory.token; }

async send(model: Data, version: number): Promise<boolean> {
// This needs to contain an "empty" await, otherwise there's
// a synchronous send / onReceive loop that can be established
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/storageNG/testing/test-storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ export class MockDriver<Data> extends Driver<Data> {
registerReceiver(receiver: ReceiveMethod<Data>) {
this.receiver = receiver;
}
getToken() {
return null;
}
async send(model: Data): Promise<boolean> {
return true;
}
Expand Down

0 comments on commit 8fec22e

Please sign in to comment.