]> granicus.if.org Git - neomutt/commitdiff
Optimize LMDB's hcache backend.
authorPietro Cerutti <gahr@gahr.ch>
Mon, 19 Sep 2016 15:29:24 +0000 (15:29 +0000)
committerRichard Russon <rich@flatcap.org>
Sat, 24 Sep 2016 09:30:39 +0000 (10:30 +0100)
Thanks to Clemens Lang for his help with the fix.

See: https://dev.mutt.org/trac/ticket/3691
Closes: #77
doc/manual.xml.head
hcache.c

index fcf518891c7980882e680abd78150a39b6122b47..9f9b85816077c0631df9dcae727ae017caeaad5b 100644 (file)
@@ -11005,6 +11005,7 @@ bind index &lt;esc&gt;L limit-current-thread
     <itemizedlist>
     <listitem><para>Pietro Cerutti <email>gahr@gahr.ch</email></para></listitem>
     <listitem><para>Jan-Piet Mens <email>jp@mens.de</email></para></listitem>
+    <listitem><para>Clemens Lang <email>neverpanic@gmail.com</email></para></listitem>
     </itemizedlist>
   </sect2>
 </sect1>
index b42e12ed05ab19e00338defefb3e2a6c9696f46c..870e05417cdb9df61036312300e6a89dcfb1eff9 100644 (file)
--- a/hcache.c
+++ b/hcache.c
@@ -96,6 +96,12 @@ struct header_cache
 static void mutt_hcache_dbt_init(DBT * dbt, void *data, size_t len);
 static void mutt_hcache_dbt_empty_init(DBT * dbt);
 #elif HAVE_LMDB
+enum mdb_txn_mode
+{
+  txn_uninitialized = 0,
+  txn_read = 1 << 0,
+  txn_write = 1 << 1
+};
 struct header_cache
 {
   MDB_env *env;
@@ -103,7 +109,47 @@ struct header_cache
   MDB_dbi db;
   char *folder;
   unsigned int crc;
+  enum mdb_txn_mode txn_mode;
 };
+
+static int mdb_get_r_txn(header_cache_t *h)
+{
+  int rc;
+
+  if (h->txn && (h->txn_mode & (txn_read | txn_write)) > 0)
+    return MDB_SUCCESS;
+
+  if (h->txn)
+    rc = mdb_txn_renew(h->txn);
+  else
+    rc = mdb_txn_begin(h->env, NULL, MDB_RDONLY, &h->txn);
+
+  if (rc == MDB_SUCCESS)
+    h->txn_mode = txn_read;
+
+  return rc;
+}
+
+static int mdb_get_w_txn(header_cache_t *h)
+{
+  int rc;
+
+  if (h->txn && (h->txn_mode == txn_write))
+    return MDB_SUCCESS;
+
+  if (h->txn)
+  {
+    if (h->txn_mode == txn_read)
+      mdb_txn_reset(h->txn);
+    h->txn = NULL;
+  }
+
+  rc = mdb_txn_begin(h->env, NULL, 0, &h->txn);
+  if (rc == MDB_SUCCESS)
+    h->txn_mode = txn_write;
+
+  return rc;
+}
 #endif
 
 typedef union
