[evolution-data-server] Bug 595389 - Auto-vacuum Camel DB on expunge



commit 00054cea4b6c3b5e8a05b5c03c8011bd54760cdd
Author: Milan Crha <mcrha redhat com>
Date:   Wed Dec 3 09:10:59 2014 +0100

    Bug 595389 - Auto-vacuum Camel DB on expunge

 camel/camel-db.c     |   55 ++++++++++++++++++++++++++++++++++++++++++++++++++
 camel/camel-db.h     |    2 +
 camel/camel-folder.c |   13 +++++++++++
 camel/camel-store.c  |   38 ++++++++++++++++++++++++++++++++++
 camel/camel-store.h  |    3 ++
 5 files changed, 111 insertions(+), 0 deletions(-)
---
diff --git a/camel/camel-db.c b/camel/camel-db.c
index 7dfea0a..17edffc 100644
--- a/camel/camel-db.c
+++ b/camel/camel-db.c
@@ -2704,3 +2704,58 @@ camel_db_get_column_ident (GHashTable **hash,
 
        return GPOINTER_TO_INT (value);
 }
+
+static gint
+get_number_cb (gpointer data,
+              gint argc,
+              gchar **argv,
+              gchar **azColName)
+{
+       guint64 *pui64 = data;
+
+       if (argc == 1) {
+               *pui64 = argv[0] ? g_ascii_strtoull (argv[0], NULL, 10) : 0;
+       } else {
+               *pui64 = 0;
+       }
+
+       return 0;
+}
+
+/**
+ * camel_db_maybe_run_maintenance:
+ * @cdb: a #CamelDB instance
+ * @error: (allow none): a #GError or %NULL
+ *
+ * Runs a @cdb maintenance, which includes vacuum, if necessary.
+ *
+ * Returns: Whether succeeded.
+ *
+ * Since: 3.14
+ **/
+gboolean
+camel_db_maybe_run_maintenance (CamelDB *cdb,
+                               GError **error)
+{
+       GError *local_error = NULL;
+       guint64 page_count = 0, freelist_count = 0;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (cdb != NULL, FALSE);
+
+       cdb_writer_lock (cdb);
+
+       if (cdb_sql_exec (cdb->db, "PRAGMA page_count;", get_number_cb, &page_count, &local_error) == 
SQLITE_OK &&
+           cdb_sql_exec (cdb->db, "PRAGMA freelist_count;", get_number_cb, &freelist_count, &local_error) == 
SQLITE_OK) {
+               /* Vacuum, if there's more than 5% of the free pages */
+               success = !page_count || !freelist_count || (freelist_count * 1000 / page_count > 50 &&
+                   cdb_sql_exec (cdb->db, "vacuum;", NULL, NULL, &local_error) == SQLITE_OK);
+       }
+
+       cdb_writer_unlock (cdb);
+
+       if (local_error)
+               g_propagate_error (error, local_error);
+
+       return success;
+}
diff --git a/camel/camel-db.h b/camel/camel-db.h
index 4495ba4..ec5e2ea 100644
--- a/camel/camel-db.h
+++ b/camel/camel-db.h
@@ -339,6 +339,8 @@ gint camel_db_write_preview_record (CamelDB *db, const gchar *folder_name, const
 gint
 camel_db_reset_folder_version (CamelDB *cdb, const gchar *folder_name, gint reset_version, GError **error);
 
+gboolean camel_db_maybe_run_maintenance (CamelDB *cdb, GError **error);
+
 G_END_DECLS
 
 #endif
diff --git a/camel/camel-folder.c b/camel/camel-folder.c
index 567ea8a..9772019 100644
--- a/camel/camel-folder.c
+++ b/camel/camel-folder.c
@@ -2823,6 +2823,13 @@ camel_folder_append_message_finish (CamelFolder *folder,
        return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+static gboolean
+camel_folder_maybe_run_db_maintenance (CamelFolder *folder,
+                                      GError **error)
+{
+       return camel_store_maybe_run_db_maintenance (camel_folder_get_parent_store (folder), error);
+}
+
 /**
  * camel_folder_expunge_sync:
  * @folder: a #CamelFolder
@@ -2870,6 +2877,9 @@ camel_folder_expunge_sync (CamelFolder *folder,
        if (!(folder->folder_flags & CAMEL_FOLDER_HAS_BEEN_DELETED)) {
                success = class->expunge_sync (folder, cancellable, error);
                CAMEL_CHECK_GERROR (folder, expunge_sync, success, error);
+
+               if (success)
+                       success = camel_folder_maybe_run_db_maintenance (folder, error);
        }
 
        camel_operation_pop_message (cancellable);
@@ -3621,6 +3631,9 @@ camel_folder_synchronize_sync (CamelFolder *folder,
                success = class->synchronize_sync (
                        folder, expunge, cancellable, error);
                CAMEL_CHECK_GERROR (folder, synchronize_sync, success, error);
+
+               if (success && expunge)
+                       success = camel_folder_maybe_run_db_maintenance (folder, error);
        }
 
        camel_folder_unlock (folder);
diff --git a/camel/camel-store.c b/camel/camel-store.c
index f770de6..ab75a96 100644
--- a/camel/camel-store.c
+++ b/camel/camel-store.c
@@ -55,6 +55,7 @@ typedef struct _SignalClosure SignalClosure;
 struct _CamelStorePrivate {
        GMutex signal_emission_lock;
        gboolean folder_info_stale_scheduled;
+       volatile gint maintenance_lock;
 };
 
 struct _AsyncContext {
@@ -426,6 +427,8 @@ store_synchronize_sync (CamelStore *store,
                /* ensure all folders are used when expunging */
                CamelFolderInfo *root, *fi;
 
+               g_atomic_int_add (&store->priv->maintenance_lock, 1);
+
                folders = g_ptr_array_new ();
                root = camel_store_get_folder_info_sync (
                        store, NULL,
@@ -488,6 +491,14 @@ store_synchronize_sync (CamelStore *store,
                g_object_unref (folder);
        }
 
+       /* Unlock it before the call, thus it's actually done. */
+       if (expunge)
+               g_atomic_int_add (&store->priv->maintenance_lock, -1);
+
+       if (!local_error && expunge) {
+               camel_store_maybe_run_db_maintenance (store, &local_error);
+       }
+
        if (local_error != NULL) {
                g_propagate_error (error, local_error);
                success = FALSE;
@@ -658,6 +669,7 @@ camel_store_init (CamelStore *store)
                CAMEL_STORE_CAN_EDIT_FOLDERS;
 
        store->mode = CAMEL_STORE_READ | CAMEL_STORE_WRITE;
+       store->priv->maintenance_lock = 0;
 }
 
 G_DEFINE_QUARK (camel-store-error-quark, camel_store_error)
@@ -2895,3 +2907,29 @@ camel_store_synchronize_finish (CamelStore *store,
        return g_task_propagate_boolean (G_TASK (result), error);
 }
 
+/**
+ * camel_store_maybe_run_db_maintenance:
+ * @store: a #CamelStore instance
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Checks the state of the current CamelDB used for the @store and eventually
+ * runs maintenance routines on it.
+ *
+ * Returns: Whether succeeded.
+ *
+ * Since: 3.14
+ **/
+gboolean
+camel_store_maybe_run_db_maintenance (CamelStore *store,
+                                     GError **error)
+{
+       g_return_val_if_fail (CAMEL_IS_STORE (store), FALSE);
+
+       if (g_atomic_int_get (&store->priv->maintenance_lock) > 0)
+               return TRUE;
+
+       if (!store->cdb_w)
+               return TRUE;
+
+       return camel_db_maybe_run_maintenance (store->cdb_w, error);
+}
diff --git a/camel/camel-store.h b/camel/camel-store.h
index 9d02a9c..028ce28 100644
--- a/camel/camel-store.h
+++ b/camel/camel-store.h
@@ -355,6 +355,9 @@ void                camel_store_synchronize         (CamelStore *store,
 gboolean       camel_store_synchronize_finish  (CamelStore *store,
                                                 GAsyncResult *result,
                                                 GError **error);
+gboolean       camel_store_maybe_run_db_maintenance
+                                               (CamelStore *store,
+                                                GError **error);
 
 G_END_DECLS
 


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