Skip to content

Commit 8baaf6b

Browse files
committed
do backfill during query
1 parent d5b96d9 commit 8baaf6b

File tree

1 file changed

+45
-36
lines changed

1 file changed

+45
-36
lines changed

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

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ final class SQLiteRemoteDocumentCache implements RemoteDocumentCache {
5858
private final LocalSerializer serializer;
5959
private IndexManager indexManager;
6060

61-
private final DocumentTypeBackfills documentTypeBackfills = new DocumentTypeBackfills();
61+
private final DocumentTypeBackfiller documentTypeBackfiller = new DocumentTypeBackfiller();
6262

6363
SQLiteRemoteDocumentCache(SQLitePersistence persistence, LocalSerializer serializer) {
6464
this.db = persistence;
@@ -177,8 +177,8 @@ public Map<DocumentKey, MutableDocument> getAll(Iterable<DocumentKey> documentKe
177177
}
178178
backgroundQueue.drain();
179179

180-
// TODO(dconeybe): schedule the backfill asynchronously.
181-
documentTypeBackfills.backfill(db);
180+
// Backfill any rows with null "document_type" discovered by processRowInBackground().
181+
documentTypeBackfiller.backfill(db);
182182

183183
// Synchronize on `results` to avoid a data race with the background queue.
184184
synchronized (results) {
@@ -278,8 +278,8 @@ private Map<DocumentKey, MutableDocument> getAll(
278278
});
279279
backgroundQueue.drain();
280280

281-
// TODO(dconeybe): schedule the backfill asynchronously.
282-
documentTypeBackfills.backfill(db);
281+
// Backfill any null "document_type" columns discovered by processRowInBackground().
282+
documentTypeBackfiller.backfill(db);
283283

284284
// Synchronize on `results` to avoid a data race with the background queue.
285285
synchronized (results) {
@@ -315,7 +315,7 @@ private void processRowInBackground(
315315
MutableDocument document =
316316
decodeMaybeDocument(rawDocument, readTimeSeconds, readTimeNanos);
317317
if (documentTypeIsNull) {
318-
documentTypeBackfills.add(path, readTimeSeconds, readTimeNanos, document);
318+
documentTypeBackfiller.enqueue(path, readTimeSeconds, readTimeNanos, document);
319319
}
320320
if (filter == null || filter.apply(document)) {
321321
synchronized (results) {
@@ -379,56 +379,62 @@ private MutableDocument decodeMaybeDocument(
379379
*
380380
* @see <a href="https://github.com/firebase/firebase-android-sdk/issues/7295">#7295</a>
381381
*/
382-
private static class DocumentTypeBackfills {
382+
private static class DocumentTypeBackfiller {
383383

384384
private final ConcurrentHashMap<BackfillKey, DocumentType> documentTypeByBackfillKey =
385385
new ConcurrentHashMap<>();
386386

387-
enum BackfillResult {
388-
NO_PENDING_BACKFILLS,
389-
HAS_PENDING_BACKFILLS,
390-
}
391-
392-
void add(String path, int readTimeSeconds, int readTimeNanos, MutableDocument document) {
387+
void enqueue(String path, int readTimeSeconds, int readTimeNanos, MutableDocument document) {
393388
BackfillKey backfillKey = new BackfillKey(path, readTimeSeconds, readTimeNanos);
394389
DocumentType documentType = DocumentType.forMutableDocument(document);
395390
documentTypeByBackfillKey.putIfAbsent(backfillKey, documentType);
396391
}
397392

398-
BackfillResult backfill(SQLitePersistence db) {
399-
// Just return immediately if there are no pending backfills, as a performance optimization.
400-
// This just elides a few allocations (e.g. the ArrayList below) that would otherwise
401-
// needlessly occur.
402-
if (documentTypeByBackfillKey.isEmpty()) {
403-
return BackfillResult.NO_PENDING_BACKFILLS;
393+
void backfill(SQLitePersistence db) {
394+
while (true) {
395+
BackfillSqlInfo backfillSqlInfo = calculateBackfillSql();
396+
if (backfillSqlInfo == null) {
397+
break;
398+
}
399+
android.util.Log.i(
400+
"zzyzx",
401+
"Backfilling document_type for " + backfillSqlInfo.numDocumentsAffected + " documents");
402+
db.execute(backfillSqlInfo.sql, backfillSqlInfo.bindings);
404403
}
404+
}
405405

406-
ArrayList<Object> sqlBindings = new ArrayList<>();
407-
String sql = calculateBackfillSql(sqlBindings);
408-
if (sql != null) {
409-
db.execute(sql, sqlBindings.toArray());
410-
}
406+
private static class BackfillSqlInfo {
407+
final String sql;
408+
final Object[] bindings;
409+
final int numDocumentsAffected;
411410

412-
return documentTypeByBackfillKey.isEmpty()
413-
? BackfillResult.NO_PENDING_BACKFILLS
414-
: BackfillResult.HAS_PENDING_BACKFILLS;
411+
BackfillSqlInfo(String sql, Object[] bindings, int numDocumentsAffected) {
412+
this.sql = sql;
413+
this.bindings = bindings;
414+
this.numDocumentsAffected = numDocumentsAffected;
415+
}
415416
}
416417

417418
@Nullable
418-
String calculateBackfillSql(ArrayList<Object> bindings) {
419+
BackfillSqlInfo calculateBackfillSql() {
420+
if (documentTypeByBackfillKey.isEmpty()) {
421+
return null; // short circuit
422+
}
423+
424+
ArrayList<Object> bindings = new ArrayList<>();
419425
StringBuilder caseClauses = new StringBuilder();
420426
StringBuilder whereClauses = new StringBuilder();
421427

422428
Iterator<BackfillKey> backfillKeys = documentTypeByBackfillKey.keySet().iterator();
423-
boolean backfillsFound = false;
429+
int numDocumentsAffected = 0;
424430
while (backfillKeys.hasNext() && bindings.size() < SQLitePersistence.LongQuery.LIMIT) {
425431
BackfillKey backfillKey = backfillKeys.next();
426432
DocumentType documentType = documentTypeByBackfillKey.remove(backfillKey);
427433
if (documentType == null) {
428434
continue;
429435
}
430436

431-
backfillsFound = true;
437+
numDocumentsAffected++;
432438
bindings.add(backfillKey.path);
433439
int pathBindingNumber = bindings.size();
434440
bindings.add(backfillKey.readTimeSeconds);
@@ -461,14 +467,17 @@ String calculateBackfillSql(ArrayList<Object> bindings) {
461467
.append(')');
462468
}
463469

464-
if (!backfillsFound) {
470+
if (numDocumentsAffected == 0) {
465471
return null;
466472
}
467473

468-
return "UPDATE remote_documents SET document_type = CASE"
469-
+ caseClauses
470-
+ " ELSE NULL END WHERE"
471-
+ whereClauses;
474+
String sql =
475+
"UPDATE remote_documents SET document_type = CASE"
476+
+ caseClauses
477+
+ " ELSE NULL END WHERE"
478+
+ whereClauses;
479+
480+
return new BackfillSqlInfo(sql, bindings.toArray(), numDocumentsAffected);
472481
}
473482

474483
private static class BackfillKey {
@@ -485,7 +494,7 @@ private static class BackfillKey {
485494
@NonNull
486495
@Override
487496
public String toString() {
488-
return "DocumentTypeBackfills.BackfillKey(path="
497+
return "DocumentTypeBackfiller.BackfillKey(path="
489498
+ path
490499
+ ", readTimeSeconds="
491500
+ readTimeSeconds

0 commit comments

Comments
 (0)