Skip to content

Commit 92ca51d

Browse files
Make Task API work with tests
1 parent edd0d77 commit 92ca51d

File tree

6 files changed

+52
-37
lines changed

6 files changed

+52
-37
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: 10 additions & 21 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 {
@@ -122,7 +123,7 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
122123
String prefixPath = EncodedPath.encode(prefix);
123124
String prefixSuccessorPath = EncodedPath.prefixSuccessor(prefixPath);
124125

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

127128
db.query("SELECT path, contents FROM remote_documents WHERE path >= ? AND path < ?")
128129
.binding(prefixPath, prefixSuccessorPath)
@@ -142,43 +143,31 @@ public ImmutableSortedMap<DocumentKey, Document> getAllDocumentsMatchingQuery(Qu
142143

143144
byte[] rawContents = row.getBlob(1);
144145

145-
pendingDeserializations.add(
146+
pendingTasks.add(
146147
Tasks.call(
147148
// Since scheduling background tasks incurs overhead, we only dispatch to a
148-
// background
149-
// thread if there are still some documents remaining.
149+
// background thread if there are still some documents remaining.
150150
row.isLast() ? Executors.DIRECT_EXECUTOR : Executors.BACKGROUND_EXECUTOR,
151151
() -> {
152152
MaybeDocument maybeDoc = decodeMaybeDocument(rawContents);
153153
if (!(maybeDoc instanceof Document)) {
154154
return null;
155155
}
156-
157156
if (!query.matches((Document) maybeDoc)) {
158157
return null;
159158
}
160-
161159
return (Document) maybeDoc;
162160
}));
163161
});
164162

165163
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-
}
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
}
176-
} catch (ExecutionException e) {
177-
throw new RuntimeException(e);
178-
} catch (InterruptedException e) {
179-
Thread.currentThread().interrupt();
180170
}
181-
182171
return matchingDocuments;
183172
}
184173

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)