Skip to content

Commit 3ef2847

Browse files
authored
Merge dfb45ad into e35db6f
2 parents e35db6f + dfb45ad commit 3ef2847

File tree

8 files changed

+144
-97
lines changed

8 files changed

+144
-97
lines changed

packages/firestore/src/core/event_manager.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,8 @@ export class QueryListener {
307307
snap.mutatedKeys,
308308
snap.fromCache,
309309
snap.syncStateChanged,
310-
/* excludesMetadataChanges= */ true
310+
/* excludesMetadataChanges= */ true,
311+
snap.resumeToken
311312
);
312313
}
313314
let raisedEvent = false;
@@ -371,8 +372,9 @@ export class QueryListener {
371372
return false;
372373
}
373374

374-
// Raise data from cache if we have any documents or we are offline
375-
return !snap.docs.isEmpty() || onlineState === OnlineState.Offline;
375+
// Raise data from cache if we have previously executed the query and have
376+
// cached data or we are offline
377+
return snap.resumeToken.approximateByteSize() > 0 || onlineState === OnlineState.Offline;
376378
}
377379

378380
private shouldRaiseEvent(snap: ViewSnapshot): boolean {
@@ -405,7 +407,8 @@ export class QueryListener {
405407
snap.query,
406408
snap.docs,
407409
snap.mutatedKeys,
408-
snap.fromCache
410+
snap.fromCache,
411+
snap.resumeToken
409412
);
410413
this.raisedInitialEvent = true;
411414
this.queryObserver.next(snap);

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ import {
115115
ViewChange
116116
} from './view';
117117
import { ViewSnapshot } from './view_snapshot';
118+
import {ByteString} from '../util/byte_string';
118119

119120
const LOG_TAG = 'SyncEngine';
120121

@@ -327,7 +328,8 @@ export async function syncEngineListen(
327328
syncEngineImpl,
328329
query,
329330
targetId,
330-
status === 'current'
331+
status === 'current',
332+
targetData.resumeToken
331333
);
332334
}
333335

