Skip to content

Commit f9c9c1f

Browse files
Use Task API
1 parent 629cd45 commit f9c9c1f

File tree

6 files changed

+67
-47
lines changed

6 files changed

+67
-47
lines changed

firebase-firestore/CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Unreleased
2-
- [changed] Reduced execution time of queries with large result sets by up to
2+
- [changed] Improved performance of queries with large result sets by up to
33
60%.
44
- [changed] Instead of failing silently, Firestore now crashes the client app
55
if it fails to load SSL Ciphers. To avoid these crashes, you must bundle

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@
3535
import java.util.List;
3636
import java.util.Map;
3737

38-
/**
39-
* Serializer for values stored in the LocalStore.
40-
*
41-
* <p>This class is thread-safe.
42-
*/
38+
/** Serializer for values stored in the LocalStore. */
4339
public final class LocalSerializer {
4440

4541
private final RemoteSerializer rpcSerializer;

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

Lines changed: 25 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,23 @@
1717
import static com.google.firebase.firestore.util.Assert.fail;
1818
import static com.google.firebase.firestore.util.Assert.hardAssert;
1919

20+
import com.google.android.gms.tasks.Task;
21+
import com.google.android.gms.tasks.Tasks;
2022
import com.google.firebase.database.collection.ImmutableSortedMap;
2123
import com.google.firebase.firestore.core.Query;
2224
import com.google.firebase.firestore.model.Document;
25+
import com.google.firebase.firestore.model.DocumentCollections;
2326
import com.google.firebase.firestore.model.DocumentKey;
2427
import com.google.firebase.firestore.model.MaybeDocument;
2528
import com.google.firebase.firestore.model.ResourcePath;
2629
import com.google.firebase.firestore.util.Executors;
30+
import com.google.firebase.firestore.util.Util;
2731
import com.google.protobuf.InvalidProtocolBufferException;
2832
import com.google.protobuf.MessageLite;
2933
import java.util.ArrayList;
3034
import java.util.HashMap;
3135
import java.util.List;
3236
import java.util.Map;
33-
import java.util.concurrent.ConcurrentHashMap;
34-
import java.util.concurrent.Executor;
35-
import java.util.concurrent.Semaphore;
3637
import javax.annotation.Nullable;
3738

3839
final class SQLiteRemoteDocumentCache implements RemoteDocumentCache {
@@ -122,10 +123,7 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
122123
String prefixPath = EncodedPath.encode(prefix);
123124
String prefixSuccessorPath = EncodedPath.prefixSuccessor(prefixPath);
124125

125-
Map<DocumentKey, Document> allDocuments = new ConcurrentHashMap<>();
126-
127-
int[] pendingTaskCount = new int[] {0};
128-
Semaphore completedTasks = new Semaphore(0);
126+
List<Task<Document>> pendingTasks = new ArrayList<>();
129127

130128
db.query("SELECT path, contents FROM remote_documents WHERE path >= ? AND path < ?")
131129
.binding(prefixPath, prefixSuccessorPath)
@@ -145,33 +143,29 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
145143

146144
byte[] rawContents = row.getBlob(1);
147145

148-
++pendingTaskCount[0];
149-
150-
// Since scheduling background tasks incurs overhead, we only dispatch to a background
151-
// thread if there are still some documents remaining.
152-
Executor deserializationExecutor =
153-
row.isLast() ? Executors.DIRECT_EXECUTOR : Executors.BACKGROUND_EXECUTOR;
154-
deserializationExecutor.execute(
155-
() -> {
156-
MaybeDocument maybeDoc = decodeMaybeDocument(rawContents);
157-
if (maybeDoc instanceof Document) {
158-
allDocuments.put(maybeDoc.getKey(), (Document) maybeDoc);
159-
}
160-
completedTasks.release();
161-
});
146+
pendingTasks.add(
147+
Tasks.call(
148+
// Since scheduling background tasks incurs overhead, we only dispatch to a
149+
// background thread if there are still some documents remaining.
150+
row.isLast() ? Executors.DIRECT_EXECUTOR : Executors.BACKGROUND_EXECUTOR,
151+
() -> {
152+
MaybeDocument maybeDoc = decodeMaybeDocument(rawContents);
153+
if (!(maybeDoc instanceof Document)) {
154+
return null;
155+
}
156+
if (!query.matches((Document) maybeDoc)) {
157+
return null;
158+
}
159+
return (Document) maybeDoc;
160+
}));
162161
});
163162

