@@ -58,7 +58,7 @@ final class SQLiteRemoteDocumentCache implements RemoteDocumentCache {
58
58
private final LocalSerializer serializer ;
59
59
private IndexManager indexManager ;
60
60
61
- private final DocumentTypeBackfills documentTypeBackfills = new DocumentTypeBackfills ();
61
+ private final DocumentTypeBackfiller documentTypeBackfiller = new DocumentTypeBackfiller ();
62
62
63
63
SQLiteRemoteDocumentCache (SQLitePersistence persistence , LocalSerializer serializer ) {
64
64
this .db = persistence ;
@@ -177,8 +177,8 @@ public Map<DocumentKey, MutableDocument> getAll(Iterable<DocumentKey> documentKe
177
177
}
178
178
backgroundQueue .drain ();
179
179
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 );
182
182
183
183
// Synchronize on `results` to avoid a data race with the background queue.
184
184
synchronized (results ) {
@@ -278,8 +278,8 @@ private Map<DocumentKey, MutableDocument> getAll(
278
278
});
279
279
backgroundQueue .drain ();
280
280
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 );
283
283
284
284
// Synchronize on `results` to avoid a data race with the background queue.
285
285
synchronized (results ) {
@@ -315,7 +315,7 @@ private void processRowInBackground(
315
315
MutableDocument document =
316
316
decodeMaybeDocument (rawDocument , readTimeSeconds , readTimeNanos );
317
317
if (documentTypeIsNull ) {
318
- documentTypeBackfills . add (path , readTimeSeconds , readTimeNanos , document );
318
+ documentTypeBackfiller . enqueue (path , readTimeSeconds , readTimeNanos , document );
319
319
}
320
320
if (filter == null || filter .apply (document )) {
321
321
synchronized (results ) {
@@ -379,56 +379,62 @@ private MutableDocument decodeMaybeDocument(
379
379
*
380
380
* @see <a href="https://github.com/firebase/firebase-android-sdk/issues/7295">#7295</a>
381
381
*/
382
- private static class DocumentTypeBackfills {
382
+ private static class DocumentTypeBackfiller {
383
383
384
384
private final ConcurrentHashMap <BackfillKey , DocumentType > documentTypeByBackfillKey =
385
385
new ConcurrentHashMap <>();
386
386
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 ) {
393
388
BackfillKey backfillKey = new BackfillKey (path , readTimeSeconds , readTimeNanos );
394
389
DocumentType documentType = DocumentType .forMutableDocument (document );
395
390
documentTypeByBackfillKey .putIfAbsent (backfillKey , documentType );
396
391
}
397
392
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 );
404
403
}
404
+ }
405
405
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 ;
411
410
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
+ }
415
416
}
416
417
417
418
@ 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 <>();
419
425
StringBuilder caseClauses = new StringBuilder ();
420
426
StringBuilder whereClauses = new StringBuilder ();
421
427
422
428
Iterator <BackfillKey > backfillKeys = documentTypeByBackfillKey .keySet ().iterator ();
423
- boolean backfillsFound = false ;
429
+ int numDocumentsAffected = 0 ;
424
430
while (backfillKeys .hasNext () && bindings .size () < SQLitePersistence .LongQuery .LIMIT ) {
425
431
BackfillKey backfillKey = backfillKeys .next ();
426
432
DocumentType documentType = documentTypeByBackfillKey .remove (backfillKey );
427
433
if (documentType == null ) {
428
434
continue ;
429
435
}
430
436
431
- backfillsFound = true ;
437
+ numDocumentsAffected ++ ;
432
438
bindings .add (backfillKey .path );
433
439
int pathBindingNumber = bindings .size ();
434
440
bindings .add (backfillKey .readTimeSeconds );
@@ -461,14 +467,17 @@ String calculateBackfillSql(ArrayList<Object> bindings) {
461
467
.append (')' );
462
468
}
463
469
464
- if (! backfillsFound ) {
470
+ if (numDocumentsAffected == 0 ) {
465
471
return null ;
466
472
}
467
473
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 );
472
481
}
473
482
474
483
private static class BackfillKey {
@@ -485,7 +494,7 @@ private static class BackfillKey {
485
494
@ NonNull
486
495
@ Override
487
496
public String toString () {
488
- return "DocumentTypeBackfills .BackfillKey(path="
497
+ return "DocumentTypeBackfiller .BackfillKey(path="
489
498
+ path
490
499
+ ", readTimeSeconds="
491
500
+ readTimeSeconds
0 commit comments