Skip to content

Commit 203e943

Browse files
committed
initial code
1 parent a4ee560 commit 203e943

File tree

12 files changed

+131
-46
lines changed

12 files changed

+131
-46
lines changed

packages/firestore/src/core/sync_engine_impl.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ import { primitiveComparator } from '../util/misc';
7171
import { ObjectMap } from '../util/obj_map';
7272
import { Deferred } from '../util/promise';
7373
import { SortedMap } from '../util/sorted_map';
74-
import { SortedSet } from '../util/sorted_set';
7574
import { BATCHID_UNKNOWN } from '../util/types';
7675

7776
import {
@@ -639,7 +638,9 @@ export async function syncEngineRejectListen(
639638
const event = new RemoteEvent(
640639
SnapshotVersion.min(),
641640
/* targetChanges= */ new Map<TargetId, TargetChange>(),
642-
/* targetMismatches= */ new SortedSet<TargetId>(primitiveComparator),
641+
/* targetMismatches= */ new SortedMap<TargetId, TargetPurpose>(
642+
primitiveComparator
643+
),
643644
documentUpdates,
644645
resolvedLimboDocuments
645646
);

packages/firestore/src/local/local_store_impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,7 @@ export function localStoreApplyRemoteEventToLocalCache(
601601
let newTargetData = oldTargetData.withSequenceNumber(
602602
txn.currentSequenceNumber
603603
);
604-
if (remoteEvent.targetMismatches.has(targetId)) {
604+
if (remoteEvent.targetMismatches.get(targetId) != null) {
605605
newTargetData = newTargetData
606606
.withResumeToken(
607607
ByteString.EMPTY_BYTE_STRING,

packages/firestore/src/local/target_data.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export const enum TargetPurpose {
3030
*/
3131
ExistenceFilterMismatch,
3232

33+
/**
34+
* The query target was used if the query is the result of a false positive in the bloom filter.
35+
*/
36+
ExistenceFilterMismatchBloom,
37+
3338
/** The query target was used to resolve a limbo document. */
3439
LimboResolution
3540
}

packages/firestore/src/remote/remote_event.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@
1717

1818
import { SnapshotVersion } from '../core/snapshot_version';
1919
import { TargetId } from '../core/types';
20+
import { TargetPurpose } from '../local/target_data';
2021
import {
2122
documentKeySet,
2223
DocumentKeySet,
2324
mutableDocumentMap,
24-
MutableDocumentMap,
25-
targetIdSet
25+
MutableDocumentMap
2626
} from '../model/collections';
2727
import { ByteString } from '../util/byte_string';
28-
import { SortedSet } from '../util/sorted_set';
28+
import { primitiveComparator } from '../util/misc';
29+
import { SortedMap } from '../util/sorted_map';
2930

3031
/**
3132
* An event from the RemoteStore. It is split into targetChanges (changes to the
@@ -46,7 +47,7 @@ export class RemoteEvent {
4647
* A set of targets that is known to be inconsistent. Listens for these
4748
* targets should be re-established without resume tokens.
4849
*/
49-
readonly targetMismatches: SortedSet<TargetId>,
50+
readonly targetMismatches: SortedMap<TargetId, TargetPurpose>,
5051
/**
5152
* A set of which documents have changed or been deleted, along with the
5253
* doc's new values (if not deleted).
@@ -82,7 +83,7 @@ export class RemoteEvent {
8283
return new RemoteEvent(
8384
SnapshotVersion.min(),
8485
targetChanges,
85-
targetIdSet(),
86+
new SortedMap<TargetId, TargetPurpose>(primitiveComparator),
8687
mutableDocumentMap(),
8788
documentKeySet()
8889
);

packages/firestore/src/remote/remote_store.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
localStoreGetNextMutationBatch
2525
} from '../local/local_store_impl';
2626
import { isIndexedDbTransactionError } from '../local/simple_db';
27-
import { TargetData, TargetPurpose } from '../local/target_data';
27+
import { TargetData } from '../local/target_data';
2828
import { MutationResult } from '../model/mutation';
2929
import { MutationBatch, MutationBatchResult } from '../model/mutation_batch';
3030
import { debugAssert, debugCast } from '../util/assert';
@@ -587,7 +587,7 @@ function raiseWatchSnapshot(
587587

588588
// Re-establish listens for the targets that have been invalidated by
589589
// existence filter mismatches.
590-
remoteEvent.targetMismatches.forEach(targetId => {
590+
remoteEvent.targetMismatches.forEach((targetId, targetPurpose) => {
591591
const targetData = remoteStoreImpl.listenTargets.get(targetId);
592592
if (!targetData) {
593593
// A watched target might have been removed already.
@@ -615,7 +615,7 @@ function raiseWatchSnapshot(
615615
const requestTargetData = new TargetData(
616616
targetData.target,
617617
targetId,
618-
TargetPurpose.ExistenceFilterMismatch,
618+
targetPurpose,
619619
targetData.sequenceNumber
620620
);
621621
sendWatchRequest(remoteStoreImpl, requestTargetData);

packages/firestore/src/remote/serializer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1018,7 +1018,7 @@ export function toListenRequestLabels(
10181018
}
10191019
}
10201020

1021-
function toLabel(
1021+
export function toLabel(
10221022
serializer: JsonProtoSerializer,
10231023
purpose: TargetPurpose
10241024
): string | null {
@@ -1027,6 +1027,8 @@ function toLabel(
10271027
return null;
10281028
case TargetPurpose.ExistenceFilterMismatch:
10291029
return 'existence-filter-mismatch';
1030+
case TargetPurpose.ExistenceFilterMismatchBloom:
1031+
return 'existence-filter-mismatch-bloom';
10301032
case TargetPurpose.LimboResolution:
10311033
return 'limbo-document';
10321034
default:

packages/firestore/src/remote/watch_change.ts

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,11 @@ export const enum WatchTargetChangeState {
8787
Reset
8888
}
8989

90+
const enum BloomFilterApplicationStatus {
91+
Success,
92+
Skipped,
93+
FalsePositive
94+
}
9095
export class WatchTargetChange {
9196
constructor(
9297
/** What kind of change occurred to the watch target. */
@@ -284,7 +289,9 @@ export class WatchChangeAggregator {
284289
* known to be inconsistent and their listens needs to be re-established by
285290
* RemoteStore.
286291
*/
287-
private pendingTargetResets = new SortedSet<TargetId>(primitiveComparator);
292+
private pendingTargetResets = new SortedMap<TargetId, TargetPurpose>(
293+
primitiveComparator
294+
);
288295

289296
/**
290297
* Processes and adds the DocumentWatchChange to the current set of changes.
@@ -422,38 +429,50 @@ export class WatchChangeAggregator {
422429
// raise a snapshot with `isFromCache:true`.
423430
if (currentSize !== expectedCount) {
424431
// Apply bloom filter to identify and mark removed documents.
425-
const bloomFilterApplied = this.applyBloomFilter(
426-
watchChange,
427-
currentSize
428-
);
429-
if (!bloomFilterApplied) {
432+
const status = this.applyBloomFilter(watchChange, currentSize);
433+
if (status !== BloomFilterApplicationStatus.Success) {
430434
// If bloom filter application fails, we reset the mapping and
431435
// trigger re-run of the query.
432436
this.resetTarget(targetId);
433-
this.pendingTargetResets = this.pendingTargetResets.add(targetId);
437+
const purpose: TargetPurpose =
438+
status === BloomFilterApplicationStatus.FalsePositive
439+
? TargetPurpose.ExistenceFilterMismatchBloom
440+
: TargetPurpose.ExistenceFilterMismatch;
441+
442+
this.pendingTargetResets = this.pendingTargetResets.insert(
443+
targetId,
444+
purpose
445+
);
434446
}
435447
}
436448
}
437449
}
438450
}
439451

440-
/** Returns whether a bloom filter removed the deleted documents successfully. */
452+
/**
453+
* Apply bloom filter to remove the deleted documents, and return the
454+
* application status.
455+
*/
441456
private applyBloomFilter(
442457
watchChange: ExistenceFilterChange,
443458
currentCount: number
444-
): boolean {
459+
): BloomFilterApplicationStatus {
445460
const { unchangedNames, count: expectedCount } =
446461
watchChange.existenceFilter;
447462

448463
if (!unchangedNames || !unchangedNames.bits) {
449-
return false;
464+
return BloomFilterApplicationStatus.Skipped;
450465
}
451466

452467
const {
453468
bits: { bitmap = '', padding = 0 },
454469
hashCount = 0
455470
} = unchangedNames;
456471

472+
if (bitmap.length === 0) {
473+
return BloomFilterApplicationStatus.Skipped;
474+
}
475+
457476
let normalizedBitmap: Uint8Array;
458477
try {
459478
normalizedBitmap = normalizeByteString(bitmap).toUint8Array();
@@ -464,7 +483,7 @@ export class WatchChangeAggregator {
464483
err.message +
465484
'); ignoring the bloom filter and falling back to full re-query.'
466485
);
467-
return false;
486+
return BloomFilterApplicationStatus.Skipped;
468487
} else {
469488
throw err;
470489
}
@@ -480,15 +499,19 @@ export class WatchChangeAggregator {
480499
} else {
481500
logWarn('Applying bloom filter failed: ', err);
482501
}
483-
return false;
502+
return BloomFilterApplicationStatus.Skipped;
484503
}
485504

486505
const removedDocumentCount = this.filterRemovedDocuments(
487506
watchChange.targetId,
488507
bloomFilter
489508
);
490509

491-
return expectedCount === currentCount - removedDocumentCount;
510+
if (expectedCount !== currentCount - removedDocumentCount) {
511+
return BloomFilterApplicationStatus.FalsePositive;
512+
}
513+
514+
return BloomFilterApplicationStatus.Success;
492515
}
493516

494517
/**
@@ -599,7 +622,9 @@ export class WatchChangeAggregator {
599622

600623
this.pendingDocumentUpdates = mutableDocumentMap();
601624
this.pendingDocumentTargetMapping = documentTargetMap();
602-
this.pendingTargetResets = new SortedSet<TargetId>(primitiveComparator);
625+
this.pendingTargetResets = new SortedMap<TargetId, TargetPurpose>(
626+
primitiveComparator
627+
);
603628

604629
return remoteEvent;
605630
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2061,8 +2061,8 @@ apiDescribe('Queries', (persistence: boolean) => {
20612061
});
20622062
});
20632063

2064-
// TODO(Mila): Skip the test when using emulator as there is a bug related to
2065-
// sending existence filter in response: b/270731363. Remove the condition
2064+
// TODO(Mila): Skip the test when using emulator as there is a bug related to
2065+
// sending existence filter in response: b/270731363. Remove the condition
20662066
// here once the bug is resolved.
20672067
// eslint-disable-next-line no-restricted-properties
20682068
(USE_EMULATOR ? it.skip : it)(

packages/firestore/test/unit/specs/existence_filter_spec.test.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717

1818
import { newQueryForPath } from '../../../src/core/query';
19+
import { TargetPurpose } from '../../../src/local/target_data';
1920
import { Code } from '../../../src/util/error';
2021
import {
2122
deletedDoc,
@@ -305,7 +306,11 @@ describeSpec('Existence Filters:', [], () => {
305306
// BloomFilter correctly identifies docC is deleted, but yields false
306307
// positive results for docB. Re-run query is triggered.
307308
.expectEvents(query1, { fromCache: true })
308-
.expectActiveTargets({ query: query1, resumeToken: '' })
309+
.expectActiveTargets({
310+
query: query1,
311+
resumeToken: '',
312+
targetPurpose: TargetPurpose.ExistenceFilterMismatchBloom
313+
})
309314
);
310315
}
311316
);
@@ -394,7 +399,11 @@ describeSpec('Existence Filters:', [], () => {
394399
.watchSnapshots(2000)
395400
// Re-run query is triggered.
396401
.expectEvents(query1, { fromCache: true })
397-
.expectActiveTargets({ query: query1, resumeToken: '' })
402+
.expectActiveTargets({
403+
query: query1,
404+
resumeToken: '',
405+
targetPurpose: TargetPurpose.ExistenceFilterMismatch
406+
})
398407
);
399408
}
400409
);
@@ -424,7 +433,11 @@ describeSpec('Existence Filters:', [], () => {
424433
.watchSnapshots(2000)
425434
// Re-run query is triggered.
426435
.expectEvents(query1, { fromCache: true })
427-
.expectActiveTargets({ query: query1, resumeToken: '' })
436+
.expectActiveTargets({
437+
query: query1,
438+
resumeToken: '',
439+
targetPurpose: TargetPurpose.ExistenceFilterMismatch
440+
})
428441
);
429442
}
430443
);
@@ -452,7 +465,11 @@ describeSpec('Existence Filters:', [], () => {
452465
.watchSnapshots(2000)
453466
// Re-run query is triggered.
454467
.expectEvents(query1, { fromCache: true })
455-
.expectActiveTargets({ query: query1, resumeToken: '' })
468+
.expectActiveTargets({
469+
query: query1,
470+
resumeToken: '',
471+
targetPurpose: TargetPurpose.ExistenceFilterMismatch
472+
})
456473
);
457474
});
458475

0 commit comments

Comments
 (0)