Skip to content

Commit b7b7899

Browse files
Delete index entries for deleted documents
1 parent 7c9c133 commit b7b7899

13 files changed

+106
-57
lines changed

firebase-firestore/src/main/java/com/google/firebase/firestore/local/LocalStore.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,6 +502,7 @@ private DocumentChangeResult populateDocumentChanges(
502502
@Nullable Map<DocumentKey, SnapshotVersion> documentVersions,
503503
SnapshotVersion globalVersion) {
504504
Map<DocumentKey, MutableDocument> changedDocs = new HashMap<>();
505+
List<DocumentKey> removedDocs = new ArrayList<>();
505506
Set<DocumentKey> conditionChanged = new HashSet<>();
506507

507508
// Each loop iteration only affects its "own" doc, so it's safe to get all the remote
@@ -525,7 +526,7 @@ private DocumentChangeResult populateDocumentChanges(
525526
if (doc.isNoDocument() && doc.getVersion().equals(SnapshotVersion.NONE)) {
526527
// NoDocuments with SnapshotVersion.NONE are used in manufactured events. We remove
527528
// these documents from cache since we lost access.
528-
remoteDocuments.remove(doc.getKey());
529+
removedDocs.add(doc.getKey());
529530
changedDocs.put(key, doc);
530531
} else if (!existingDoc.isValidDocument()
531532
|| doc.getVersion().compareTo(existingDoc.getVersion()) > 0
@@ -545,6 +546,7 @@ private DocumentChangeResult populateDocumentChanges(
545546
doc.getVersion());
546547
}
547548
}
549+
remoteDocuments.removeAll(removedDocs);
548550
return new DocumentChangeResult(changedDocs, conditionChanged);
549551
}
550552

firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryEagerReferenceDelegate.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616

1717
import com.google.firebase.firestore.core.ListenSequence;
1818
import com.google.firebase.firestore.model.DocumentKey;
19+
import java.util.ArrayList;
1920
import java.util.HashSet;
21+
import java.util.List;
2022
import java.util.Set;
2123