@@ -342,7 +344,8 @@ async function initializeViewAndComputeSnapshot(
342344
syncEngineImpl: SyncEngineImpl,
343345
query: Query,
344346
targetId: TargetId,
345-
current: boolean
347+
current: boolean,
348+
resumeToken: ByteString
346349
): Promise<ViewSnapshot> {
347350
// PORTING NOTE: On Web only, we inject the code that registers new Limbo
348351
// targets based on view changes. This allows us to only depend on Limbo
@@ -360,12 +363,13 @@ async function initializeViewAndComputeSnapshot(
360363
const synthesizedTargetChange =
361364
TargetChange.createSynthesizedTargetChangeForCurrentChange(
362365
targetId,
363-
current && syncEngineImpl.onlineState !== OnlineState.Offline
366+
current && syncEngineImpl.onlineState !== OnlineState.Offline,
367+
resumeToken
364368
);
365369
const viewChange = view.applyChanges(
366370
viewDocChanges,
367371
/* updateLimboDocuments= */ syncEngineImpl.isPrimaryClient,
368-
synthesizedTargetChange
372+
synthesizedTargetChange,
369373
);
370374
updateTrackedLimbos(syncEngineImpl, targetId, viewChange.limboChanges);
371375

@@ -1025,7 +1029,7 @@ export async function syncEngineEmitNewSnapsAndNotifyLocalStore(
10251029
if (syncEngineImpl.isPrimaryClient) {
10261030
syncEngineImpl.sharedClientState.updateQueryState(
10271031
queryView.targetId,
1028-
viewSnapshot.fromCache ? 'not-current' : 'current'
1032+
viewSnapshot.fromCache ? 'not-current' : 'current',
10291033
);
10301034
}
10311035
newSnaps.push(viewSnapshot);
@@ -1379,7 +1383,8 @@ async function synchronizeQueryViewsAndRaiseSnapshots(
13791383
syncEngineImpl,
13801384
synthesizeTargetToQuery(target!),
13811385
targetId,
1382-
/*current=*/ false
1386+
/*current=*/ false,
1387+
targetData.resumeToken
13831388
);
13841389
}
13851390

@@ -1451,7 +1456,8 @@ export async function syncEngineApplyTargetState(
14511456
const synthesizedRemoteEvent =
14521457
RemoteEvent.createSynthesizedRemoteEventForCurrentChange(
14531458
targetId,
1454-
state === 'current'
1459+
state === 'current',
1460+
ByteString.EMPTY_BYTE_STRING
14551461
);
14561462
await syncEngineEmitNewSnapsAndNotifyLocalStore(
14571463
syncEngineImpl,
@@ -1506,7 +1512,8 @@ export async function syncEngineApplyActiveTargetsChange(
15061512
syncEngineImpl,
15071513
synthesizeTargetToQuery(target),
15081514
targetData.targetId,
1509-
/*current=*/ false
1515+
/*current=*/ false,
1516+
targetData.resumeToken
15101517
);
15111518
remoteStoreListen(syncEngineImpl.remoteStore, targetData);
15121519
}

packages/firestore/src/core/view.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import {
3535
SyncState,
3636
ViewSnapshot
3737
} from './view_snapshot';
38+
import {ByteString} from '../util/byte_string';
3839

3940
export type LimboDocumentChange = AddedLimboDocument | RemovedLimboDocument;
4041
export class AddedLimboDocument {
@@ -72,6 +73,7 @@ export interface ViewChange {
7273
*/
7374
export class View {
7475
private syncState: SyncState | null = null;
76+
private resumeToken: ByteString | null = null;
7577
/**
7678
* A flag whether the view is current with the backend. A view is considered
7779
* current after it has seen the current flag from the backend and did not
@@ -306,11 +308,13 @@ export class View {
306308
const newSyncState = synced ? SyncState.Synced : SyncState.Local;
307309
const syncStateChanged = newSyncState !== this.syncState;
308310
this.syncState = newSyncState;
311+
this.resumeToken = targetChange?.resumeToken ?? null;
309312

310313
if (changes.length === 0 && !syncStateChanged) {
311314
// no changes
312315
return { limboChanges };
313316
} else {
317+
314318
const snap: ViewSnapshot = new ViewSnapshot(
315319
this.query,
316320
docChanges.documentSet,
@@ -319,7 +323,8 @@ export class View {
319323
docChanges.mutatedKeys,
320324
newSyncState === SyncState.Local,
321325
syncStateChanged,
322-
/* excludesMetadataChanges= */ false
326+
/* excludesMetadataChanges= */ false,
327+
targetChange?.resumeToken ?? ByteString.EMPTY_BYTE_STRING
323328
);
324329
return {
325330
snapshot: snap,
@@ -468,7 +473,8 @@ export class View {
468473
this.query,
469474
this.documentSet,
470475
this.mutatedKeys,
471-
this.syncState === SyncState.Local
476+
this.syncState === SyncState.Local,
477+
this.resumeToken ?? ByteString.EMPTY_BYTE_STRING
472478
);
473479
}
474480
}

packages/firestore/src/core/view_snapshot.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { fail } from '../util/assert';
2323
import { SortedMap } from '../util/sorted_map';
2424

2525
import { Query, queryEquals } from './query';
26+
import {ByteString} from '../util/byte_string';
2627

2728
export const enum ChangeType {
2829
Added,
@@ -146,15 +147,19 @@ export class ViewSnapshot {
146147
readonly mutatedKeys: DocumentKeySet,
147148
readonly fromCache: boolean,
148149
readonly syncStateChanged: boolean,
149-
readonly excludesMetadataChanges: boolean
150-
) {}
150+
readonly excludesMetadataChanges: boolean,
151+
readonly resumeToken: ByteString
152+
) {
153+
1 == 1;
154+
}
151155

152156
/** Returns a view snapshot as if all documents in the snapshot were added. */
153157
static fromInitialDocuments(
154158
query: Query,
155159
documents: DocumentSet,
156160
mutatedKeys: DocumentKeySet,
157-
fromCache: boolean
161+
fromCache: boolean,
162+
resumeToken: ByteString
158163
): ViewSnapshot {
159164
const changes: DocumentViewChange[] = [];
160165
documents.forEach(doc => {
@@ -169,7 +174,8 @@ export class ViewSnapshot {
169174
mutatedKeys,
170175
fromCache,
171176
/* syncStateChanged= */ true,
172-
/* excludesMetadataChanges= */ false
177+
/* excludesMetadataChanges= */ false,
178+
resumeToken
173179
);
174180
}
175181

@@ -184,7 +190,8 @@ export class ViewSnapshot {
184190
!this.mutatedKeys.isEqual(other.mutatedKeys) ||
185191
!queryEquals(this.query, other.query) ||
186192
!this.docs.isEqual(other.docs) ||
187-
!this.oldDocs.isEqual(other.oldDocs)
193+
!this.oldDocs.isEqual(other.oldDocs) ||
194+
this.resumeToken !== other.resumeToken
188195
) {
189196
return false;
190197
}

packages/firestore/src/remote/remote_event.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,16 @@ export class RemoteEvent {
6767
// PORTING NOTE: Multi-tab only
6868
static createSynthesizedRemoteEventForCurrentChange(
6969
targetId: TargetId,
70-
current: boolean
70+
current: boolean,
71+
resumeToken: ByteString
7172
): RemoteEvent {
7273
const targetChanges = new Map<TargetId, TargetChange>();
7374
targetChanges.set(
7475
targetId,
7576
TargetChange.createSynthesizedTargetChangeForCurrentChange(
7677
targetId,
77-
current
78+
current,
79+
resumeToken
7880
)
7981
);
8082
return new RemoteEvent(
@@ -134,10 +136,11 @@ export class TargetChange {
134136
*/
135137
static createSynthesizedTargetChangeForCurrentChange(
136138
targetId: TargetId,
137-
current: boolean
139+
current: boolean,
140+
resumeToken: ByteString
138141
): TargetChange {
139142
return new TargetChange(
140-
ByteString.EMPTY_BYTE_STRING,
143+
resumeToken,
141144
current,
142145
documentKeySet(),
143146
documentKeySet(),

packages/firestore/test/integration/api/query.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,6 +1288,25 @@ apiDescribe('Queries', (persistence: boolean) => {
12881288
expect(toDataArray(snapshot)).to.deep.equal([{ map: { nested: 'foo' } }]);
12891289
});
12901290
});
1291+
1292+
// eslint-disable-next-line no-restricted-properties
1293+
(persistence ? it : it.skip)('empty query results are cached', () => {
1294+
// Reproduces https://github.com/firebase/firebase-js-sdk/issues/5873
1295+
return withTestCollection(persistence, {}, async coll => {
1296+
const snapshot1 = await coll.get(); // Populate the cache
1297+
expect(snapshot1.metadata.fromCache).to.be.false;
1298+
expect(toDataArray(snapshot1)).to.deep.equal([]); // Precondition check
1299+
1300+
// Add a snapshot listener whose first event should be raised from cache.
1301+
const storeEvent = new EventsAccumulator<firestore.QuerySnapshot>();
1302+
coll.onSnapshot(storeEvent.storeEvent);
1303+
const snapshot2 = await storeEvent.awaitEvent();
1304+
1305+
expect(snapshot2.metadata.fromCache).to.be.true;
1306+
expect(toDataArray(snapshot2)).to.deep.equal([]);
1307+
});
1308+
});
1309+
12911310
});
12921311

12931312
function verifyDocumentChange<T>(

0 commit comments

Comments
 (0)