164-
try {
165-
completedTasks.acquire(pendingTaskCount[0]);
166-
} catch (InterruptedException e) {
167-
Thread.currentThread().interrupt();
168-
}
169-
170163
ImmutableSortedMap<DocumentKey, Document> matchingDocuments =
171-
ImmutableSortedMap.Builder.emptyMap(DocumentKey.comparator());
172-
for (Map.Entry<DocumentKey, Document> entry : allDocuments.entrySet()) {
173-
if (query.matches(entry.getValue())) {
174-
matchingDocuments = matchingDocuments.insert(entry.getKey(), entry.getValue());
164+
DocumentCollections.emptyDocumentMap();
165+
List<Document> results = Util.await(Util.whenAllComplete(pendingTasks));
166+
for (Document doc : results) {
167+
if (doc != null) {
168+
matchingDocuments = matchingDocuments.insert(doc.getKey(), doc);
175169
}
176170
}
177171
return matchingDocuments;

firebase-firestore/src/main/java/com/google/firebase/firestore/model/DatabaseId.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,7 @@
1616

1717
import androidx.annotation.NonNull;
1818

19-
/**
20-
* Represents a particular database in Firestore.
21-
*
22-
* <p>This class is thread-safe.
23-
*/
19+
/** Represents a particular database in Firestore */
2420
public final class DatabaseId implements Comparable<DatabaseId> {
2521
public static final String DEFAULT_DATABASE_ID = "(default)";
2622

firebase-firestore/src/main/java/com/google/firebase/firestore/remote/RemoteSerializer.java

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,7 @@
106106
import java.util.Map;
107107
import java.util.Set;
108108

109-
/**
110-
* Serializer that converts to and from Firestore API protos.
111-
*
112-
* <p>This class is thread-safe.
113-
*/
109+
/** Serializer that converts to and from Firestore API protos. */
114110
public final class RemoteSerializer {
115111

116112
private final DatabaseId databaseId;

firebase-firestore/src/main/java/com/google/firebase/firestore/util/Util.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
import android.os.Looper;
1919
import androidx.annotation.Nullable;
2020
import com.google.android.gms.tasks.Continuation;
21+
import com.google.android.gms.tasks.Task;
22+
import com.google.android.gms.tasks.Tasks;
2123
import com.google.cloud.datastore.core.number.NumberComparisonHelper;
2224
import com.google.firebase.firestore.FieldPath;
2325
import com.google.firebase.firestore.FirebaseFirestoreException;
@@ -31,6 +33,7 @@
3133
import java.util.Comparator;
3234
import java.util.List;
3335
import java.util.Random;
36+
import java.util.concurrent.Semaphore;
3437

3538
/** A utility class for Firestore */
3639
public class Util {
@@ -222,4 +225,39 @@ public static void crashMainThread(RuntimeException exception) {
222225
throw exception;
223226
});
224227
}
228+
229+
/**
230+
* Waits for the given Task to complete.
231+
*
232+
* <p>Similar to Tasks.await() but can be called from the main thread to support unit testing.
233+
*/
234+
public static <TResult> TResult await(Task<TResult> task) {
235+
Semaphore semaphore = new Semaphore(0);
236+
task.addOnCompleteListener(Executors.BACKGROUND_EXECUTOR, t -> semaphore.release());
237+
try {
238+
semaphore.acquire();
239+
} catch (InterruptedException e) {
240+
Thread.currentThread().interrupt();
241+
}
242+
return task.getResult();
243+
}
244+
245+
/**
246+
* Returns a Task with a list of Tasks that completes successfully when all of the specified Tasks
247+
* complete.
248+
*
249+
* <p>Similar to Tasks.whenAllComplete() but does not schedule the completion on the main thread.
250+
*/
251+
public static <TResult> Task<List<TResult>> whenAllComplete(List<Task<TResult>> tasks) {
252+
return Tasks.whenAll(tasks)
253+
.continueWithTask(
254+
Executors.BACKGROUND_EXECUTOR,
255+
t -> {
256+
List<TResult> results = new ArrayList<>();
257+
for (Task<TResult> task : tasks) {
258+
results.add(task.getResult());
259+
}
260+
return Tasks.forResult(results);
261+
});
262+
}
225263
}

0 commit comments

Comments
 (0)