use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class LocalStoreTestCase method testLoadingNamedQueriesAllocatesTargetsAndUpdatesTargetDocumentMapping.
@Test
public void testLoadingNamedQueriesAllocatesTargetsAndUpdatesTargetDocumentMapping() {
bundleDocuments(doc("foo1/bar", 1, map("sum", 1337)), doc("foo2/bar", 1, map("sum", 42)));
assertChanged(doc("foo1/bar", 1, map("sum", 1337)), doc("foo2/bar", 1, map("sum", 42)));
assertContains(doc("foo1/bar", 1, map("sum", 1337)));
assertContains(doc("foo2/bar", 1, map("sum", 42)));
Target target1 = Query.atPath(ResourcePath.fromString("foo1")).toTarget();
NamedQuery namedQuery1 = new NamedQuery("query-1", new BundledQuery(target1, Query.LimitType.LIMIT_TO_FIRST), new SnapshotVersion(Timestamp.now()));
saveNamedQuery(namedQuery1, key("foo1/bar"));
assertHasNamedQuery(namedQuery1);
assertQueryDocumentMapping(/* targetId= */
4, key("foo1/bar"));
Target target2 = Query.atPath(ResourcePath.fromString("foo2")).toTarget();
NamedQuery namedQuery2 = new NamedQuery("query-2", new BundledQuery(target2, Query.LimitType.LIMIT_TO_FIRST), new SnapshotVersion(Timestamp.now()));
saveNamedQuery(namedQuery2, key("foo2/bar"));
assertHasNamedQuery(namedQuery2);
assertQueryDocumentMapping(/* targetId= */
6, key("foo2/bar"));
}
use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class LruGarbageCollectorTestCase method updateTargetInTransaction.
private void updateTargetInTransaction(TargetData targetData) {
SnapshotVersion version = version(2);
ByteString resumeToken = resumeToken(2);
TargetData updated = targetData.withResumeToken(resumeToken, version).withSequenceNumber(persistence.getReferenceDelegate().getCurrentSequenceNumber());
targetCache.updateTargetData(updated);
}
use of com.google.firebase.firestore.model.SnapshotVersion in project firebase-android-sdk by firebase.
the class LruGarbageCollectorTestCase method testRemoveTargetsThenGC.
@Test
public void testRemoveTargetsThenGC() {
// Create 3 targets, add docs to all of them
// Leave oldest target alone, it is still live
// Remove newest target
// Blind write 2 documents
// Add one of the blind write docs to oldest target (preserves it)
// Remove some documents from middle target (bumps sequence number)
// Add some documents from newest target to oldest target (preserves them)
// Update a doc from middle target
// Remove middle target
// Do a blind write
// GC up to but not including the removal of the middle target
//
// Expect:
// All docs in oldest target are still around
// One blind write is gone, the first one not added to oldest target
// Documents removed from middle target are gone, except ones added to oldest target
// Documents from newest target are gone, except those added to the old target as well
// Through the various steps, track which documents we expect to be removed vs
// documents we expect to be retained.
Set<DocumentKey> expectedRetained = new HashSet<>();
Set<DocumentKey> expectedRemoved = new HashSet<>();
// Add oldest target, 5 documents, and add those documents to the target.
// This target will not be removed, so all documents that are part of it will
// be retained.
TargetData oldestTarget = persistence.runTransaction("Add oldest target and docs", () -> {
TargetData targetData = addNextQueryInTransaction();
for (int i = 0; i < 5; i++) {
MutableDocument doc = cacheADocumentInTransaction();
expectedRetained.add(doc.getKey());
addDocumentToTarget(doc.getKey(), targetData.getTargetId());
}
return targetData;
});
// Add middle target and docs. Some docs will be removed from this target later,
// which we track here.
Set<DocumentKey> middleDocsToRemove = new HashSet<>();
// This will be the document in this target that gets an update later.
DocumentKey[] middleDocToUpdateHolder = new DocumentKey[1];
TargetData middleTarget = persistence.runTransaction("Add middle target and docs", () -> {
TargetData targetData = addNextQueryInTransaction();
// expect them to be removed.
for (int i = 0; i < 2; i++) {
MutableDocument doc = cacheADocumentInTransaction();
addDocumentToTarget(doc.getKey(), targetData.getTargetId());
expectedRemoved.add(doc.getKey());
middleDocsToRemove.add(doc.getKey());
}
// target prevents them from being GC'd, so they are also expected to be retained.
for (int i = 2; i < 4; i++) {
MutableDocument doc = cacheADocumentInTransaction();
expectedRetained.add(doc.getKey());
addDocumentToTarget(doc.getKey(), targetData.getTargetId());
}
// This doc stays in this target, but gets updated.
{
MutableDocument doc = cacheADocumentInTransaction();
expectedRetained.add(doc.getKey());
addDocumentToTarget(doc.getKey(), targetData.getTargetId());
middleDocToUpdateHolder[0] = doc.getKey();
}
return targetData;
});
DocumentKey middleDocToUpdate = middleDocToUpdateHolder[0];
// Add the newest target and add 5 documents to it. Some of those documents will
// additionally be added to the oldest target, which will cause those documents to
// be retained. The remaining documents are expected to be removed, since this target
// will be removed.
Set<DocumentKey> newestDocsToAddToOldest = new HashSet<>();
persistence.runTransaction("Add newest target and docs", () -> {
TargetData targetData = addNextQueryInTransaction();
// because this target will also be removed.
for (int i = 0; i < 3; i++) {
MutableDocument doc = cacheADocumentInTransaction();
expectedRemoved.add(doc.getKey());
addDocumentToTarget(doc.getKey(), targetData.getTargetId());
}
// docs to add to the oldest target in addition to this target. They will be retained
for (int i = 3; i < 5; i++) {
MutableDocument doc = cacheADocumentInTransaction();
expectedRetained.add(doc.getKey());
addDocumentToTarget(doc.getKey(), targetData.getTargetId());
newestDocsToAddToOldest.add(doc.getKey());
}
});
// 2 doc writes, add one of them to the oldest target.
persistence.runTransaction("2 doc writes, add one of them to the oldest target", () -> {
// write two docs and have them ack'd by the server. can skip mutation queue
// and set them in document cache. Add potentially orphaned first, also add one
// doc to a target.
MutableDocument doc1 = cacheADocumentInTransaction();
markDocumentEligibleForGcInTransaction(doc1.getKey());
updateTargetInTransaction(oldestTarget);
addDocumentToTarget(doc1.getKey(), oldestTarget.getTargetId());
// doc1 should be retained by being added to oldestTarget
expectedRetained.add(doc1.getKey());
MutableDocument doc2 = cacheADocumentInTransaction();
markDocumentEligibleForGcInTransaction(doc2.getKey());
// nothing is keeping doc2 around, it should be removed
expectedRemoved.add(doc2.getKey());
});
// Remove some documents from the middle target.
persistence.runTransaction("Remove some documents from the middle target", () -> {
updateTargetInTransaction(middleTarget);
for (DocumentKey key : middleDocsToRemove) {
removeDocumentFromTarget(key, middleTarget.getTargetId());
}
});
// Add a couple docs from the newest target to the oldest (preserves them past the point where
// newest was removed)
// upperBound is the sequence number right before middleTarget is updated, then removed.
long upperBound = persistence.runTransaction("Add a couple docs from the newest target to the oldest", () -> {
updateTargetInTransaction(oldestTarget);
for (DocumentKey key : newestDocsToAddToOldest) {
addDocumentToTarget(key, oldestTarget.getTargetId());
}
return persistence.getReferenceDelegate().getCurrentSequenceNumber();
});
// Update a doc in the middle target
persistence.runTransaction("Update a doc in the middle target", () -> {
SnapshotVersion newVersion = version(3);
MutableDocument doc = MutableDocument.newFoundDocument(middleDocToUpdate, newVersion, testValue);
documentCache.add(doc, newVersion);
updateTargetInTransaction(middleTarget);
});
// Remove the middle target
persistence.runTransaction("remove middle target", () -> persistence.getReferenceDelegate().removeTarget(middleTarget));
// Write a doc and get an ack, not part of a target
persistence.runTransaction("Write a doc and get an ack, not part of a target", () -> {
MutableDocument doc = cacheADocumentInTransaction();
// Mark it as eligible for GC, but this is after our upper bound for what we will collect.
markDocumentEligibleForGcInTransaction(doc.getKey());
// This should be retained, it's too new to get removed.
expectedRetained.add(doc.getKey());
});
// Finally, do the garbage collection, up to but not including the removal of middleTarget
SparseArray<TargetData> activeTargetIds = new SparseArray<>();
activeTargetIds.put(oldestTarget.getTargetId(), oldestTarget);
int targetsRemoved = garbageCollector.removeTargets(upperBound, activeTargetIds);
// Expect to remove newest target
assertEquals(1, targetsRemoved);
int docsRemoved = garbageCollector.removeOrphanedDocuments(upperBound);
assertEquals(expectedRemoved.size(), docsRemoved);
persistence.runTransaction("verify results", () -> {
for (DocumentKey key : expectedRemoved) {
assertFalse(documentCache.get(key).isValidDocument());
assertFalse(targetCache.containsKey(key));
}
for (DocumentKey key : expectedRetained) {
assertTrue(documentCache.get(key).isValidDocument());
}
});
}
Aggregations