From 2321036228b5d24b480e1ad1c20aae65c28e521b Mon Sep 17 00:00:00 2001 From: Ben Johnson <benbjohnson@yahoo.com> Date: Fri, 6 Jun 2014 17:14:17 -0600 Subject: [PATCH] Fix double free in merge-left rebalance. This commit fixes a bug where deletions that caused merge-left rebalances were updating the parent node which caused a node to "reappear" even after it had been deleted. This was fixed in merge-right rebalances a while ago but merge-left is less frequent so it was missed until now. Many thanks to Jordan Sherer (@jsherer) for finding and reporting the bug. --- bucket_test.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ node.go | 1 - 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/bucket_test.go b/bucket_test.go index bae3941..21ddfca 100644 --- a/bucket_test.go +++ b/bucket_test.go @@ -2,6 +2,7 @@ package bolt import ( "bytes" + "encoding/binary" "errors" "fmt" "math/rand" @@ -189,6 +190,50 @@ func TestBucket_Delete_Large(t *testing.T) { }) } +// Ensure that deleting a large set of keys will work correctly. +// Reported by Jordan Sherer: https://github.com/boltdb/bolt/issues/184 +func TestBucket_Delete_Large2(t *testing.T) { + if testing.Short() { + t.Skip("skipping test in short mode.") + } + + withOpenDB(func(db *DB, path string) { + k := make([]byte, 16) + for i := uint64(0); i < 10000; i++ { + err := db.Update(func(tx *Tx) error { + b, err := tx.CreateBucketIfNotExists([]byte("0")) + if err != nil { + t.Fatalf("bucket error: %s", err) + } + + for j := uint64(0); j < 1000; j++ { + binary.BigEndian.PutUint64(k[:8], i) + binary.BigEndian.PutUint64(k[8:], j) + if err := b.Put(k, nil); err != nil { + t.Fatalf("put error: %s", err) + } + } + + return nil + }) + + if err != nil { + t.Fatalf("update error: %s", err) + } + } + + // Delete all of them in one large transaction + db.Update(func(tx *Tx) error { + b := tx.Bucket([]byte("0")) + c := b.Cursor() + for k, _ := c.First(); k != nil; k, _ = c.Next() { + b.Delete(k) + } + return nil + }) + }) +} + // Ensure that accessing and updating nested buckets is ok across transactions. func TestBucket_Nested(t *testing.T) { withOpenDB(func(db *DB, path string) { diff --git a/node.go b/node.go index f0978ca..1865ffe 100644 --- a/node.go +++ b/node.go @@ -486,7 +486,6 @@ func (n *node) rebalance() { target.inodes = append(target.inodes, n.inodes...) n.parent.del(n.key) n.parent.removeChild(n) - n.parent.put(target.key, target.inodes[0].key, nil, target.pgid, 0) delete(n.bucket.nodes, n.pgid) n.free() }