[geary/wip/search-fixes: 5/10] Execute ConversationMonitor ops in batches to avoid UI stalling



commit ce51b74e92d0dc897007d607bf359e317caf41c9
Author: Michael Gratton <mike vee net>
Date:   Mon Aug 5 18:08:21 2019 +1000

    Execute ConversationMonitor ops in batches to avoid UI stalling
    
    When search results are updated in response to a serch query changing,
    there can be both very large (up to 1000, the current hard-coded limit)
    email and hence converstion inserts and removals in the search folder.
    
    This splits up these large ops that execute multiple times in batches
    (currently n=20), so that the main thread has a chance to breathe while
    handling the updates.

 .../conversation-monitor/app-append-operation.vala | 22 +++++------
 .../app-conversation-operation.vala                | 43 +++++++++++++++++++++-
 .../app-external-append-operation.vala             | 22 ++++++-----
 .../app-fill-window-operation.vala                 | 17 +++++++--
 .../conversation-monitor/app-insert-operation.vala | 23 ++++++------
 .../conversation-monitor/app-remove-operation.vala | 28 +++++++-------
 6 files changed, 102 insertions(+), 53 deletions(-)
---
diff --git a/src/engine/app/conversation-monitor/app-append-operation.vala 
b/src/engine/app/conversation-monitor/app-append-operation.vala
index 8507da24..f8960a00 100644
--- a/src/engine/app/conversation-monitor/app-append-operation.vala
+++ b/src/engine/app/conversation-monitor/app-append-operation.vala
@@ -1,27 +1,25 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
-private class Geary.App.AppendOperation : ConversationOperation {
-
-
-    private Gee.Collection<Geary.EmailIdentifier> appended_ids;
+private class Geary.App.AppendOperation : BatchOperation<EmailIdentifier> {
 
 
     public AppendOperation(ConversationMonitor monitor,
-                           Gee.Collection<Geary.EmailIdentifier> appended_ids) {
-        base(monitor);
-        this.appended_ids = appended_ids;
+                           Gee.Collection<EmailIdentifier> appended_ids) {
+        base(monitor, appended_ids);
     }
 
-    public override async void execute_async() throws Error {
-        debug("%d message(s) appended to %s, fetching to add to conversations...",
-              this.appended_ids.size, this.monitor.base_folder.to_string());
+    public override async void execute_batch(Gee.Collection<EmailIdentifier> batch)
+        throws GLib.Error {
+        debug("Appending %d message(s) to %s",
+              batch.size, this.monitor.base_folder.to_string());
 
-        yield this.monitor.load_by_sparse_id(this.appended_ids);
+        yield this.monitor.load_by_sparse_id(batch);
     }
 
 }
diff --git a/src/engine/app/conversation-monitor/app-conversation-operation.vala 
b/src/engine/app/conversation-monitor/app-conversation-operation.vala
index b0948a6f..118b3484 100644
--- a/src/engine/app/conversation-monitor/app-conversation-operation.vala
+++ b/src/engine/app/conversation-monitor/app-conversation-operation.vala
@@ -1,9 +1,9 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2018 Michael Gratton <mike vee net>
+ * Copyright 2018-2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
 /**
@@ -32,3 +32,42 @@ internal abstract class Geary.App.ConversationOperation : BaseObject {
     public abstract async void execute_async() throws Error;
 
 }
+
+/**
+ * An operation that executes on a collection in batches.
+ */
+internal abstract class Geary.App.BatchOperation<T> : ConversationOperation {
+
+
+    private const int BATCH_MAX_N = 20;
+
+
+    private Gee.Collection<T> full;
+
+
+    protected BatchOperation(ConversationMonitor? monitor,
+                             Gee.Collection<T> full) {
+        base(monitor, true);
+        this.full = full;
+    }
+
+    public override async void execute_async() throws GLib.Error {
+        Gee.Collection<T>? batch = new Gee.LinkedList<T>();
+        foreach (T element in this.full) {
+            batch.add(element);
+
+            if (batch.size == BATCH_MAX_N) {
+                yield execute_batch(batch);
+                batch = new Gee.LinkedList<T>();
+            }
+        }
+
+        if (!batch.is_empty) {
+            yield execute_batch(batch);
+        }
+    }
+
+    public abstract async void execute_batch(Gee.Collection<T> batch)
+        throws GLib.Error;
+
+}
diff --git a/src/engine/app/conversation-monitor/app-external-append-operation.vala 
b/src/engine/app/conversation-monitor/app-external-append-operation.vala
index ee9acaa2..530122e7 100644
--- a/src/engine/app/conversation-monitor/app-external-append-operation.vala
+++ b/src/engine/app/conversation-monitor/app-external-append-operation.vala
@@ -1,32 +1,34 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
-private class Geary.App.ExternalAppendOperation : ConversationOperation {
+private class Geary.App.ExternalAppendOperation : BatchOperation<EmailIdentifier> {
+
 
     private Geary.Folder folder;
-    private Gee.Collection<Geary.EmailIdentifier> appended_ids;
+
 
     public ExternalAppendOperation(ConversationMonitor monitor,
                                    Geary.Folder folder,
-                                   Gee.Collection<Geary.EmailIdentifier> appended_ids) {
-        base(monitor);
+                                   Gee.Collection<EmailIdentifier> appended_ids) {
+        base(monitor, appended_ids);
         this.folder = folder;
-        this.appended_ids = appended_ids;
     }
 
-    public override async void execute_async() throws Error {
+    public override async void execute_batch(Gee.Collection<EmailIdentifier> batch)
+        throws GLib.Error {
         if (!this.monitor.get_search_folder_blacklist().contains(folder.path) &&
             !this.monitor.conversations.is_empty) {
-            debug("%d out of folder message(s) appended to %s, fetching to add to conversations...",
-                  this.appended_ids.size,
+            debug("Appending %d out of folder message(s) to %s",
+                  batch.size,
                   this.folder.to_string());
 
             yield this.monitor.external_load_by_sparse_id(
-                this.folder, this.appended_ids, Geary.Folder.ListFlags.NONE
+                this.folder, batch, Geary.Folder.ListFlags.NONE
             );
         }
     }
diff --git a/src/engine/app/conversation-monitor/app-fill-window-operation.vala 
b/src/engine/app/conversation-monitor/app-fill-window-operation.vala
index 40231bc9..0138ed31 100644
--- a/src/engine/app/conversation-monitor/app-fill-window-operation.vala
+++ b/src/engine/app/conversation-monitor/app-fill-window-operation.vala
@@ -1,9 +1,9 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
- * Copyright 2018 Michael Gratton <mike vee net>
+ * Copyright 2018-2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
 private class Geary.App.FillWindowOperation : ConversationOperation {
@@ -38,14 +38,23 @@ private class Geary.App.FillWindowOperation : ConversationOperation {
         }
 
         debug(
-            "Filling %d messages in %s...",
-            num_to_load, this.monitor.base_folder.to_string()
+            "Filling %d more messages in %s...",
+            num_to_load,
+            this.monitor.base_folder.to_string()
         );
 
         int loaded = yield this.monitor.load_by_id_async(
             this.monitor.window_lowest, num_to_load
         );
 
+        debug(
+            "Filled %d of %d, window: %d, total: %d",
+            loaded, num_to_load,
+            this.monitor.conversations.size,
+            this.monitor.base_folder.properties.email_total
+        );
+
+
         // Check to see if we need any more, but only if there might
         // be some more to load either locally or from the remote. If
         // we loaded the full amount, there might be some more
diff --git a/src/engine/app/conversation-monitor/app-insert-operation.vala 
b/src/engine/app/conversation-monitor/app-insert-operation.vala
index 14f63ebb..64475a86 100644
--- a/src/engine/app/conversation-monitor/app-insert-operation.vala
+++ b/src/engine/app/conversation-monitor/app-insert-operation.vala
@@ -1,42 +1,41 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
 /**
  * Handles an insertion of messages from a monitor's base folder.
  */
-private class Geary.App.InsertOperation : ConversationOperation {
+private class Geary.App.InsertOperation : BatchOperation<EmailIdentifier> {
 
 
-    private Gee.Collection<EmailIdentifier> inserted_ids;
-
     public InsertOperation(ConversationMonitor monitor,
                            Gee.Collection<EmailIdentifier> inserted_ids) {
-        base(monitor);
-        this.inserted_ids = inserted_ids;
+        base(monitor, inserted_ids);
     }
 
-    public override async void execute_async() throws Error {
+    public override async void execute_batch(Gee.Collection<EmailIdentifier> batch)
+        throws GLib.Error {
         Geary.EmailIdentifier? lowest = this.monitor.window_lowest;
         Gee.Collection<EmailIdentifier>? to_insert = null;
         if (lowest != null) {
             to_insert = new Gee.LinkedList<EmailIdentifier>();
-            foreach (EmailIdentifier inserted in this.inserted_ids) {
+            foreach (EmailIdentifier inserted in batch) {
                 if (lowest.natural_sort_comparator(inserted) < 0) {
                     to_insert.add(inserted);
                 }
             }
         } else {
-            to_insert = this.inserted_ids;
+            to_insert = batch;
         }
 
-        debug("Inserting %d messages in %s after %d inserted...",
+        debug("Inserting %d of %d messages into %s",
               to_insert.size,
-              this.monitor.base_folder.to_string(),
-              this.inserted_ids.size);
+              batch.size,
+              this.monitor.base_folder.to_string());
         yield this.monitor.load_by_sparse_id(to_insert);
     }
 }
diff --git a/src/engine/app/conversation-monitor/app-remove-operation.vala 
b/src/engine/app/conversation-monitor/app-remove-operation.vala
index 33cbc6d8..8105d959 100644
--- a/src/engine/app/conversation-monitor/app-remove-operation.vala
+++ b/src/engine/app/conversation-monitor/app-remove-operation.vala
@@ -1,26 +1,28 @@
 /*
  * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 Michael Gratton <mike vee net>
  *
  * This software is licensed under the GNU Lesser General Public License
- * (version 2.1 or later).  See the COPYING file in this distribution.
+ * (version 2.1 or later). See the COPYING file in this distribution.
  */
 
-private class Geary.App.RemoveOperation : ConversationOperation {
+private class Geary.App.RemoveOperation : BatchOperation<EmailIdentifier> {
+
+
+    private Folder source_folder;
 
-    private Geary.Folder source_folder;
-    private Gee.Collection<Geary.EmailIdentifier> removed_ids;
 
     public RemoveOperation(ConversationMonitor monitor,
-                           Geary.Folder source_folder,
-                           Gee.Collection<Geary.EmailIdentifier> removed_ids) {
-        base(monitor);
+                           Folder source_folder,
+                           Gee.Collection<EmailIdentifier> removed_ids) {
+        base(monitor, removed_ids);
         this.source_folder = source_folder;
-        this.removed_ids = removed_ids;
     }
 
-    public override async void execute_async() throws Error {
-        debug("%d messages(s) removed from %s, trimming/removing conversations...",
-              this.removed_ids.size, this.source_folder.to_string()
+    public override async void execute_batch(Gee.Collection<EmailIdentifier> batch)
+        throws GLib.Error {
+        debug("Removing %d messages(s) from %s",
+              batch.size, this.source_folder.to_string()
         );
 
         Gee.Set<Conversation> removed = new Gee.HashSet<Conversation>();
@@ -28,7 +30,7 @@ private class Geary.App.RemoveOperation : ConversationOperation {
             new Gee.HashMultiMap<Conversation, Geary.Email>();
         this.monitor.conversations.remove_all_emails_by_identifier(
             source_folder.path,
-            removed_ids,
+            batch,
             removed,
             trimmed
         );
@@ -38,7 +40,7 @@ private class Geary.App.RemoveOperation : ConversationOperation {
         this.monitor.removed(
             removed,
             trimmed,
-            (this.source_folder == this.monitor.base_folder) ? this.removed_ids : null
+            (this.source_folder == this.monitor.base_folder) ? batch : null
         );
     }
 


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