[tracker] libtracker-data: Don't use GPrivate to store TrackerDBInterfaces



commit a367dd08efdc093f84aefdbc5b0a42dc97fd2159
Author: Carlos Garnacho <carlosg gnome org>
Date:   Fri Jul 7 00:41:48 2017 +0200

    libtracker-data: Don't use GPrivate to store TrackerDBInterfaces
    
    The improvement is twofold here. On one hand ensures there's no
    confusions with multiple TrackerDBManagers, because we had no guarantees
    that a thread would be reused across those.
    
    But also we can be much smarter about which TrackerDBInterface we
    hand for operations, giving one that is "free" (i.e. has no other
    active cursors) gives much more reduced mutex contention on stress
    loads.
    
    The maximum number of interfaces has been obtained rather
    unscientifically, seems the turning point at which throughtput does
    not really increment on downright stupid workloads, given the amount
    of readonly threads we allow on the direct connection.

 src/libtracker-data/tracker-db-interface-sqlite.c |    6 +++
 src/libtracker-data/tracker-db-interface.h        |    2 +
 src/libtracker-data/tracker-db-manager.c          |   38 +++++++++++++++++----
 3 files changed, 39 insertions(+), 7 deletions(-)
---
diff --git a/src/libtracker-data/tracker-db-interface-sqlite.c 
b/src/libtracker-data/tracker-db-interface-sqlite.c
index 051f6d8..2b62d70 100644
--- a/src/libtracker-data/tracker-db-interface-sqlite.c
+++ b/src/libtracker-data/tracker-db-interface-sqlite.c
@@ -2949,3 +2949,9 @@ tracker_db_interface_get_user_data (TrackerDBInterface *db_interface)
 {
        return db_interface->user_data;
 }
+
+gboolean
+tracker_db_interface_get_is_used (TrackerDBInterface *db_interface)
+{
+       return g_atomic_int_get (&db_interface->n_active_cursors) > 0;
+}
diff --git a/src/libtracker-data/tracker-db-interface.h b/src/libtracker-data/tracker-db-interface.h
index d5011a0..09e78b5 100644
--- a/src/libtracker-data/tracker-db-interface.h
+++ b/src/libtracker-data/tracker-db-interface.h
@@ -115,6 +115,8 @@ void                    tracker_db_interface_execute_query           (TrackerDBI
 gboolean                tracker_db_interface_start_transaction       (TrackerDBInterface         *interface);
 gboolean                tracker_db_interface_end_db_transaction      (TrackerDBInterface         *interface,
                                                                       GError                    **error);
+gboolean                tracker_db_interface_get_is_used             (TrackerDBInterface         *interface);
+
 void                    tracker_db_statement_bind_double             (TrackerDBStatement         *stmt,
                                                                       int                         index,
                                                                       double                      value);
diff --git a/src/libtracker-data/tracker-db-manager.c b/src/libtracker-data/tracker-db-manager.c
index 2e55ea2..a1d9899 100644
--- a/src/libtracker-data/tracker-db-manager.c
+++ b/src/libtracker-data/tracker-db-manager.c
@@ -51,6 +51,9 @@
 /* ZLib buffer settings */
 #define ZLIB_BUF_SIZE                 8192
 
+#define MAX_INTERFACES_PER_CPU        16
+#define MAX_INTERFACES                (MAX_INTERFACES_PER_CPU * g_get_num_processors ())
+
 /* Required minimum space needed to create databases (5Mb) */
 #define TRACKER_DB_MIN_REQUIRED_SPACE 5242880
 
@@ -132,6 +135,8 @@ struct _TrackerDBManager {
        guint u_cache_size;
 
        GWeakRef iface_data;
+
+       GAsyncQueue *interfaces;
 };
 
 static gboolean            db_exec_no_reply                        (TrackerDBInterface   *iface,
@@ -142,8 +147,6 @@ static TrackerDBInterface *tracker_db_manager_create_db_interface   (TrackerDBMa
                                                                      GError             **error);
 static void                db_remove_locale_file                    (TrackerDBManager    *db_manager);
 
-static GPrivate              interface_data_key = G_PRIVATE_INIT ((GDestroyNotify)g_object_unref);
-
 static gboolean
 db_exec_no_reply (TrackerDBInterface *iface,
                   const gchar        *query,
@@ -606,6 +609,7 @@ tracker_db_manager_new (TrackerDBManagerFlags   flags,
        db_manager->flags = flags;
        db_manager->s_cache_size = select_cache_size;
        db_manager->u_cache_size = update_cache_size;
+       db_manager->interfaces = g_async_queue_new_full (g_object_unref);
 
        g_set_object (&db_manager->cache_location, cache_location);
        g_set_object (&db_manager->data_location, data_location);
@@ -900,7 +904,8 @@ tracker_db_manager_new (TrackerDBManagerFlags   flags,
                }
        }
 
-       g_private_replace (&interface_data_key, resources_iface);
+       tracker_data_manager_init_fts (resources_iface, FALSE);
+       g_async_queue_push (db_manager->interfaces, resources_iface);
 
        return db_manager;
 }
@@ -908,6 +913,7 @@ tracker_db_manager_new (TrackerDBManagerFlags   flags,
 void
 tracker_db_manager_free (TrackerDBManager *db_manager)
 {
+       g_async_queue_unref (db_manager->interfaces);
        g_free (db_manager->db.abs_filename);
        g_clear_object (&db_manager->db.iface);
        g_weak_ref_clear (&db_manager->iface_data);
@@ -1025,12 +1031,28 @@ tracker_db_manager_get_db_interface (TrackerDBManager *db_manager)
        GError *internal_error = NULL;
        TrackerDBInterface *interface;
 
-       interface = g_private_get (&interface_data_key);
+       /* The interfaces never actually leave the async queue,
+        * we use it as a thread synchronized LRU, which doesn't
+        * mean the interface found has no other active cursors,
+        * in which case we either optionally create a new
+        * TrackerDBInterface, or resign to sharing the obtained
+        * one with other threads (thus getting increased contention
+        * in the interface lock).
+        */
+       g_async_queue_lock (db_manager->interfaces);
+       interface = g_async_queue_try_pop_unlocked (db_manager->interfaces);
+
+       if (interface && tracker_db_interface_get_is_used (interface) &&
+           g_async_queue_length_unlocked (db_manager->interfaces) < MAX_INTERFACES) {
+               /* Put it back and go at creating a new one */
+               g_async_queue_push_front_unlocked (db_manager->interfaces, interface);
+               interface = NULL;
+       }
 
-       /* Ensure the interface is there */
        if (!interface) {
                TrackerDBManagerFlags flags;
 
+               /* Create a new one to satisfy the request */
                flags = tracker_db_manager_get_flags (db_manager, NULL, NULL);
                interface = tracker_db_manager_create_db_interface (db_manager,
                                                                    TRUE, &internal_error);
@@ -1038,14 +1060,16 @@ tracker_db_manager_get_db_interface (TrackerDBManager *db_manager)
                if (internal_error) {
                        g_critical ("Error opening database: %s", internal_error->message);
                        g_error_free (internal_error);
+                       g_async_queue_unlock (db_manager->interfaces);
                        return NULL;
                }
 
                tracker_data_manager_init_fts (interface, FALSE);
-
-               g_private_set (&interface_data_key, interface);
        }
 
+       g_async_queue_push_unlocked (db_manager->interfaces, interface);
+       g_async_queue_unlock (db_manager->interfaces);
+
        return interface;
 }
 


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