Search in sources :

Example 96 with DocumentKey

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());
        }
    });
}
Also used : SparseArray(android.util.SparseArray) SnapshotVersion(com.google.firebase.firestore.model.SnapshotVersion) DocumentKey(com.google.firebase.firestore.model.DocumentKey) MutableDocument(com.google.firebase.firestore.model.MutableDocument) HashSet(java.util.HashSet) Test(org.junit.Test)

Example 97 with DocumentKey

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);
}
Also used : HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) TestUtil.wrapObject(com.google.firebase.firestore.testutil.TestUtil.wrapObject) ByteString(com.google.protobuf.ByteString)

Example 98 with DocumentKey

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();
}
Also used : HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) ArrayList(java.util.ArrayList) MutableDocument(com.google.firebase.firestore.model.MutableDocument) Test(org.junit.Test)

Example 99 with DocumentKey

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);
}
Also used : HashMap(java.util.HashMap) DocumentKey(com.google.firebase.firestore.model.DocumentKey) ArrayList(java.util.ArrayList) MutableDocument(com.google.firebase.firestore.model.MutableDocument) Test(org.junit.Test)

Example 100 with DocumentKey

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));
}
Also used : ResourcePath(com.google.firebase.firestore.model.ResourcePath) DocumentKey(com.google.firebase.firestore.model.DocumentKey) MutableDocument(com.google.firebase.firestore.model.MutableDocument) Test(org.junit.Test)

Aggregations

DocumentKey (com.google.firebase.firestore.model.DocumentKey)134 MutableDocument (com.google.firebase.firestore.model.MutableDocument)52 Test (org.junit.Test)36 HashMap (java.util.HashMap)29 ArrayList (java.util.ArrayList)25 Document (com.google.firebase.firestore.model.Document)23 SnapshotVersion (com.google.firebase.firestore.model.SnapshotVersion)16 Mutation (com.google.firebase.firestore.model.mutation.Mutation)15 Map (java.util.Map)15 ResourcePath (com.google.firebase.firestore.model.ResourcePath)13 MutationBatch (com.google.firebase.firestore.model.mutation.MutationBatch)11 HashSet (java.util.HashSet)11 Overlay (com.google.firebase.firestore.model.mutation.Overlay)10 ImmutableSortedMap (com.google.firebase.database.collection.ImmutableSortedMap)9 TestUtil.patchMutation (com.google.firebase.firestore.testutil.TestUtil.patchMutation)7 TestUtil.setMutation (com.google.firebase.firestore.testutil.TestUtil.setMutation)7 ByteString (com.google.protobuf.ByteString)7 Query (com.google.firebase.firestore.core.Query)6 ObjectValue (com.google.firebase.firestore.model.ObjectValue)6 Timestamp (com.google.firebase.Timestamp)5