Skip to content

Commit 982d65d

Browse files
Adding SnapshotVersion to SharedClientState
1 parent 4625e84 commit 982d65d

File tree

6 files changed

+194
-28
lines changed

6 files changed

+194
-28
lines changed

packages/firestore/src/core/sync_engine.ts

+39-8
Original file line numberDiff line numberDiff line change
@@ -349,7 +349,10 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
349349
.then(result => {
350350
this.sharedClientState.addPendingMutation(result.batchId);
351351
this.addMutationCallback(result.batchId, userCallback);
352-
return this.emitNewSnapsAndNotifyLocalStore(result.changes);
352+
return this.emitNewSnapsAndNotifyLocalStore(
353+
result.changes,
354+
SnapshotVersion.MIN
355+
);
353356
})
354357
.then(() => {
355358
return this.remoteStore.fillWritePipeline();
@@ -459,7 +462,11 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
459462
}
460463
}
461464
);
462-
return this.emitNewSnapsAndNotifyLocalStore(changes, remoteEvent);
465+
return this.emitNewSnapsAndNotifyLocalStore(
466+
changes,
467+
remoteEvent.snapshotVersion,
468+
remoteEvent
469+
);
463470
})
464471
.catch(err => this.ignoreIfPrimaryLeaseLoss(err));
465472
}
@@ -505,7 +512,12 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
505512
this.assertSubscribed('rejectListens()');
506513

507514
// PORTING NOTE: Multi-tab only.
508-
this.sharedClientState.updateQueryState(targetId, 'rejected', err);
515+
this.sharedClientState.updateQueryState(
516+
targetId,
517+
SnapshotVersion.MIN,
518+
'rejected',
519+
err
520+
);
509521

510522
const limboResolution = this.limboResolutionsByTarget[targetId];
511523
const limboKey = limboResolution && limboResolution.key;
@@ -552,6 +564,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
552564
// PORTING NOTE: Multi-tab only
553565
async applyBatchState(
554566
batchId: BatchId,
567+
snapshotVersion: SnapshotVersion,
555568
batchState: MutationBatchState,
556569
error?: FirestoreError
557570
): Promise<void> {
@@ -585,7 +598,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
585598
fail(`Unknown batchState: ${batchState}`);
586599
}
587600

588-
await this.emitNewSnapsAndNotifyLocalStore(documents);
601+
await this.emitNewSnapsAndNotifyLocalStore(documents, snapshotVersion);
589602
}
590603

591604
applySuccessfulWrite(
@@ -604,7 +617,10 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
604617
return this.localStore
605618
.acknowledgeBatch(mutationBatchResult)
606619
.then(changes => {
607-
return this.emitNewSnapsAndNotifyLocalStore(changes);
620+
return this.emitNewSnapsAndNotifyLocalStore(
621+
changes,
622+
mutationBatchResult.commitVersion
623+
);
608624
})
609625
.catch(err => this.ignoreIfPrimaryLeaseLoss(err));
610626
}
@@ -621,8 +637,16 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
621637
return this.localStore
622638
.rejectBatch(batchId)
623639
.then(changes => {
624-
this.sharedClientState.updateMutationState(batchId, 'rejected', error);
625-
return this.emitNewSnapsAndNotifyLocalStore(changes);
640+
this.sharedClientState.updateMutationState(
641+
batchId,
642+
SnapshotVersion.MIN,
643+
'rejected',
644+
error
645+
);
646+
return this.emitNewSnapsAndNotifyLocalStore(
647+
changes,
648+
SnapshotVersion.MIN
649+
);
626650
})
627651
.catch(err => this.ignoreIfPrimaryLeaseLoss(err));
628652
}
@@ -759,6 +783,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
759783

760784
private async emitNewSnapsAndNotifyLocalStore(
761785
changes: MaybeDocumentMap,
786+
snapshotVersion: SnapshotVersion,
762787
remoteEvent?: RemoteEvent
763788
): Promise<void> {
764789
const newSnaps: ViewSnapshot[] = [];
@@ -796,6 +821,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
796821
if (this.isPrimary) {
797822
this.sharedClientState.updateQueryState(
798823
queryView.targetId,
824+
snapshotVersion,
799825
viewChange.snapshot.fromCache ? 'not-current' : 'current'
800826
);
801827
}
@@ -860,7 +886,10 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
860886
result.removedBatchIds,
861887
result.addedBatchIds
862888
);
863-
await this.emitNewSnapsAndNotifyLocalStore(result.affectedDocuments);
889+
await this.emitNewSnapsAndNotifyLocalStore(
890+
result.affectedDocuments,
891+
SnapshotVersion.MIN
892+
);
864893
}
865894

866895
await this.remoteStore.handleCredentialChange();
@@ -973,6 +1002,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
9731002
// PORTING NOTE: Multi-tab only
9741003
async applyTargetState(
9751004
targetId: TargetId,
1005+
snapshotVersion: SnapshotVersion,
9761006
state: QueryTargetState,
9771007
error?: FirestoreError
9781008
): Promise<void> {
@@ -994,6 +1024,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
9941024
);
9951025
return this.emitNewSnapsAndNotifyLocalStore(
9961026
changes,
1027+
snapshotVersion,
9971028
synthesizedRemoteEvent
9981029
);
9991030
}

