Skip to content

Commit b2ba569

Browse files
Merge master into release
2 parents 9cc1eea + fe2ac13 commit b2ba569

File tree

5 files changed

+156
-189
lines changed

5 files changed

+156
-189
lines changed

.changeset/hungry-zebras-laugh.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
---
22
"@firebase/firestore": minor
3+
"firebase": minor
34
---
45

56
Expose the MultiDB API for Public Preview. [#7356](https://github.com/firebase/firebase-js-sdk/pull/7356)

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

+147-177
Original file line numberDiff line numberDiff line change
@@ -277,8 +277,7 @@ describeSpec('Existence Filters:', [], () => {
277277
*/
278278
specTest(
279279
'Full re-query is skipped when bloom filter can identify documents deleted',
280-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
281-
['no-ios'],
280+
[],
282281
() => {
283282
const query1 = query('collection');
284283
const docA = doc('collection/a', 1000, { v: 1 });
@@ -310,8 +309,7 @@ describeSpec('Existence Filters:', [], () => {
310309

311310
specTest(
312311
'Full re-query is triggered when bloom filter can not identify documents deleted',
313-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
314-
['no-ios'],
312+
[],
315313
() => {
316314
const query1 = query('collection');
317315
const docA = doc('collection/a', 1000, { v: 1 });
@@ -343,8 +341,7 @@ describeSpec('Existence Filters:', [], () => {
343341

344342
specTest(
345343
'Bloom filter can process special characters in document name',
346-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
347-
['no-ios'],
344+
[],
348345
() => {
349346
const query1 = query('collection');
350347
const docA = doc('collection/ÀÒ∑', 1000, { v: 1 });
@@ -372,8 +369,7 @@ describeSpec('Existence Filters:', [], () => {
372369

373370
specTest(
374371
'Bloom filter fills in default values for undefined padding and hashCount',
375-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
376-
['no-ios'],
372+
[],
377373
() => {
378374
const query1 = query('collection');
379375
const docA = doc('collection/a', 1000, { v: 1 });
@@ -445,8 +441,7 @@ describeSpec('Existence Filters:', [], () => {
445441

446442
specTest(
447443
'Full re-query is triggered when bloom filter hashCount is invalid',
448-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
449-
['no-ios'],
444+
[],
450445
() => {
451446
const query1 = query('collection');
452447
const docA = doc('collection/a', 1000, { v: 1 });
@@ -478,187 +473,162 @@ describeSpec('Existence Filters:', [], () => {
478473
}
479474
);
480475

481-
specTest(
482-
'Full re-query is triggered when bloom filter is empty',
483-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
484-
['no-ios'],
485-
() => {
486-
const query1 = query('collection');
487-
const docA = doc('collection/a', 1000, { v: 1 });
488-
const docB = doc('collection/b', 1000, { v: 1 });
489-
490-
//Generate an empty bloom filter.
491-
const bloomFilterProto = generateBloomFilterProto({
492-
contains: [],
493-
notContains: [],
494-
bitCount: 0,
495-
hashCount: 0
496-
});
497-
498-
return (
499-
spec()
500-
.userListens(query1)
501-
.watchAcksFull(query1, 1000, docA, docB)
502-
.expectEvents(query1, { added: [docA, docB] })
503-
// DocB is deleted in the next sync.
504-
.watchFilters([query1], [docA.key], bloomFilterProto)
505-
.watchSnapshots(2000)
506-
// Re-run query is triggered.
507-
.expectEvents(query1, { fromCache: true })
508-
.expectActiveTargets({
509-
query: query1,
510-
resumeToken: '',
511-
targetPurpose: TargetPurpose.ExistenceFilterMismatch
512-
})
513-
);
514-
}
515-
);
476+
specTest('Full re-query is triggered when bloom filter is empty', [], () => {
477+
const query1 = query('collection');
478+
const docA = doc('collection/a', 1000, { v: 1 });
479+
const docB = doc('collection/b', 1000, { v: 1 });
516480

517-
specTest(
518-
'Same documents can have different bloom filters',
519-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
520-
['no-ios'],
521-
() => {
522-
const query1 = query('collection', filter('v', '<=', 2));
523-
const query2 = query('collection', filter('v', '>=', 2));
481+
//Generate an empty bloom filter.
482+
const bloomFilterProto = generateBloomFilterProto({
483+
contains: [],
484+
notContains: [],
485+
bitCount: 0,
486+
hashCount: 0
487+
});
524488

525-
const docA = doc('collection/a', 1000, { v: 1 });
526-
const docB = doc('collection/b', 1000, { v: 2 });
527-
const docC = doc('collection/c', 1000, { v: 3 });
489+
return (
490+
spec()
491+
.userListens(query1)
492+
.watchAcksFull(query1, 1000, docA, docB)
493+
.expectEvents(query1, { added: [docA, docB] })
494+
// DocB is deleted in the next sync.
495+
.watchFilters([query1], [docA.key], bloomFilterProto)
496+
.watchSnapshots(2000)
497+
// Re-run query is triggered.
498+
.expectEvents(query1, { fromCache: true })
499+
.expectActiveTargets({
500+
query: query1,
501+
resumeToken: '',
502+
targetPurpose: TargetPurpose.ExistenceFilterMismatch
503+
})
504+
);
505+
});
528506

529-
const bloomFilterProto1 = generateBloomFilterProto({
530-
contains: [docB],
531-
notContains: [docA, docC],
532-
bitCount: 5,
533-
hashCount: 2
534-
});
535-
const bloomFilterProto2 = generateBloomFilterProto({
536-
contains: [docB],
537-
notContains: [docA, docC],
538-
bitCount: 4,
539-
hashCount: 1
540-
});
541-
return (
542-
spec()
543-
.userListens(query1)
544-
.watchAcksFull(query1, 1000, docA, docB)
545-
.expectEvents(query1, { added: [docA, docB] })
546-
.userListens(query2)
547-
.expectEvents(query2, { added: [docB], fromCache: true })
548-
.watchAcksFull(query2, 1001, docB, docC)
549-
.expectEvents(query2, { added: [docC] })
507+
specTest('Same documents can have different bloom filters', [], () => {
508+
const query1 = query('collection', filter('v', '<=', 2));
509+
const query2 = query('collection', filter('v', '>=', 2));
550510

551-
// DocA is deleted in the next sync for query1.
552-
.watchFilters([query1], [docB.key], bloomFilterProto1)
553-
.watchSnapshots(2000)
554-
// BloomFilter identify docA is deleted, skip full query.
555-
.expectEvents(query1, { fromCache: true })
556-
.expectLimboDocs(docA.key) // DocA is now in limbo.
557-
558-
// DocC is deleted in the next sync for query2.
559-
.watchFilters([query2], [docB.key], bloomFilterProto2)
560-
.watchSnapshots(3000)
561-
// BloomFilter identify docC is deleted, skip full query.
562-
.expectEvents(query2, { fromCache: true })
563-
.expectLimboDocs(docA.key, docC.key) // DocC is now in limbo.
564-
);
565-
}
566-
);
511+
const docA = doc('collection/a', 1000, { v: 1 });
512+
const docB = doc('collection/b', 1000, { v: 2 });
513+
const docC = doc('collection/c', 1000, { v: 3 });
514+
515+
const bloomFilterProto1 = generateBloomFilterProto({
516+
contains: [docB],
517+
notContains: [docA, docC],
518+
bitCount: 5,
519+
hashCount: 2
520+
});
521+
const bloomFilterProto2 = generateBloomFilterProto({
522+
contains: [docB],
523+
notContains: [docA, docC],
524+
bitCount: 4,
525+
hashCount: 1
526+
});
527+
return (
528+
spec()
529+
.userListens(query1)
530+
.watchAcksFull(query1, 1000, docA, docB)
531+
.expectEvents(query1, { added: [docA, docB] })
532+
.userListens(query2)
533+
.expectEvents(query2, { added: [docB], fromCache: true })
534+
.watchAcksFull(query2, 1001, docB, docC)
535+
.expectEvents(query2, { added: [docC] })
567536

568-
specTest(
569-
'Bloom filter is handled at global snapshot',
570-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
571-
['no-ios'],
572-
() => {
573-
const query1 = query('collection');
574-
const docA = doc('collection/a', 1000, { v: 1 });
575-
const docB = doc('collection/b', 2000, { v: 2 });
576-
const docC = doc('collection/c', 3000, { v: 3 });
537+
// DocA is deleted in the next sync for query1.
538+
.watchFilters([query1], [docB.key], bloomFilterProto1)
539+
.watchSnapshots(2000)
540+
// BloomFilter identify docA is deleted, skip full query.
541+
.expectEvents(query1, { fromCache: true })
542+
.expectLimboDocs(docA.key) // DocA is now in limbo.
543+
544+
// DocC is deleted in the next sync for query2.
545+
.watchFilters([query2], [docB.key], bloomFilterProto2)
546+
.watchSnapshots(3000)
547+
// BloomFilter identify docC is deleted, skip full query.
548+
.expectEvents(query2, { fromCache: true })
549+
.expectLimboDocs(docA.key, docC.key) // DocC is now in limbo.
550+
);
551+
});
577552

578-
const bloomFilterProto = generateBloomFilterProto({
579-
contains: [docA],
580-
notContains: [docB]
581-
});
553+
specTest('Bloom filter is handled at global snapshot', [], () => {
554+
const query1 = query('collection');
555+
const docA = doc('collection/a', 1000, { v: 1 });
556+
const docB = doc('collection/b', 2000, { v: 2 });
557+
const docC = doc('collection/c', 3000, { v: 3 });
582558

583-
return (
584-
spec()
585-
.userListens(query1)
586-
.watchAcksFull(query1, 1000, docA, docB)
587-
.expectEvents(query1, { added: [docA, docB] })
588-
// Send a mismatching existence filter with one document, but don't
589-
// send a new global snapshot. We should not see an event until we
590-
// receive the snapshot.
591-
.watchFilters([query1], [docA.key], bloomFilterProto)
592-
.watchSends({ affects: [query1] }, docC)
593-
.watchSnapshots(2000)
594-
.expectEvents(query1, { added: [docC], fromCache: true })
595-
// Re-run of the query1 is skipped, docB is in limbo.
596-
.expectLimboDocs(docB.key)
597-
);
598-
}
599-
);
559+
const bloomFilterProto = generateBloomFilterProto({
560+
contains: [docA],
561+
notContains: [docB]
562+
});
600563

601-
specTest(
602-
'Bloom filter limbo resolution is denied',
603-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
604-
['no-ios'],
605-
() => {
606-
const query1 = query('collection');
607-
const docA = doc('collection/a', 1000, { v: 1 });
608-
const docB = doc('collection/b', 1000, { v: 1 });
609-
const bloomFilterProto = generateBloomFilterProto({
610-
contains: [docA],
611-
notContains: [docB]
612-
});
613-
return spec()
564+
return (
565+
spec()
614566
.userListens(query1)
615567
.watchAcksFull(query1, 1000, docA, docB)
616568
.expectEvents(query1, { added: [docA, docB] })
569+
// Send a mismatching existence filter with one document, but don't
570+
// send a new global snapshot. We should not see an event until we
571+
// receive the snapshot.
617572
.watchFilters([query1], [docA.key], bloomFilterProto)
573+
.watchSends({ affects: [query1] }, docC)
618574
.watchSnapshots(2000)
619-
.expectEvents(query1, { fromCache: true })
620-
.expectLimboDocs(docB.key) // DocB is now in limbo.
621-
.watchRemoves(
622-
newQueryForPath(docB.key.path),
623-
new RpcError(Code.PERMISSION_DENIED, 'no')
624-
)
625-
.expectLimboDocs() // DocB is no longer in limbo.
626-
.expectEvents(query1, {
627-
removed: [docB]
628-
});
629-
}
630-
);
631-
632-
specTest(
633-
'Bloom filter with large size works as expected',
634-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
635-
['no-ios'],
636-
() => {
637-
const query1 = query('collection');
638-
const docs = [];
639-
for (let i = 0; i < 100; i++) {
640-
docs.push(doc(`collection/doc${i}`, 1000, { v: 1 }));
641-
}
642-
const docKeys = docs.map(item => item.key);
575+
.expectEvents(query1, { added: [docC], fromCache: true })
576+
// Re-run of the query1 is skipped, docB is in limbo.
577+
.expectLimboDocs(docB.key)
578+
);
579+
});
643580

644-
const bloomFilterProto = generateBloomFilterProto({
645-
contains: docs.slice(0, 50),
646-
notContains: docs.slice(50),
647-
bitCount: 1000,
648-
hashCount: 16
581+
specTest('Bloom filter limbo resolution is denied', [], () => {
582+
const query1 = query('collection');
583+
const docA = doc('collection/a', 1000, { v: 1 });
584+
const docB = doc('collection/b', 1000, { v: 1 });
585+
const bloomFilterProto = generateBloomFilterProto({
586+
contains: [docA],
587+
notContains: [docB]
588+
});
589+
return spec()
590+
.userListens(query1)
591+
.watchAcksFull(query1, 1000, docA, docB)
592+
.expectEvents(query1, { added: [docA, docB] })
593+
.watchFilters([query1], [docA.key], bloomFilterProto)
594+
.watchSnapshots(2000)
595+
.expectEvents(query1, { fromCache: true })
596+
.expectLimboDocs(docB.key) // DocB is now in limbo.
597+
.watchRemoves(
598+
newQueryForPath(docB.key.path),
599+
new RpcError(Code.PERMISSION_DENIED, 'no')
600+
)
601+
.expectLimboDocs() // DocB is no longer in limbo.
602+
.expectEvents(query1, {
603+
removed: [docB]
649604
});
650-
return (
651-
spec()
652-
.userListens(query1)
653-
.watchAcksFull(query1, 1000, ...docs)
654-
.expectEvents(query1, { added: docs })
655-
// Doc0 to doc49 are deleted in the next sync.
656-
.watchFilters([query1], docKeys.slice(0, 50), bloomFilterProto)
657-
.watchSnapshots(2000)
658-
// BloomFilter correctly identifies docs that deleted, skip full query.
659-
.expectEvents(query1, { fromCache: true })
660-
.expectLimboDocs(...docKeys.slice(50))
661-
);
605+
});
606+
607+
specTest('Bloom filter with large size works as expected', [], () => {
608+
const query1 = query('collection');
609+
const docs = [];
610+
for (let i = 0; i < 100; i++) {
611+
docs.push(doc(`collection/doc${i}`, 1000, { v: 1 }));
662612
}
663-
);
613+
const docKeys = docs.map(item => item.key);
614+
615+
const bloomFilterProto = generateBloomFilterProto({
616+
contains: docs.slice(0, 50),
617+
notContains: docs.slice(50),
618+
bitCount: 1000,
619+
hashCount: 16
620+
});
621+
return (
622+
spec()
623+
.userListens(query1)
624+
.watchAcksFull(query1, 1000, ...docs)
625+
.expectEvents(query1, { added: docs })
626+
// Doc0 to doc49 are deleted in the next sync.
627+
.watchFilters([query1], docKeys.slice(0, 50), bloomFilterProto)
628+
.watchSnapshots(2000)
629+
// BloomFilter correctly identifies docs that deleted, skip full query.
630+
.expectEvents(query1, { fromCache: true })
631+
.expectLimboDocs(...docKeys.slice(50))
632+
);
633+
});
664634
});

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

+1-2
Original file line numberDiff line numberDiff line change
@@ -929,8 +929,7 @@ describeSpec('Limbo Documents:', [], () => {
929929

930930
specTest(
931931
'Limbo resolution throttling with bloom filter application',
932-
// TODO(b/278759251) Remove 'no-ios' once bloom filter is merged.
933-
['no-ios'],
932+
[],
934933
() => {
935934
const query1 = query('collection');
936935
const docA1 = doc('collection/a1', 1000, { key: 'a1' });

0 commit comments

Comments
 (0)