mirror of https://github.com/etcd-io/bbolt.git
Merge pull request #67 from heyitsanthony/fix-freelist-corruption
Fix freelist corruption on tx.WriteTopull/68/head v1.3.1-coreos.5
commit
32c383e75c
60
db_test.go
60
db_test.go
|
@ -9,6 +9,7 @@ import (
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -588,6 +589,65 @@ func TestDB_BeginRW(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestDB_Concurrent_WriteTo checks that issuing WriteTo operations concurrently
|
||||||
|
// with commits does not produce corrupted db files.
|
||||||
|
func TestDB_Concurrent_WriteTo(t *testing.T) {
|
||||||
|
o := &bolt.Options{NoFreelistSync: false}
|
||||||
|
db := MustOpenWithOption(o)
|
||||||
|
defer db.MustClose()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wtxs, rtxs := 5, 5
|
||||||
|
wg.Add(wtxs * rtxs)
|
||||||
|
f := func(tx *bolt.Tx) {
|
||||||
|
defer wg.Done()
|
||||||
|
f, err := ioutil.TempFile("", "bolt-")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(rand.Intn(20)+1) * time.Millisecond)
|
||||||
|
tx.WriteTo(f)
|
||||||
|
tx.Rollback()
|
||||||
|
f.Close()
|
||||||
|
snap := &DB{nil, f.Name(), o}
|
||||||
|
snap.MustReopen()
|
||||||
|
defer snap.MustClose()
|
||||||
|
snap.MustCheck()
|
||||||
|
}
|
||||||
|
|
||||||
|
tx1, err := db.Begin(true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if _, err := tx1.CreateBucket([]byte("abc")); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tx1.Commit(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < wtxs; i++ {
|
||||||
|
tx, err := db.Begin(true)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := tx.Bucket([]byte("abc")).Put([]byte{0}, []byte{0}); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for j := 0; j < rtxs; j++ {
|
||||||
|
rtx, rerr := db.Begin(false)
|
||||||
|
if rerr != nil {
|
||||||
|
t.Fatal(rerr)
|
||||||
|
}
|
||||||
|
go f(rtx)
|
||||||
|
}
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure that opening a transaction while the DB is closed returns an error.
|
// Ensure that opening a transaction while the DB is closed returns an error.
|
||||||
func TestDB_BeginRW_Closed(t *testing.T) {
|
func TestDB_BeginRW_Closed(t *testing.T) {
|
||||||
var db bolt.DB
|
var db bolt.DB
|
||||||
|
|
|
@ -132,9 +132,9 @@ func (f *freelist) free(txid txid, p *page) {
|
||||||
allocTxid, ok := f.allocs[p.id]
|
allocTxid, ok := f.allocs[p.id]
|
||||||
if ok {
|
if ok {
|
||||||
delete(f.allocs, p.id)
|
delete(f.allocs, p.id)
|
||||||
} else if (p.flags & (freelistPageFlag | metaPageFlag)) != 0 {
|
} else if (p.flags & freelistPageFlag) != 0 {
|
||||||
// Safe to claim txid as allocating since these types are private to txid.
|
// Freelist is always allocated by prior tx.
|
||||||
allocTxid = txid
|
allocTxid = txid - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
for id := p.id; id <= p.id+pgid(p.overflow); id++ {
|
for id := p.id; id <= p.id+pgid(p.overflow); id++ {
|
||||||
|
|
Loading…
Reference in New Issue