[geary/wip/only-incomplete: 1/2] Background prefetcher much smoother and doesn't hang intermittently
- From: Jim Nelson <jnelson src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/wip/only-incomplete: 1/2] Background prefetcher much smoother and doesn't hang intermittently
- Date: Wed, 16 Apr 2014 01:39:07 +0000 (UTC)
commit d1abee7f17a44535740d9f96f4d35d9c1ad78004
Author: Jim Nelson <jim yorba org>
Date: Tue Apr 15 17:39:14 2014 -0700
Background prefetcher much smoother and doesn't hang intermittently
src/engine/imap-db/imap-db-folder.vala | 103 ++++++++++++++------
.../imap-engine-account-synchronizer.vala | 2 +-
.../imap-engine/imap-engine-email-prefetcher.vala | 37 +++----
src/engine/util/util-collection.vala | 17 +++
4 files changed, 105 insertions(+), 54 deletions(-)
---
diff --git a/src/engine/imap-db/imap-db-folder.vala b/src/engine/imap-db/imap-db-folder.vala
index 7fa2d04..5d3bd2c 100644
--- a/src/engine/imap-db/imap-db-folder.vala
+++ b/src/engine/imap-db/imap-db-folder.vala
@@ -269,12 +269,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
SELECT MessageLocationTable.message_id, ordering, remove_marker
FROM MessageLocationTable
""");
- if (only_incomplete) {
- sql.append("""
- INNER JOIN MessageTable
- ON MessageTable.id = MessageLocationTable.message_id
- """);
- }
sql.append("WHERE folder_id = ? ");
@@ -283,23 +277,23 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
else
sql.append("AND ordering <= ? ");
- if (only_incomplete)
- sql.append_printf("AND fields != %d ", Geary.Email.Field.ALL);
-
if (oldest_to_newest)
sql.append("ORDER BY ordering ASC ");
else
sql.append("ORDER BY ordering DESC ");
- sql.append("LIMIT ?");
-
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
stmt.bind_int64(1, start_uid.value);
- stmt.bind_int(2, count);
locations = do_results_to_locations(stmt.exec(cancellable), flags, cancellable);
+ if (only_incomplete)
+ do_remove_complete_locations(cx, locations, cancellable);
+
+ if (locations.size > count)
+ locations = locations.slice(0, count);
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
@@ -389,16 +383,7 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
FROM MessageLocationTable
""");
- if (only_incomplete) {
- sql.append("""
- INNER JOIN MessageTable
- ON MessageTable.id = MessageLocationTable.message_id
- """);
- }
-
sql.append("WHERE folder_id = ? AND ordering >= ? AND ordering <= ? ");
- if (only_incomplete)
- sql.append_printf(" AND fields != %d ", Geary.Email.Field.ALL);
Db.Statement stmt = cx.prepare(sql.str);
stmt.bind_rowid(0, folder_id);
@@ -407,6 +392,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
locations = do_results_to_locations(stmt.exec(cancellable), flags, cancellable);
+ if (only_incomplete)
+ do_remove_complete_locations(cx, locations, cancellable);
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
@@ -436,13 +424,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
FROM MessageLocationTable
""");
- if (only_incomplete) {
- sql.append("""
- INNER JOIN MessageTable
- ON MessageTable.id = MessageLocationTable.message_id
- """);
- }
-
if (locs.size != 1) {
sql.append("WHERE ordering IN (");
bool first = true;
@@ -458,9 +439,6 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
sql.append_printf("WHERE ordering = '%s' ", locs[0].uid.to_string());
}
- if (only_incomplete)
- sql.append_printf(" AND fields != %d ", Geary.Email.Field.ALL);
-
sql.append("AND folder_id = ? ");
Db.Statement stmt = cx.prepare(sql.str);
@@ -468,6 +446,9 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
locations = do_results_to_locations(stmt.exec(cancellable), flags, cancellable);
+ if (only_incomplete)
+ do_remove_complete_locations(cx, locations, cancellable);
+
return Db.TransactionOutcome.SUCCESS;
}, cancellable);
@@ -2042,6 +2023,66 @@ private class Geary.ImapDB.Folder : BaseObject, Geary.ReferenceSemantics {
return locations;
}
+ // Use a separate step to strip out complete emails because original implementation (using an
+ // INNER JOIN) was horribly slow under load
+ private void do_remove_complete_locations(Db.Connection cx, Gee.List<LocationIdentifier>? locations,
+ Cancellable? cancellable) throws Error {
+ if (locations == null || locations.size == 0)
+ return;
+
+ StringBuilder sql = new StringBuilder("""
+ SELECT id, fields FROM MessageTable WHERE id IN (
+ """);
+ bool first = true;
+ for (int ctr = 0; ctr < locations.size; ctr++) {
+ if (!first)
+ sql.append(",");
+
+ sql.append(locations[ctr].message_id.to_string());
+ first = false;
+ }
+ sql.append(")");
+
+ Db.Statement stmt = cx.prepare(sql.str);
+ Db.Result results = stmt.exec(cancellable);
+
+ Gee.HashSet<int64?> complete_locations = new Gee.HashSet<int64?>(Collection.int64_hash_func,
+ Collection.int64_equal_func);
+ while (!results.finished) {
+ if (results.int_at(1) == Geary.Email.Field.ALL)
+ complete_locations.add(results.int64_at(0));
+
+ results.next(cancellable);
+ }
+
+ if (complete_locations.size == 0) {
+ debug("No complete locations found (out of %d)", locations.size);
+
+ return;
+ }
+
+ // simple optimization, but worth it
+ if (complete_locations.size == locations.size) {
+ debug("All %d locations, complete, clearing return list", locations.size);
+ locations.clear();
+
+ return;
+ }
+
+ debug("%d complete locations out of %d found, removing...", complete_locations.size,
+ locations.size);
+
+ int original_size = locations.size;
+ Gee.Iterator<LocationIdentifier> iter = locations.iterator();
+ while (iter.next()) {
+ if (complete_locations.contains(iter.get().message_id))
+ iter.remove();
+ }
+
+ debug("Removed, %d -> %d locations remaining", original_size, locations.size);
+ assert(locations.size == (original_size - complete_locations.size));
+ }
+
private LocationIdentifier? do_get_location_for_id(Db.Connection cx, ImapDB.EmailIdentifier id,
ListFlags flags, Cancellable? cancellable) throws Error {
Db.Statement stmt = cx.prepare("""
diff --git a/src/engine/imap-engine/imap-engine-account-synchronizer.vala
b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
index 8d53d50..63a96aa 100644
--- a/src/engine/imap-engine/imap-engine-account-synchronizer.vala
+++ b/src/engine/imap-engine/imap-engine-account-synchronizer.vala
@@ -6,7 +6,7 @@
private class Geary.ImapEngine.AccountSynchronizer : Geary.BaseObject {
private const int FETCH_DATE_RECEIVED_CHUNK_COUNT = 25;
- private const int SYNC_DELAY_SEC = 15;
+ private const int SYNC_DELAY_SEC = 5;
public GenericAccount account { get; private set; }
diff --git a/src/engine/imap-engine/imap-engine-email-prefetcher.vala
b/src/engine/imap-engine/imap-engine-email-prefetcher.vala
index 4fd3a47..1c25ed4 100644
--- a/src/engine/imap-engine/imap-engine-email-prefetcher.vala
+++ b/src/engine/imap-engine/imap-engine-email-prefetcher.vala
@@ -15,7 +15,6 @@ private class Geary.ImapEngine.EmailPrefetcher : Object {
public const int PREFETCH_DELAY_SEC = 1;
private const Geary.Email.Field PREFETCH_FIELDS = Geary.Email.Field.ALL;
- private const int PREFETCH_IDS_CHUNKS = 500;
private const int PREFETCH_CHUNK_BYTES = 32 * 1024;
public Nonblocking.CountingSemaphore active_sem { get; private set;
@@ -80,7 +79,10 @@ private class Geary.ImapEngine.EmailPrefetcher : Object {
}
// emails should include PROPERTIES
- private void schedule_prefetch(Gee.Collection<Geary.Email> emails) {
+ private void schedule_prefetch(Gee.Collection<Geary.Email>? emails) {
+ if (emails == null || emails.size == 0)
+ return;
+
debug("%s: scheduling %d emails for prefetching", folder.to_string(), emails.size);
prefetch_emails.add_all(emails);
@@ -103,26 +105,18 @@ private class Geary.ImapEngine.EmailPrefetcher : Object {
}
private async void do_prepare_all_local_async() {
- Geary.EmailIdentifier? lowest = null;
- for (;;) {
- Gee.List<Geary.Email>? list = null;
- try {
- list = yield folder.local_folder.list_email_by_id_async((ImapDB.EmailIdentifier) lowest,
- PREFETCH_IDS_CHUNKS, Geary.Email.Field.PROPERTIES,
ImapDB.Folder.ListFlags.ONLY_INCOMPLETE,
- cancellable);
- } catch (Error err) {
- debug("Error while list local emails for %s: %s", folder.to_string(), err.message);
- }
-
- if (list == null || list.size == 0)
- break;
-
- // find lowest for next iteration
- lowest = Geary.EmailIdentifier.sort_emails(list).first().id;
-
- schedule_prefetch(list);
+ Gee.List<Geary.Email>? list = null;
+ try {
+ debug("Listing all emails needing prefetching in %s...", folder.to_string());
+ list = yield folder.local_folder.list_email_by_id_async(null, int.MAX,
+ Geary.Email.Field.PROPERTIES, ImapDB.Folder.ListFlags.ONLY_INCOMPLETE, cancellable);
+ debug("Listed all emails needing prefetching in %s", folder.to_string());
+ } catch (Error err) {
+ debug("Error while list local emails for %s: %s", folder.to_string(), err.message);
}
+ schedule_prefetch(list);
+
active_sem.blind_notify();
}
@@ -136,8 +130,7 @@ private class Geary.ImapEngine.EmailPrefetcher : Object {
debug("Error while list local emails for %s: %s", folder.to_string(), err.message);
}
- if (list != null && list.size > 0)
- schedule_prefetch(list);
+ schedule_prefetch(list);
active_sem.blind_notify();
}
diff --git a/src/engine/util/util-collection.vala b/src/engine/util/util-collection.vala
index 7c86c2e..7ec7a65 100644
--- a/src/engine/util/util-collection.vala
+++ b/src/engine/util/util-collection.vala
@@ -126,6 +126,23 @@ public inline static uint int64_hash(int64 value) {
}
/**
+ * To be used as hash_func for Gee collections.
+ */
+public uint int64_hash_func(int64? n) {
+ return hash_memory((uint8 *) n, sizeof(int64));
+}
+
+/**
+ * To be used as equal_func for Gee collections.
+ */
+public bool int64_equal_func(int64? a, int64? b) {
+ int64 *bia = (int64 *) a;
+ int64 *bib = (int64 *) b;
+
+ return (*bia) == (*bib);
+}
+
+/**
* A rotating-XOR hash that can be used to hash memory buffers of any size.
*/
public static uint hash_memory(void *ptr, size_t bytes) {
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]