-
Notifications
You must be signed in to change notification settings - Fork 946
Dequeue limbo resolutions when their respective queries are stopped #4395
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cddbae3
c5f0747
b0ea581
35e6d3e
9bbd652
03a9c22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@firebase/firestore': patch | ||
--- | ||
|
||
Fixes a bug where local cache inconsistencies were unnecessarily being resolved. |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -922,4 +922,149 @@ describeSpec('Limbo Documents:', [], () => { | |
); | ||
} | ||
); | ||
|
||
specTest( | ||
'A limbo resolution for a document should not be started if one is already active', | ||
[], | ||
() => { | ||
const doc1 = doc('collection/doc', 1000, { key: 1 }); | ||
const fullQuery = query('collection'); | ||
const filteredQuery1 = query('collection', filter('key', '==', 1)); | ||
const filteredQuery2 = query('collection', filter('key', '>=', 1)); | ||
|
||
return ( | ||
spec() | ||
.withGCEnabled(false) | ||
|
||
// Start a limbo resolution listen for a document (doc1). | ||
.userListens(fullQuery) | ||
.watchAcksFull(fullQuery, 1000, doc1) | ||
.expectEvents(fullQuery, { added: [doc1] }) | ||
.userUnlistens(fullQuery) | ||
.userListens(filteredQuery1) | ||
.expectEvents(filteredQuery1, { added: [doc1], fromCache: true }) | ||
.watchAcksFull(filteredQuery1, 1001) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs() | ||
|
||
// Put doc1 into limbo in a different query; verify that another limbo | ||
// resolution is neither started nor enqueued. | ||
.userListens(filteredQuery2) | ||
.expectEvents(filteredQuery2, { added: [doc1], fromCache: true }) | ||
.watchAcksFull(filteredQuery2, 1002) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: Both of the tests below seem to be subsets of this test. I would therefore move this test under the other tests to first verify the basic implementation before going into the more completed cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
); | ||
} | ||
); | ||
|
||
specTest( | ||
'A limbo resolution for a document should not be enqueued if one is already enqueued', | ||
[], | ||
() => { | ||
const doc1 = doc('collection1/doc1', 1000, { key: 1 }); | ||
const fullQuery1 = query('collection1'); | ||
const filteredQuery1 = query('collection1', filter('key', '==', 1)); | ||
const doc2 = doc('collection2/doc2', 1000, { key: 2 }); | ||
const fullQuery2 = query('collection2'); | ||
const filteredQuery2a = query('collection2', filter('key', '==', 2)); | ||
const filteredQuery2b = query('collection2', filter('key', '>=', 2)); | ||
|
||
return ( | ||
spec() | ||
.withGCEnabled(false) | ||
.withMaxConcurrentLimboResolutions(1) | ||
|
||
// Max out the number of active limbo resolutions. | ||
.userListens(fullQuery1) | ||
.watchAcksFull(fullQuery1, 1000, doc1) | ||
.expectEvents(fullQuery1, { added: [doc1] }) | ||
.userUnlistens(fullQuery1) | ||
.userListens(filteredQuery1) | ||
.expectEvents(filteredQuery1, { added: [doc1], fromCache: true }) | ||
.watchAcksFull(filteredQuery1, 1001) | ||
.expectLimboDocs(doc1.key) | ||
|
||
// Start a limbo resolution listen for a different document (doc2). | ||
.userListens(fullQuery2) | ||
.watchAcksFull(fullQuery2, 1002, doc2) | ||
.expectEvents(fullQuery2, { added: [doc2] }) | ||
.userUnlistens(fullQuery2) | ||
.userListens(filteredQuery2a) | ||
.expectEvents(filteredQuery2a, { added: [doc2], fromCache: true }) | ||
.watchAcksFull(filteredQuery2a, 1003) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs(doc2.key) | ||
|
||
// Put doc2 into limbo in a different query and verify that it's not | ||
// added to the limbo resolution queue again. | ||
.userListens(filteredQuery2b) | ||
.expectEvents(filteredQuery2b, { added: [doc2], fromCache: true }) | ||
.watchAcksFull(filteredQuery2b, 1004) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs(doc2.key) | ||
); | ||
} | ||
); | ||
|
||
specTest( | ||
'A limbo resolution for a document should be removed from the queue when the last query listen stops', | ||
[], | ||
() => { | ||
const doc1 = doc('collection1/doc', 1000, { key: 1 }); | ||
const fullQuery1 = query('collection1'); | ||
const filteredQuery1 = query('collection1', filter('key', '==', 1)); | ||
|
||
const doc2 = doc('collection2/doc', 1000, { key: 2 }); | ||
const fullQuery2 = query('collection2'); | ||
const filteredQuery2a = query('collection2', filter('key', '==', 2)); | ||
const filteredQuery2b = query('collection2', filter('key', '>=', 2)); | ||
|
||
return ( | ||
spec() | ||
.withGCEnabled(false) | ||
.withMaxConcurrentLimboResolutions(1) | ||
|
||
// Max out the number of active limbo resolutions. | ||
.userListens(fullQuery1) | ||
.watchAcksFull(fullQuery1, 1000, doc1) | ||
.expectEvents(fullQuery1, { added: [doc1] }) | ||
.userUnlistens(fullQuery1) | ||
.userListens(filteredQuery1) | ||
.expectEvents(filteredQuery1, { added: [doc1], fromCache: true }) | ||
.watchAcksFull(filteredQuery1, 1001) | ||
.expectLimboDocs(doc1.key) | ||
|
||
// Enqueue a limbo resolution for doc2. | ||
.userListens(fullQuery2) | ||
.watchAcksFull(fullQuery2, 1002, doc2) | ||
.expectEvents(fullQuery2, { added: [doc2] }) | ||
.userUnlistens(fullQuery2) | ||
.userListens(filteredQuery2a) | ||
.expectEvents(filteredQuery2a, { added: [doc2], fromCache: true }) | ||
.watchAcksFull(filteredQuery2a, 1003) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs(doc2.key) | ||
|
||
// Start another query that puts doc2 into limbo again. | ||
.userListens(filteredQuery2b) | ||
.expectEvents(filteredQuery2b, { added: [doc2], fromCache: true }) | ||
.watchAcksFull(filteredQuery2b, 1004) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs(doc2.key) | ||
|
||
// Stop one of the queries that enqueued a limbo resolution for doc2; | ||
// verify that doc2 is not removed from the limbo resolution queue. | ||
.userUnlistens(filteredQuery2b) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs(doc2.key) | ||
|
||
// Stop the other query that enqueued a limbo resolution for doc2; | ||
// verify that doc2 *is* removed from the limbo resolution queue. | ||
.userUnlistens(filteredQuery2a) | ||
.expectLimboDocs(doc1.key) | ||
.expectEnqueuedLimboDocs() | ||
); | ||
} | ||
); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should maybe mention that we can use this datastructure in JS because it preserves insertion order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.