off_t size;
MDB_page *dp;
MDB_env *env;
- pgno_t next, freecnt;
+ pgno_t next, freecnt, maxfree_1pg;
txnid_t oldpg_txnid, id;
MDB_cursor mc;
/* Save the freelist as of this transaction to the freeDB. This
* can change the freelist, so keep trying until it stabilizes.
*
- * env->me_pglast and the length of txn->mt_free_pgs cannot decrease.
+ * env->me_pglast and the length of txn->mt_free_pgs cannot decrease,
+ * except the code below can decrease env->me_pglast to split pghead.
* Page numbers cannot disappear from txn->mt_free_pgs. New pages
* can only appear in env->me_pghead when env->me_pglast increases.
* Until then, the me_pghead pointer won't move but can become NULL.
mdb_cursor_init(&mc, txn, FREE_DBI, NULL);
oldpg_txnid = id = 0;
freecnt = 0;
+ /* Preferred max #items per freelist entry, to avoid overflow pages.
+ * Leave room for headers, key (txnid), pagecount (pageno_t), and
+ * FIXME: a bit more in case there is some delimiter I don't know about.
+ */
+ maxfree_1pg = (env->me_psize - (PAGEHDRSZ + NODESIZE + 3*sizeof(MDB_ID)))
+ / sizeof(pgno_t);
/* should only be one record now */
if (env->me_pghead || env->me_pglast) {
/* Put back page numbers we took from freeDB but did not use */
if (env->me_pghead) {
+ for (;;) {
MDB_val key, data;
pgno_t orig, *mop;
i = 2;
do {
orig = mop[0];
- data.mv_size = MDB_IDL_SIZEOF(mop);
+ if (orig > maxfree_1pg && id > 4)
+ orig = maxfree_1pg; /* Do not use an overflow page */
+ data.mv_size = (orig + 1) * sizeof(pgno_t);
rc = mdb_cursor_put(&mc, &key, &data, MDB_RESERVE);
if (rc)
goto fail;
/* mop could have been used again here */
if (id != env->me_pglast || env->me_pghead == NULL)
goto again; /* was completely used up */
- assert(mop == env->me_pghead && mop[0] <= orig);
- } while (mop[0] != orig && --i);
+ assert(mop == env->me_pghead);
+ } while (mop[0] < orig && --i);
memcpy(data.mv_data, mop, data.mv_size);
+ if (mop[0] <= orig)
+ break;
+ *(pgno_t *)data.mv_data = orig;
+ mop[0] -= orig;
+ memmove(&mop[1], &mop[1 + orig],
+ mop[0] * sizeof(pgno_t));
+ /* Save more oldpages at the previous txnid. */
+ assert(env->me_pglast == id && id == oldpg_txnid);
+ env->me_pglast = --oldpg_txnid;
+ }
}
/* Check for growth of freelist again */