use of com.google.firebase.firestore.model.DocumentKey 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());
}
});
}
use of com.google.firebase.firestore.model.DocumentKey in project firebase-android-sdk by firebase.
the class LruGarbageCollectorTestCase method nextTestDocument.
private MutableDocument nextTestDocument() {
DocumentKey key = nextTestDocumentKey();
long version = 1;
Map<String, Object> data = new HashMap<>();
data.put("baz", true);
data.put("ok", "fine");
return doc(key, version, data);
}
use of com.google.firebase.firestore.model.DocumentKey in project firebase-android-sdk by firebase.
the class RemoteDocumentCacheTestCase method testSetReadAndDeleteLotsOfDocuments.
// PORTING NOTE: this test only applies to Android, because it's the only platform where the
// implementation of getAll might split the input into several queries.
@Test
public void testSetReadAndDeleteLotsOfDocuments() {
// Make sure to force SQLite implementation to split the large query into several smaller ones.
int lotsOfDocuments = 2000;
List<DocumentKey> keys = new ArrayList<>();
Map<DocumentKey, MutableDocument> expected = new HashMap<>();
for (int i = 0; i < lotsOfDocuments; i++) {
DocumentKey key = key("foo/" + i);
keys.add(key);
expected.put(key, addTestDocumentAtPath(key));
}
Map<DocumentKey, MutableDocument> read = remoteDocumentCache.getAll(keys);
assertEquals(expected, read);
remoteDocumentCache.removeAll(keys);
read = remoteDocumentCache.getAll(keys);
assertThat(read.values().stream().filter(MutableDocument::isFoundDocument).toArray()).isEmpty();
}
use of com.google.firebase.firestore.model.DocumentKey in project firebase-android-sdk by firebase.
the class RemoteDocumentCacheTestCase method testReadSeveralDocumentsIncludingMissingDocument.
@Test
public void testReadSeveralDocumentsIncludingMissingDocument() {
List<DocumentKey> keys = new ArrayList<>(Arrays.asList(key("foo/1"), key("foo/2")));
Map<DocumentKey, MutableDocument> written = new HashMap<>();
for (DocumentKey key : keys) {
written.put(key, addTestDocumentAtPath(key));
}
written.put(DocumentKey.fromPathString("foo/nonexistent"), null);
keys.add(key("foo/nonexistent"));
written.put(key("foo/nonexistent"), MutableDocument.newInvalidDocument(key("foo/nonexistent")));
Map<DocumentKey, MutableDocument> read = remoteDocumentCache.getAll(keys);
assertEquals(written, read);
}
use of com.google.firebase.firestore.model.DocumentKey in project firebase-android-sdk by firebase.
the class RemoteDocumentCacheTestCase method testGetAllFromSinceReadTimeAndSeconds.
@Test
public void testGetAllFromSinceReadTimeAndSeconds() {
addTestDocumentAtPath("b/old", /* updateTime= */
1, /* readTime= */
11);
addTestDocumentAtPath("b/current", /* updateTime= */
2, /* readTime= = */
12);
addTestDocumentAtPath("b/new", /* updateTime= */
3, /* readTime= = */
13);
ResourcePath collection = path("b");
Map<DocumentKey, MutableDocument> results = remoteDocumentCache.getAll(collection, IndexOffset.createSuccessor(version(12), -1));
assertThat(results.values()).containsExactly(doc("b/new", 3, DOC_DATA));
}
Aggregations