tdb: tdb_repack() only when it's worthwhile.
[ira/wip.git] / lib / tdb / common / transaction.c
index 4bc8044219fcd2de06be6576ac25c4695f3fb587..e110a08dbcca91ac580efaf08524510fa5006402 100644 (file)
@@ -138,8 +138,8 @@ struct tdb_transaction {
        /* old file size before transaction */
        tdb_len_t old_map_size;
 
-       /* we should re-pack on commit */
-       bool need_repack;
+       /* did we expand in this transaction */
+       bool expanded;
 };
 
 
@@ -403,7 +403,7 @@ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size,
                return -1;
        }
 
-       tdb->transaction->need_repack = true;
+       tdb->transaction->expanded = true;
 
        return 0;
 }
@@ -983,6 +983,28 @@ _PUBLIC_ int tdb_transaction_prepare_commit(struct tdb_context *tdb)
        return _tdb_transaction_prepare_commit(tdb);
 }
 
+/* A repack is worthwhile if the largest is less than half total free. */
+static bool repack_worthwhile(struct tdb_context *tdb)
+{
+       tdb_off_t ptr;
+       struct tdb_record rec;
+       tdb_len_t total = 0, largest = 0;
+
+       if (tdb_ofs_read(tdb, FREELIST_TOP, &ptr) == -1) {
+               return false;
+       }
+
+       while (ptr != 0 && tdb_rec_free_read(tdb, ptr, &rec) == 0) {
+               total += rec.rec_len;
+               if (rec.rec_len > largest) {
+                       largest = rec.rec_len;
+               }
+               ptr = rec.next;
+       }
+
+       return total > largest * 2;
+}
+
 /*
   commit the current transaction
 */
@@ -990,7 +1012,7 @@ _PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb)
 {
        const struct tdb_methods *methods;
        int i;
-       bool need_repack;
+       bool need_repack = false;
 
        if (tdb->transaction == NULL) {
                TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n"));
@@ -1058,6 +1080,11 @@ _PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb)
                SAFE_FREE(tdb->transaction->blocks[i]);
        } 
 
+       /* Do this before we drop lock or blocks. */
+       if (tdb->transaction->expanded) {
+               need_repack = repack_worthwhile(tdb);
+       }
+
        SAFE_FREE(tdb->transaction->blocks);
        tdb->transaction->num_blocks = 0;
 
@@ -1081,8 +1108,6 @@ _PUBLIC_ int tdb_transaction_commit(struct tdb_context *tdb)
        utime(tdb->name, NULL);
 #endif
 
-       need_repack = tdb->transaction->need_repack;
-
        /* use a transaction cancel to free memory and remove the
           transaction locks */
        _tdb_transaction_cancel(tdb);