Handle loose pages

Pages that were dirtied and deleted in the same txn should be
reused, instead of consuming freeDB pages.
incre
Howard Chu 11 years ago
parent eebbd22c2f
commit 7cab7b95e2
  1. 75
      libraries/liblmdb/mdb.c

@ -650,6 +650,7 @@ typedef struct MDB_page {
#define P_DIRTY 0x10 /**< dirty page, also set for #P_SUBP pages */ #define P_DIRTY 0x10 /**< dirty page, also set for #P_SUBP pages */
#define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */ #define P_LEAF2 0x20 /**< for #MDB_DUPFIXED records */
#define P_SUBP 0x40 /**< for #MDB_DUPSORT sub-pages */ #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 */ #define P_KEEP 0x8000 /**< leave this page alone during spill */
/** @} */ /** @} */
uint16_t mp_flags; /**< @ref mdb_page */ uint16_t mp_flags; /**< @ref mdb_page */
@ -1021,6 +1022,7 @@ typedef struct MDB_xcursor {
typedef struct MDB_pgstate { typedef struct MDB_pgstate {
pgno_t *mf_pghead; /**< Reclaimed freeDB pages, or NULL before use */ 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 */ 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; } MDB_pgstate;
/** The database environment. */ /** The database environment. */
@ -1057,6 +1059,7 @@ struct MDB_env {
MDB_pgstate me_pgstate; /**< state of old pages from freeDB */ MDB_pgstate me_pgstate; /**< state of old pages from freeDB */
# define me_pglast me_pgstate.mf_pglast # define me_pglast me_pgstate.mf_pglast
# define me_pghead me_pgstate.mf_pghead # 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 */ MDB_page *me_dpages; /**< list of malloc'd blocks for re-use */
/** IDL of pages that became unused in a write txn */ /** IDL of pages that became unused in a write txn */
MDB_IDL me_free_pgs; MDB_IDL me_free_pgs;
@ -1485,7 +1488,6 @@ mdb_page_malloc(MDB_txn *txn, unsigned num)
} }
return ret; return ret;
} }
/** Free a single page. /** Free a single page.
* Saves single pages to a list, for future reuse. * Saves single pages to a list, for future reuse.
* (This is not used for multi-page overflow pages.) * (This is not used for multi-page overflow pages.)
@ -1525,6 +1527,23 @@ mdb_dlist_free(MDB_txn *txn)
dl[0].mid = 0; 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. /** 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] mc A cursor handle for the current operation.
* @param[in] pflags Flags of the pages to update: * @param[in] pflags Flags of the pages to update:
@ -1573,6 +1592,12 @@ mdb_pages_xkeep(MDB_cursor *mc, unsigned pflags, int all)
break; 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) { if (all) {
/* Mark dirty root pages */ /* Mark dirty root pages */
for (i=0; i<txn->mt_numdbs; i++) { 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_op op;
MDB_cursor m2; 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; *mp = NULL;
/* If our dirty list is already full, we can't do anything */ /* If our dirty list is already full, we can't do anything */
@ -2661,6 +2697,38 @@ mdb_freelist_save(MDB_txn *txn)
return rc; 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) */ /* MDB_RESERVE cancels meminit in ovpage malloc (when no WRITEMAP) */
clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP)) clean_limit = (env->me_flags & (MDB_NOMEMINIT|MDB_WRITEMAP))
? SSIZE_MAX : maxfree_1pg; ? SSIZE_MAX : maxfree_1pg;
@ -7261,10 +7329,7 @@ mdb_page_merge(MDB_cursor *csrc, MDB_cursor *cdst)
} }
csrc->mc_top++; csrc->mc_top++;
rc = mdb_midl_append(&csrc->mc_txn->mt_free_pgs, mdb_page_loose(csrc->mc_txn->mt_env, csrc->mc_pg[csrc->mc_top]);
csrc->mc_pg[csrc->mc_top]->mp_pgno);
if (rc)
return rc;
if (IS_LEAF(csrc->mc_pg[csrc->mc_top])) if (IS_LEAF(csrc->mc_pg[csrc->mc_top]))
csrc->mc_db->md_leaf_pages--; csrc->mc_db->md_leaf_pages--;
else else

Loading…
Cancel
Save