]> git.sur5r.net Git - openldap/commitdiff
Handle loose pages
authorHoward Chu <hyc@symas.com>
Sat, 21 Jun 2014 10:30:34 +0000 (03:30 -0700)
committerHoward Chu <hyc@symas.com>
Sat, 21 Jun 2014 10:30:34 +0000 (03:30 -0700)
Pages that were dirtied and deleted in the same txn should be
reused, instead of consuming freeDB pages.

libraries/liblmdb/mdb.c

index 99e68727e07471e74673873e1011186ecc56ed77..c6985b009bbe8beffe201d8650854e0c6b18dc70 100644 (file)
@@ -650,6 +650,7 @@ typedef struct MDB_page {
 #define        P_DIRTY          0x10           /**< dirty page, also set for #P_SUBP pages */
 #define        P_LEAF2          0x20           /**< for #MDB_DUPFIXED records */
 #define        P_SUBP           0x40           /**< for #MDB_DUPSORT sub-pages */
+#define        P_LOOSE          0x4000         /**< page was dirtied then freed, can be reused */
 #define        P_KEEP           0x8000         /**< leave this page alone during spill */
 /** @} */
        uint16_t        mp_flags;               /**< @ref mdb_page */
@@ -1021,6 +1022,7 @@ typedef struct MDB_xcursor {
 typedef struct MDB_pgstate {
        pgno_t          *mf_pghead;     /**< Reclaimed freeDB pages, or NULL before use */
        txnid_t         mf_pglast;      /**< ID of last used record, or 0 if !mf_pghead */
+       MDB_page        *mf_pgloose;    /**< Dirty pages that can be reused */
 } MDB_pgstate;
 
        /** The database environment. */
@@ -1057,6 +1059,7 @@ struct MDB_env {
        MDB_pgstate     me_pgstate;             /**< state of old pages from freeDB */
 #      define          me_pglast       me_pgstate.mf_pglast
 #      define          me_pghead       me_pgstate.mf_pghead
+#      define          me_pgloose      me_pgstate.mf_pgloose
        MDB_page        *me_dpages;             /**< list of malloc'd blocks for re-use */
        /** IDL of pages that became unused in a write txn */
        MDB_IDL         me_free_pgs;
@@ -1485,7 +1488,6 @@ mdb_page_malloc(MDB_txn *txn, unsigned num)
        }
        return ret;
 }
-
 /** Free a single page.
  * Saves single pages to a list, for future reuse.
  * (This is not used for multi-page overflow pages.)
@@ -1525,6 +1527,23 @@ mdb_dlist_free(MDB_txn *txn)
        dl[0].mid = 0;
 }
 
+/** Loosen a single page.
+ * Saves single pages to a list for future reuse
+ * in this same txn. It has been pulled from the freeDB
+ * and already resides on the dirty list, but has been
+ * deleted. Use these pages first before pulling again
+ * from the freeDB.
+ */
+static void
+mdb_page_loose(MDB_env *env, MDB_page *mp)
+{
+               pgno_t *pp = (pgno_t *)mp->mp_ptrs;
+               *pp = mp->mp_pgno;
+               mp->mp_next = env->me_pgloose;
+               env->me_pgloose = mp;
+               mp->mp_flags |= P_LOOSE;
+}
+
 /** Set or clear P_KEEP in dirty, non-overflow, non-sub pages watched by txn.
  * @param[in] mc A cursor handle for the current operation.
  * @param[in] pflags Flags of the pages to update:
@@ -1573,6 +1592,12 @@ mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all)
                        break;
        }
 
+       /* Loose pages shouldn't be spilled */
+       for (dp = txn->mt_env->me_pgloose; dp; dp=dp->mp_next) {
+               if ((dp->mp_flags & Mask) == pflags)
+                       dp->mp_flags ^= P_KEEP;
+       }
+
        if (all) {
                /* Mark dirty root pages */
                for (i=0; i<txn->mt_numdbs; i++) {
@@ -1800,6 +1825,17 @@ mdb_page_alloc(MDB_cursor *mc, int num, MDB_page **mp)
        MDB_cursor_op op;
        MDB_cursor m2;
 
+       /* If there are any loose pages, just use them */
+       if (num == 1 && env->me_pgloose) {
+               pgno_t *pp;
+               np = env->me_pgloose;
+               env->me_pgloose = np->mp_next;
+               pp = (pgno_t *)np->mp_ptrs;
+               np->mp_pgno = *pp;
+               *mp = np;
+               return MDB_SUCCESS;
+       }
+
        *mp = NULL;
 
        /* If our dirty list is already full, we can't do anything */
@@ -2661,6 +2697,38 @@ mdb_freelist_save(MDB_txn *txn)
                        return rc;
        }
 
+       /* Dispose of loose pages. Usually they will have all
+        * been used up by the time we get here.
+        */
+       if (env->me_pgloose) {
+               MDB_page *mp = env->me_pgloose;
+               pgno_t *pp;
+               /* Just return them to freeDB */
+               if (env->me_pghead) {
+                       int i, j;
+                       mop = env->me_pghead;
+                       while(mp) {
+                               pgno_t pg;
+                               pp = (pgno_t *)mp->mp_ptrs;
+                               pg = *pp;
+                               j = mop[0] + 1;
+                               for (i = mop[0]; i && mop[i] < pg; i--)
+                                       mop[j--] = mop[i];
+                               mop[j] = pg;
+                               mop[0] += 1;
+                               mp = mp->mp_next;
+                       }
+               } else {
+               /* Oh well, they were wasted. Put on freelist */
+                       while(mp) {
+                               pp = (pgno_t *)mp->mp_ptrs;
+                               mdb_midl_append(&txn->mt_free_pgs, *pp);
+                               mp = mp->mp_next;
+                       }
+               }
+               env->me_pgloose = NULL;
+       }
+
        /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */
        clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP))
                ? SSIZE_MAX : maxfree_1pg;
@@ -7261,10 +7329,7 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
        }
        csrc->mc_top++;
 
-       rc = mdb_midl_append(&csrc->mc_txn->mt_free_pgs,
-               csrc->mc_pg[csrc->mc_top]->mp_pgno);
-       if (rc)
-               return rc;
+       mdb_page_loose(csrc->mc_txn->mt_env, csrc->mc_pg[csrc->mc_top]);
        if (IS_LEAF(csrc->mc_pg[csrc->mc_top]))
                csrc->mc_db->md_leaf_pages--;
        else