@@ -799,7 +845,7 @@ mutt_hcache_fetch_raw (header_cache_t *h, const char *filename,
   key.mv_size = ksize;
   data.mv_data = NULL;
   data.mv_size = 0;
-  rc = mdb_txn_renew(h->txn);
+  rc = mdb_get_r_txn(h);
   if (rc != MDB_SUCCESS)
   {
     h->txn = NULL;
@@ -809,20 +855,17 @@ mutt_hcache_fetch_raw (header_cache_t *h, const char *filename,
   rc = mdb_get(h->txn, h->db, &key, &data);
   if (rc == MDB_NOTFOUND)
   {
-    mdb_txn_reset(h->txn);
     return NULL;
   }
   if (rc != MDB_SUCCESS)
   {
     fprintf(stderr, "mdb_get: %s\n", mdb_strerror(rc));
-    mdb_txn_reset(h->txn);
     return NULL;
   }
   /* Caller frees the data we return, so I MUST make a copy of it */
 
   char *d = safe_malloc(data.mv_size);
   memcpy(d, data.mv_data, data.mv_size);
-  mdb_txn_reset(h->txn);
 
   return d;
 
@@ -898,7 +941,6 @@ mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
 #elif HAVE_LMDB
   MDB_val key;
   MDB_val databuf;
-  MDB_txn *txn;
   size_t folderlen;
   int rc;
 #endif
@@ -929,20 +971,21 @@ mutt_hcache_store_raw (header_cache_t* h, const char* filename, void* data,
   key.mv_size = ksize;
   databuf.mv_data = data;
   databuf.mv_size = dlen;
-  rc = mdb_txn_begin(h->env, NULL, 0, &txn);
+  rc = mdb_get_w_txn(h);
   if (rc != MDB_SUCCESS)
   {
     fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc));
     return rc;
   }
-  rc = mdb_put(txn, h->db, &key, &databuf, 0);
+  rc = mdb_put(h->txn, h->db, &key, &databuf, 0);
   if (rc != MDB_SUCCESS)
   {
     fprintf(stderr, "mdb_put: %s\n", mdb_strerror(rc));
-    mdb_txn_abort(txn);
+    mdb_txn_abort(h->txn);
+    h->txn_mode = txn_uninitialized;
+    h->txn = NULL;
     return rc;
   }
-  rc = mdb_txn_commit(txn);
   return rc;
 #else
   strncpy(path, h->folder, sizeof (path));
@@ -1342,7 +1385,7 @@ hcache_open_lmdb (struct header_cache* h, const char* path)
     goto fail_env;
   }
 
-  rc = mdb_txn_begin(h->env, NULL, MDB_RDONLY, &h->txn);
+  rc = mdb_get_r_txn(h);
   if (rc != MDB_SUCCESS)
   {
       fprintf(stderr, "hcache_open_lmdb: mdb_txn_begin: %s", mdb_strerror(rc));
@@ -1357,10 +1400,12 @@ hcache_open_lmdb (struct header_cache* h, const char* path)
   }
 
   mdb_txn_reset(h->txn);
+  h->txn_mode = txn_uninitialized;
   return 0;
 
 fail_dbi:
   mdb_txn_abort(h->txn);
+  h->txn_mode = txn_uninitialized;
   h->txn = NULL;
 
 fail_env:
@@ -1374,6 +1419,13 @@ mutt_hcache_close(header_cache_t *h)
   if (!h)
     return;
 
+  if (h->txn && h->txn_mode == txn_write)
+  {
+    mdb_txn_commit(h->txn);
+    h->txn_mode = txn_uninitialized;
+    h->txn = NULL;
+  }
+
   mdb_env_close(h->env);
   FREE (&h->folder);
   FREE (&h);
@@ -1384,7 +1436,6 @@ mutt_hcache_delete(header_cache_t *h, const char *filename,
                    size_t(*keylen) (const char *fn))
 {
   MDB_val key;
-  MDB_txn *txn;
   int rc;
 
   if (!h)
@@ -1395,24 +1446,25 @@ mutt_hcache_delete(header_cache_t *h, const char *filename,
 
   key.mv_data = (char *)filename;
   key.mv_size = strlen(filename);
-  rc = mdb_txn_begin(h->env, NULL, 0, &txn);
+  rc = mdb_get_w_txn(h);
   if (rc != MDB_SUCCESS)
   {
     fprintf(stderr, "txn_begin: %s\n", mdb_strerror(rc));
     return rc;
   }
-  rc = mdb_del(txn, h->db, &key, NULL);
+  rc = mdb_del(h->txn, h->db, &key, NULL);
   if (rc != MDB_SUCCESS)
   {
     if (rc != MDB_NOTFOUND)
     {
       fprintf(stderr, "mdb_del: %s\n", mdb_strerror(rc));
+      mdb_txn_abort(h->txn);
+      h->txn_mode = txn_uninitialized;
+      h->txn = NULL;
     }
-    mdb_txn_abort(txn);
     return rc;
   }
 
-  mdb_txn_commit(txn);
   return rc;
 }
 #endif