packages/firestore/src/local/local_store.ts

+1
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ export class LocalStore {
954954
// is committed. b/33446471 will remove this reliance.
955955
this.sharedClientState.updateMutationState(
956956
batchResult.batch.batchId,
957+
SnapshotVersion.MIN, // TODO: Replace with batch version from commit
957958
'acknowledged'
958959
);
959960
});

packages/firestore/src/local/shared_client_state.ts

+61-4
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import {
3434
import { AsyncQueue } from '../util/async_queue';
3535
import { Platform } from '../platform/platform';
3636
import { TargetIdSet, targetIdSet } from '../model/collections';
37+
import { SnapshotVersion } from '../core/snapshot_version';
38+
import { Timestamp } from '../api/timestamp';
3739

3840
const LOG_TAG = 'SharedClientState';
3941

@@ -91,6 +93,7 @@ export interface SharedClientState {
9193
*/
9294
updateMutationState(
9395
batchId: BatchId,
96+
snapshotVersion: SnapshotVersion,
9497
state: 'acknowledged' | 'rejected',
9598
error?: FirestoreError
9699
): void;
@@ -113,6 +116,7 @@ export interface SharedClientState {
113116
*/
114117
updateQueryState(
115118
targetId: TargetId,
119+
snapshotVersion: SnapshotVersion,
116120
state: QueryTargetState,
117121
error?: FirestoreError
118122
): void;
@@ -173,6 +177,7 @@ export interface SharedClientState {
173177
* encoded as part of the key.
174178
*/
175179
interface MutationMetadataSchema {
180+
snapshotVersion: { seconds: number; nanos: number };
176181
state: MutationBatchState;
177182
error?: { code: string; message: string }; // Only set when state === 'rejected'
178183
}
@@ -186,6 +191,7 @@ export class MutationMetadata {
186191
constructor(
187192
readonly user: User,
188193
readonly batchId: BatchId,
194+
readonly snapshotVersion: SnapshotVersion,
189195
readonly state: MutationBatchState,
190196
readonly error?: FirestoreError
191197
) {
@@ -210,6 +216,7 @@ export class MutationMetadata {
210216
typeof mutationBatch === 'object' &&
211217
['pending', 'acknowledged', 'rejected'].indexOf(mutationBatch.state) !==
212218
-1 &&
219+
isSnapshotVersion(mutationBatch.snapshotVersion) &&
213220
(mutationBatch.error === undefined ||
214221
typeof mutationBatch.error === 'object');
215222

@@ -231,6 +238,12 @@ export class MutationMetadata {
231238
return new MutationMetadata(
232239
user,
233240
batchId,
241+
SnapshotVersion.fromTimestamp(
242+
new Timestamp(
243+
mutationBatch.snapshotVersion.seconds,
244+
mutationBatch.snapshotVersion.nanos
245+
)
246+
),
234247
mutationBatch.state,
235248
firestoreError
236249
);
@@ -244,7 +257,12 @@ export class MutationMetadata {
244257
}
245258

246259
toLocalStorageJSON(): string {
260+
const timestamp = this.snapshotVersion.toTimestamp();
247261
const batchMetadata: MutationMetadataSchema = {
262+
snapshotVersion: {
263+
seconds: timestamp.seconds,
264+
nanos: timestamp.nanoseconds
265+
},
248266
state: this.state
249267
};
250268

@@ -264,6 +282,7 @@ export class MutationMetadata {
264282
* serialization. The TargetId is omitted as it is encoded as part of the key.
265283
*/
266284
interface QueryTargetStateSchema {
285+
snapshotVersion: { seconds: number; nanos: number };
267286
state: QueryTargetState;
268287
error?: { code: string; message: string }; // Only set when state === 'rejected'
269288
}
@@ -276,6 +295,7 @@ interface QueryTargetStateSchema {
276295
export class QueryTargetMetadata {
277296
constructor(
278297
readonly targetId: TargetId,
298+
readonly snapshotVersion: SnapshotVersion,
279299
readonly state: QueryTargetState,
280300
readonly error?: FirestoreError
281301
) {
@@ -297,6 +317,7 @@ export class QueryTargetMetadata {
297317

298318
let validData =
299319
typeof targetState === 'object' &&
320+
isSnapshotVersion(targetState.snapshotVersion) &&
300321
['not-current', 'current', 'rejected'].indexOf(targetState.state) !==
301322
-1 &&
302323
(targetState.error === undefined ||
@@ -319,6 +340,12 @@ export class QueryTargetMetadata {
319340
if (validData) {
320341
return new QueryTargetMetadata(
321342
targetId,
343+
SnapshotVersion.fromTimestamp(
344+
new Timestamp(
345+
targetState.snapshotVersion.seconds,
346+
targetState.snapshotVersion.nanos
347+
)
348+
),
322349
targetState.state,
323350
firestoreError
324351
);
@@ -332,7 +359,12 @@ export class QueryTargetMetadata {
332359
}
333360

334361
toLocalStorageJSON(): string {
362+
const timestamp = this.snapshotVersion.toTimestamp();
335363
const targetState: QueryTargetStateSchema = {
364+
snapshotVersion: {
365+
seconds: timestamp.seconds,
366+
nanos: timestamp.nanoseconds
367+
},
336368
state: this.state
337369
};
338370

@@ -654,15 +686,16 @@ export class WebStorageSharedClientState implements SharedClientState {
654686
}
655687

656688
addPendingMutation(batchId: BatchId): void {
657-
this.persistMutationState(batchId, 'pending');
689+
this.persistMutationState(batchId, SnapshotVersion.MIN, 'pending');
658690
}
659691

660692
updateMutationState(
661693
batchId: BatchId,
694+
snapshotVersion: SnapshotVersion,
662695
state: 'acknowledged' | 'rejected',
663696
error?: FirestoreError
664697
): void {
665-
this.persistMutationState(batchId, state, error);
698+
this.persistMutationState(batchId, snapshotVersion, state, error);
666699

667700
// Once a final mutation result is observed by other clients, they no longer
668701
// access the mutation's metadata entry. Since LocalStorage replays events
@@ -708,10 +741,11 @@ export class WebStorageSharedClientState implements SharedClientState {
708741

709742
updateQueryState(
710743
targetId: TargetId,
744+
snapshotVersion: SnapshotVersion,
711745
state: QueryTargetState,
712746
error?: FirestoreError
713747
): void {
714-
this.persistQueryTargetState(targetId, state, error);
748+
this.persistQueryTargetState(targetId, snapshotVersion, state, error);
715749
}
716750

717751
handleUserChange(
@@ -836,12 +870,14 @@ export class WebStorageSharedClientState implements SharedClientState {
836870

837871
private persistMutationState(
838872
batchId: BatchId,
873+
snapshotVersion: SnapshotVersion,
839874
state: MutationBatchState,
840875
error?: FirestoreError
841876
): void {
842877
const mutationState = new MutationMetadata(
843878
this.currentUser,
844879
batchId,
880+
snapshotVersion,
845881
state,
846882
error
847883
);
@@ -864,11 +900,17 @@ export class WebStorageSharedClientState implements SharedClientState {
864900

865901
private persistQueryTargetState(
866902
targetId: TargetId,
903+
snapshotVersion: SnapshotVersion,
867904
state: QueryTargetState,
868905
error?: FirestoreError
869906
): void {
870907
const targetKey = this.toLocalStorageQueryTargetMetadataKey(targetId);
871-
const targetMetadata = new QueryTargetMetadata(targetId, state, error);
908+
const targetMetadata = new QueryTargetMetadata(
909+
targetId,
910+
snapshotVersion,
911+
state,
912+
error
913+
);
872914
this.setItem(targetKey, targetMetadata.toLocalStorageJSON());
873915
}
874916

@@ -978,6 +1020,7 @@ export class WebStorageSharedClientState implements SharedClientState {
9781020

9791021
return this.syncEngine.applyBatchState(
9801022
mutationBatch.batchId,
1023+
mutationBatch.snapshotVersion,
9811024
mutationBatch.state,
9821025
mutationBatch.error
9831026
);
@@ -988,6 +1031,7 @@ export class WebStorageSharedClientState implements SharedClientState {
9881031
): Promise<void> {
9891032
return this.syncEngine.applyTargetState(
9901033
targetMetadata.targetId,
1034+
targetMetadata.snapshotVersion,
9911035
targetMetadata.state,
9921036
targetMetadata.error
9931037
);
@@ -1058,6 +1102,7 @@ export class MemorySharedClientState implements SharedClientState {
10581102

10591103
updateMutationState(
10601104
batchId: BatchId,
1105+
snapshotVersion: SnapshotVersion,
10611106
state: 'acknowledged' | 'rejected',
10621107
error?: FirestoreError
10631108
): void {
@@ -1071,6 +1116,7 @@ export class MemorySharedClientState implements SharedClientState {
10711116

10721117
updateQueryState(
10731118
targetId: TargetId,
1119+
snapshotVersion: SnapshotVersion,
10741120
state: QueryTargetState,
10751121
error?: FirestoreError
10761122
): void {
@@ -1112,3 +1158,14 @@ export class MemorySharedClientState implements SharedClientState {
11121158

11131159
shutdown(): void {}
11141160
}
1161+
1162+
function isSnapshotVersion(snapshotVersion: {
1163+
seconds: number;
1164+
nanos: number;
1165+
}): boolean {
1166+
return (
1167+
typeof snapshotVersion === 'object' &&
1168+
isSafeInteger(snapshotVersion.seconds) &&
1169+
isSafeInteger(snapshotVersion.nanos)
1170+
);
1171+
}

0 commit comments

Comments
 (0)