Skip to content

Commit ab1961e

Browse files
Make Task API work with tests
1 parent a3049bb commit ab1961e

File tree

6 files changed

+55
-38
lines changed

6 files changed

+55
-38
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 execution time 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: 12 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,18 @@
2222
import com.google.firebase.database.collection.ImmutableSortedMap;
2323
import com.google.firebase.firestore.core.Query;
2424
import com.google.firebase.firestore.model.Document;
25+
import com.google.firebase.firestore.model.DocumentCollections;
2526
import com.google.firebase.firestore.model.DocumentKey;
2627
import com.google.firebase.firestore.model.MaybeDocument;
2728
import com.google.firebase.firestore.model.ResourcePath;
2829
import com.google.firebase.firestore.util.Executors;
30+
import com.google.firebase.firestore.util.Util;
2931
import com.google.protobuf.InvalidProtocolBufferException;
3032
import com.google.protobuf.MessageLite;
3133
import java.util.ArrayList;
3234
import java.util.HashMap;
3335
import java.util.List;
3436
import java.util.Map;
35-
import java.util.concurrent.ExecutionException;
3637
import javax.annotation.Nullable;
3738

3839
final class SQLiteRemoteDocumentCache implements RemoteDocumentCache {
@@ -115,14 +116,17 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
115116
!query.isCollectionGroupQuery(),
116117
"CollectionGroup queries should be handled in LocalDocumentsView");
117118

119+
ImmutableSortedMap<DocumentKey, Document> matchingDocuments =
120+
DocumentCollections.emptyDocumentMap();
121+
118122
// Use the query path as a prefix for testing if a document matches the query.
119123
ResourcePath prefix = query.getPath();
120124
int immediateChildrenPathLength = prefix.length() + 1;
121125

122126
String prefixPath = EncodedPath.encode(prefix);
123127
String prefixSuccessorPath = EncodedPath.prefixSuccessor(prefixPath);
124128

125-
List<Task<Document>> pendingDeserializations = new ArrayList<>();
129+
List<Task<Document>> pendingTasks = new ArrayList<>();
126130

127131
db.query("SELECT path, contents FROM remote_documents WHERE path >= ? AND path < ?")
128132
.binding(prefixPath, prefixSuccessorPath)
@@ -142,43 +146,29 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
142146

143147
byte[] rawContents = row.getBlob(1);
144148

145-
pendingDeserializations.add(
149+
pendingTasks.add(
146150
Tasks.call(
147151
// Since scheduling background tasks incurs overhead, we only dispatch to a
148-
// background
149-
// thread if there are still some documents remaining.
152+
// background thread if there are still some documents remaining.
150153
row.isLast() ? Executors.DIRECT_EXECUTOR : Executors.BACKGROUND_EXECUTOR,
151154
() -> {
152155
MaybeDocument maybeDoc = decodeMaybeDocument(rawContents);
153156
if (!(maybeDoc instanceof Document)) {
154157
return null;
155158
}
156-
157159
if (!query.matches((Document) maybeDoc)) {
158160
return null;
159161
}
160-
161162
return (Document) maybeDoc;
162163
}));
163164
});
164165

165-
ImmutableSortedMap<DocumentKey, Document> matchingDocuments =
166-
ImmutableSortedMap.Builder.emptyMap(DocumentKey.comparator());
167-
168-
try {
169-
List<Task<Document>> result = Tasks.await(Tasks.whenAllSuccess(pendingDeserializations));
170-
for (Task<Document> task : result) {
171-
@Nullable Document doc = task.getResult();
172-
if (doc != null) {
173-
matchingDocuments = matchingDocuments.insert(doc.getKey(), doc);
174-
}
166+
List<Document> result = Util.await(Util.whenAllComplete(pendingTasks));
167+
for (Document doc : result) {
168+
if (doc != null) {
169+
matchingDocuments = matchingDocuments.insert(doc.getKey(), doc);
175170
}
176-
} catch (ExecutionException e) {
177-
throw new RuntimeException(e);
178-
} catch (InterruptedException e) {
179-
Thread.currentThread().interrupt();
180171
}
181-
182172
return matchingDocuments;
183173
}
184174

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: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
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;
24+
import com.google.common.base.Function;
2225
import com.google.firebase.firestore.FieldPath;
2326
import com.google.firebase.firestore.FirebaseFirestoreException;
2427
import com.google.firebase.firestore.FirebaseFirestoreException.Code;
@@ -31,6 +34,7 @@
3134
import java.util.Comparator;
3235
import java.util.List;
3336
import java.util.Random;
37+
import java.util.concurrent.Semaphore;
3438

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

0 commit comments

Comments
 (0)