diff --git a/libraries/liblmdb/mdb.c b/libraries/liblmdb/mdb.c index 99e6872..c6985b0 100644 --- a/libraries/liblmdb/mdb.c +++ b/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_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; imt_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