[geary] Avoid fetching email if prior ReplayQueue operation pulled it down



commit cb7ea1bb9958452c032f25be71b566265598c774
Author: Jim Nelson <jim yorba org>
Date:   Wed Mar 4 17:54:41 2015 -0800

    Avoid fetching email if prior ReplayQueue operation pulled it down
    
    A mild optimization, but since it's possible for an operation ahead
    of the current list operation to pull down an email into the database,
    it's worth checking the store once more before going to the network
    for data.

 .../imap-engine-abstract-list-email.vala           |   61 ++++++++++++++++++++
 1 files changed, 61 insertions(+), 0 deletions(-)
---
diff --git a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala 
b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
index f2b0287..7338885 100644
--- a/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
+++ b/src/engine/imap-engine/replay-ops/imap-engine-abstract-list-email.vala
@@ -5,6 +5,8 @@
  */
 
 private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.SendReplayOperation {
+    private static int total_fetches_avoided = 0;
+    
     private class RemoteBatchOperation : Nonblocking.BatchOperation {
         // IN
         public MinimalFolder owner;
@@ -121,6 +123,20 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
         if (unfulfilled.size == 0)
             return ReplayOperation.Status.COMPLETED;
         
+        // since list and search commands ahead of this one in the queue may have fulfilled some of
+        // the emails thought to be unfulfilled when first checked locally, look for them now
+        int fetches_avoided = yield remove_fulfilled_uids_async();
+        if (fetches_avoided > 0) {
+            total_fetches_avoided += fetches_avoided;
+            
+            debug("[%s] %d previously-fulfilled fetches avoided in list operation, %d total",
+                owner.to_string(), fetches_avoided, total_fetches_avoided);
+            
+            // if all fulfilled, emails were added to accumulator in remove call, so done
+            if (unfulfilled.size == 0)
+                return ReplayOperation.Status.COMPLETED;
+        }
+        
         // convert UID -> needed fields mapping to needed fields -> UIDs, as they can be grouped
         // and submitted at same time
         Gee.HashMultiMap<Geary.Email.Field, Imap.UID> reverse_unfulfilled = new Gee.HashMultiMap<
@@ -311,6 +327,51 @@ private abstract class Geary.ImapEngine.AbstractListEmail : Geary.ImapEngine.Sen
         return uids != null && uids.size > 0 ? uids : null;
     }
     
+    private async int remove_fulfilled_uids_async() throws Error {
+        // if the update is forced, don't rely on cached database, have to go to the horse's mouth
+        if (flags.is_force_update())
+            return 0;
+        
+        ImapDB.Folder.ListFlags list_flags = ImapDB.Folder.ListFlags.from_folder_flags(flags);
+        
+        Gee.Set<ImapDB.EmailIdentifier>? unfulfilled_ids = yield owner.local_folder.get_ids_async(
+            unfulfilled.keys, list_flags, cancellable);
+        if (unfulfilled_ids == null || unfulfilled_ids.size == 0)
+            return 0;
+        
+        Gee.Map<ImapDB.EmailIdentifier, Email.Field>? local_fields =
+            yield owner.local_folder.list_email_fields_by_id_async(unfulfilled_ids, list_flags,
+                cancellable);
+        if (local_fields == null || local_fields.size == 0)
+            return 0;
+        
+        // For each identifier, if now fulfilled in the database, fetch it, add it to the accumulator,
+        // and remove it from the unfulfilled map -- one network operation saved
+        int fetch_avoided = 0;
+        foreach (ImapDB.EmailIdentifier id in local_fields.keys) {
+            if (!local_fields.get(id).fulfills(required_fields))
+                continue;
+            
+            try {
+                Email email = yield owner.local_folder.fetch_email_async(id, required_fields, list_flags,
+                    cancellable);
+                accumulator.add(email);
+            } catch (Error err) {
+                if (err is IOError.CANCELLED)
+                    throw err;
+                
+                // some problem locally, do the network round-trip
+                continue;
+            }
+            
+            // got it, don't fetch from remote
+            unfulfilled.unset(id.uid);
+            fetch_avoided++;
+        }
+        
+        return fetch_avoided;
+    }
+    
     public override async void backout_local_async() throws Error {
         // R/O, no backout
     }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]