Fix child txn dirty_room counts in spill/unspill
authorHoward Chu <hyc@symas.com>
Sun, 14 Jul 2013 23:53:04 +0000 (16:53 -0700)
committerHoward Chu <hyc@symas.com>
Sun, 14 Jul 2013 23:53:04 +0000 (16:53 -0700)
Don't count pages twice if they're already accounted in an ancestor txn.

libraries/liblmdb/mdb.c

index 102bd77591d62099c2f5d21e8eb8d5ca840dc195..2f997737f91f8d1139cdf0a9035c4c50817de9d8 100644 (file)
@@ -1478,7 +1478,26 @@ mdb_page_spill(MDB_cursor *m0, MDB_val *key, MDB_val *data)
        mdb_cursorpages_mark(m0, P_DIRTY|P_KEEP);
 
        if (rc == 0) {
-               txn->mt_dirty_room = MDB_IDL_UM_MAX - dl[0].mid;
+               if (txn->mt_parent) {
+                       MDB_txn *tx2;
+                       pgno_t pgno = dl[i].mid;
+                       txn->mt_dirty_room = txn->mt_parent->mt_dirty_room - dl[0].mid;
+                       /* dirty pages that are dirty in an ancestor don't
+                        * count against this txn's dirty_room.
+                        */
+                       for (i=1; i<=dl[0].mid; i++) {
+                               for (tx2 = txn->mt_parent; tx2; tx2 = tx2->mt_parent) {
+                                       j = mdb_mid2l_search(tx2->mt_u.dirty_list, pgno);
+                                       if (j <= tx2->mt_u.dirty_list[0].mid &&
+                                               tx2->mt_u.dirty_list[j].mid == pgno) {
+                                               txn->mt_dirty_room++;
+                                               break;
+                                       }
+                               }
+                       }
+               } else {
+                       txn->mt_dirty_room = MDB_IDL_UM_MAX - dl[0].mid;
+               }
                txn->mt_flags |= MDB_TXN_SPILLS;
        }
        return rc;
@@ -1743,6 +1762,22 @@ mdb_page_unspill(MDB_txn *tx0, MDB_page *mp, MDB_page **ret)
                        }       /* otherwise, if belonging to a parent txn, the
                                 * page remains spilled until child commits
                                 */
+
+                       if (txn->mt_parent) {
+                               MDB_txn *tx2;
+                               /* If this page is also in a parent's dirty list, then
+                                * it's already accounted in dirty_room, and we need to
+                                * cancel out the decrement that mdb_page_dirty does.
+                                */
+                               for (tx2 = txn->mt_parent; tx2; tx2 = tx2->mt_parent) {
+                                       x = mdb_mid2l_search(tx2->mt_u.dirty_list, pgno);
+                                       if (x <= tx2->mt_u.dirty_list[0].mid &&
+                                               tx2->mt_u.dirty_list[x].mid == pgno) {
+                                               txn->mt_dirty_room++;
+                                               break;
+                                       }
+                               }
+                       }
                        mdb_page_dirty(tx0, np);
                        np->mp_flags |= P_DIRTY;
                        *ret = np;