2224
/** Provides eager garbage collection for MemoryPersistence. */
@@ -72,11 +74,13 @@ public void onTransactionStarted() {
7274
@Override
7375
public void onTransactionCommitted() {
7476
MemoryRemoteDocumentCache remoteDocuments = persistence.getRemoteDocumentCache();
77+
List<DocumentKey> docsToRemove = new ArrayList<>();
7578
for (DocumentKey key : orphanedDocuments) {
7679
if (!isReferenced(key)) {
77-
remoteDocuments.remove(key);
80+
docsToRemove.add(key);
7881
}
7982
}
83+
remoteDocuments.removeAll(docsToRemove);
8084
orphanedDocuments = null;
8185
}
8286

firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryLruReferenceDelegate.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
import com.google.firebase.firestore.model.DocumentKey;
2222
import com.google.firebase.firestore.model.MutableDocument;
2323
import com.google.firebase.firestore.util.Consumer;
24+
import java.util.ArrayList;
2425
import java.util.HashMap;
26+
import java.util.List;
2527
import java.util.Map;
2628

2729
/** Provides LRU garbage collection functionality for MemoryPersistence. */
@@ -115,17 +117,17 @@ public int removeTargets(long upperBound, SparseArray<?> activeTargetIds) {
115117

116118
@Override
117119
public int removeOrphanedDocuments(long upperBound) {
118-
int count = 0;
119120
MemoryRemoteDocumentCache cache = persistence.getRemoteDocumentCache();
121+
List<DocumentKey> docsToRemove = new ArrayList<>();
120122
for (MutableDocument doc : cache.getDocuments()) {
121123
DocumentKey key = doc.getKey();
122124
if (!isPinned(key, upperBound)) {
123-
cache.remove(key);
125+
docsToRemove.add(key);
124126
orphanedSequenceNumbers.remove(key);
125-
count++;
126127
}
127128
}
128-
return count;
129+
cache.removeAll(docsToRemove);
130+
return docsToRemove.size();
129131
}
130132

131133
@Override

firebase-firestore/src/main/java/com/google/firebase/firestore/local/MemoryRemoteDocumentCache.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,17 @@
2020
import androidx.annotation.NonNull;
2121
import com.google.firebase.database.collection.ImmutableSortedMap;
2222
import com.google.firebase.firestore.core.Query;
23+
import com.google.firebase.firestore.model.Document;
2324
import com.google.firebase.firestore.model.DocumentKey;
2425
import com.google.firebase.firestore.model.FieldIndex.IndexOffset;
2526
import com.google.firebase.firestore.model.MutableDocument;
2627
import com.google.firebase.firestore.model.ResourcePath;
2728
import com.google.firebase.firestore.model.SnapshotVersion;
29+
import java.util.ArrayList;
30+
import java.util.Collection;
2831
import java.util.HashMap;
2932
import java.util.Iterator;
33+
import java.util.List;
3034
import java.util.Map;
3135

3236
/** In-memory cache of remote documents. */
@@ -62,8 +66,15 @@ public void add(MutableDocument document, SnapshotVersion readTime) {
6266
}
6367

6468
@Override
65-
public void remove(DocumentKey key) {
66-
docs = docs.remove(key);
69+
public void removeAll(Collection<DocumentKey> keys) {
70+
hardAssert(indexManager != null, "setIndexManager() not called");
71+
72+
List<Document> deletedDocs = new ArrayList<>();
73+
for (DocumentKey key : keys) {
74+
docs = docs.remove(key);
75+
deletedDocs.add(MutableDocument.newNoDocument(key, SnapshotVersion.NONE));
76+
}
77+
indexManager.updateIndexEntries(deletedDocs);
6778
}
6879

6980
@Override

firebase-firestore/src/main/java/com/google/firebase/firestore/local/RemoteDocumentCache.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.firebase.firestore.model.FieldIndex;
2121
import com.google.firebase.firestore.model.MutableDocument;
2222
import com.google.firebase.firestore.model.SnapshotVersion;
23+
import java.util.Collection;
2324
import java.util.Map;
2425

2526
/**
@@ -45,8 +46,8 @@ interface RemoteDocumentCache {
4546
*/
4647
void add(MutableDocument document, SnapshotVersion readTime);
4748

48-
/** Removes the cached entry for the given key (no-op if no entry exists). */
49-
void remove(DocumentKey documentKey);
49+
/** Removes the cached entries for the given keys (no-op if no entry exists). */
50+
void removeAll(Collection<DocumentKey> keys);
5051

5152
/**
5253
* Looks up an entry in the cache.

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteIndexManager.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,8 @@ public void deleteFieldIndex(FieldIndex index) {
224224
@Override
225225
public void updateIndexEntries(Collection<Document> documents) {
226226
hardAssert(started, "IndexManager not started");
227+
if (!Persistence.INDEXING_SUPPORT_ENABLED) return;
228+
227229
for (Document document : documents) {
228230
Collection<FieldIndex> fieldIndexes = getFieldIndexes(document.getKey().getCollectionGroup());
229231
for (FieldIndex fieldIndex : fieldIndexes) {

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteLruReferenceDelegate.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import com.google.firebase.firestore.model.DocumentKey;
2222
import com.google.firebase.firestore.model.ResourcePath;
2323
import com.google.firebase.firestore.util.Consumer;
24+
import java.util.ArrayList;
25+
import java.util.List;
2426

2527
/** Provides LRU functionality for SQLite persistence. */
2628
class SQLiteLruReferenceDelegate implements ReferenceDelegate, LruDelegate {
@@ -159,6 +161,8 @@ public int removeOrphanedDocuments(long upperBound) {
159161

160162
boolean resultsRemaining = true;
161163

164+
List<DocumentKey> docsToRemove = new ArrayList<>();
165+
162166
while (resultsRemaining) {
163167
int rowsProccessed =
164168
persistence
@@ -171,14 +175,15 @@ public int removeOrphanedDocuments(long upperBound) {
171175
DocumentKey key = DocumentKey.fromPath(path);
172176
if (!isPinned(key)) {
173177
count[0]++;
174-
persistence.getRemoteDocumentCache().remove(key);
178+
docsToRemove.add(key);
175179
removeSentinel(key);
176180
}
177181
});
178182

179183
resultsRemaining = (rowsProccessed == REMOVE_ORPHANED_DOCUMENTS_BATCH_SIZE);
180184
}
181185

186+
persistence.getRemoteDocumentCache().removeAll(docsToRemove);
182187
return count[0];
183188
}
184189

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLitePersistence.java

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import static com.google.firebase.firestore.util.Assert.fail;
1818
import static com.google.firebase.firestore.util.Assert.hardAssert;
19+
import static com.google.firebase.firestore.util.Util.repeatSequence;
1920

2021
import android.content.Context;
2122
import android.database.Cursor;
@@ -671,23 +672,27 @@ boolean hasMoreSubqueries() {
671672
return argsIter.hasNext();
672673
}
673674

674-
/** Performs the next subquery and returns a {@link Query} object for method chaining. */
675-
Query performNextSubquery() {
676-
++subqueriesPerformed;
677-
675+
private Object[] getNextSubqueryArgs() {
678676
List<Object> subqueryArgs = new ArrayList<>(argsHead);
679-
StringBuilder placeholdersBuilder = new StringBuilder();
680677
for (int i = 0; argsIter.hasNext() && i < LIMIT - argsHead.size(); i++) {
681-
if (i > 0) {
682-
placeholdersBuilder.append(", ");
683-
}
684-
placeholdersBuilder.append("?");
685-
686678
subqueryArgs.add(argsIter.next());
687679
}
688-
String placeholders = placeholdersBuilder.toString();
680+
return subqueryArgs.toArray();
681+
}
689682

690-
return db.query(head + placeholders + tail).binding(subqueryArgs.toArray());
683+
/** Performs the next subquery and returns a {@link Query} object for method chaining. */
684+
Query performNextSubquery() {
685+
++subqueriesPerformed;
686+
Object[] subqueryArgs = getNextSubqueryArgs();
687+
return db.query(head + repeatSequence("?", subqueryArgs.length, ", ") + tail)
688+
.binding(subqueryArgs);
689+
}
690+
691+
/** Executes the next subquery. */
692+
void executeNextSubquery() {
693+
++subqueriesPerformed;
694+
Object[] subqueryArgs = getNextSubqueryArgs();
695+
db.execute(head + repeatSequence("?", subqueryArgs.length, ", ") + tail, subqueryArgs);
691696
}
692697

693698
/** How many subqueries were performed. */

firebase-firestore/src/main/java/com/google/firebase/firestore/local/SQLiteRemoteDocumentCache.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.firebase.Timestamp;
2121
import com.google.firebase.database.collection.ImmutableSortedMap;
2222
import com.google.firebase.firestore.core.Query;
23+
import com.google.firebase.firestore.model.Document;
2324
import com.google.firebase.firestore.model.DocumentCollections;
2425
import com.google.firebase.firestore.model.DocumentKey;
2526
import com.google.firebase.firestore.model.FieldIndex;
@@ -30,6 +31,7 @@
3031
import com.google.protobuf.InvalidProtocolBufferException;
3132
import com.google.protobuf.MessageLite;
3233
import java.util.ArrayList;
34+
import java.util.Collection;
3335
import java.util.HashMap;
3436
import java.util.List;
3537
import java.util.Map;
@@ -75,10 +77,24 @@ public void add(MutableDocument document, SnapshotVersion readTime) {
7577
}
7678

7779
@Override
78-
public void remove(DocumentKey documentKey) {
79-
String path = EncodedPath.encode(documentKey.getPath());
80+
public void removeAll(Collection<DocumentKey> keys) {
81+
if (keys.isEmpty()) return;
82+
List<Object> encodedPaths = new ArrayList<>();
83+
List<Document> deletedDocs = new ArrayList<>();
84+
85+
for (DocumentKey key : keys) {
86+
encodedPaths.add(EncodedPath.encode(key.getPath()));
87+
deletedDocs.add(MutableDocument.newNoDocument(key, SnapshotVersion.NONE));
88+
}
89+
90+
SQLitePersistence.LongQuery longQuery =
91+
new SQLitePersistence.LongQuery(
92+
db, "DELETE FROM remote_documents WHERE path IN (", encodedPaths, ")");
93+
while (longQuery.hasMoreSubqueries()) {
94+
longQuery.executeNextSubquery();
95+
}
8096

81-
db.execute("DELETE FROM remote_documents WHERE path = ?", path);
97+
indexManager.updateIndexEntries(deletedDocs);
8298
}
8399

84100
@Override

firebase-firestore/src/test/java/com/google/firebase/firestore/local/CountingQueryEngine.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.firebase.firestore.model.mutation.Mutation;
2828
import com.google.firebase.firestore.model.mutation.MutationBatch;
2929
import com.google.protobuf.ByteString;
30+
import java.util.Collection;
3031
import java.util.List;
3132
import java.util.Map;
3233

@@ -128,11 +129,10 @@ public void add(MutableDocument document, SnapshotVersion readTime) {
128129
}
129130

130131
@Override
131-
public void remove(DocumentKey documentKey) {
132-
subject.remove(documentKey);
132+
public void removeAll(Collection<DocumentKey> keys) {
133+
subject.removeAll(keys);
133134
}
134135

135-
@Nullable
136136
@Override
137137
public MutableDocument get(DocumentKey documentKey) {
138138
MutableDocument result = subject.get(documentKey);

firebase-firestore/src/test/java/com/google/firebase/firestore/local/MutationQueueTestCase.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ public void setUp() {
7272
persistence.getReferenceDelegate().setInMemoryPins(new ReferenceSet());
7373
IndexManager indexManager = persistence.getIndexManager(User.UNAUTHENTICATED);
7474
indexManager.start();
75+
persistence.getRemoteDocumentCache().setIndexManager(indexManager);
7576
mutationQueue = persistence.getMutationQueue(User.UNAUTHENTICATED, indexManager);
7677
mutationQueue.start();
7778
}

0 commit comments

Comments
 (0)