-
-
Save tidwall/e823bcb75558c2a590cc17227d630ee1 to your computer and use it in GitHub Desktop.
Partial write could corrupt BuntDB database
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"io/ioutil" | |
"os" | |
"path/filepath" | |
"strings" | |
"github.com/tidwall/buntdb" | |
) | |
const VolumeName = "/Volumes/SMALL" // Tiny Volume | |
const VolumeSize = 0x40000000 // 1 GB | |
const ValueSize = 0xA00000 // 10 MB | |
func main() { | |
// Create a very small volume. | |
// I created a 1GB volume at /Volumes/SMALL | |
// Delete the previous "data.db", if exists | |
if err := os.RemoveAll(filepath.Join(VolumeName, "data.db")); err != nil { | |
panic(err) | |
} | |
// create an blank file that is half the size of the volume | |
println("* write blank file") | |
if err := ioutil.WriteFile(filepath.Join(VolumeName, "blank"), | |
make([]byte, VolumeSize/2), 0600); err != nil { | |
panic(err) | |
} | |
// Open the database | |
println("* open database") | |
db, err := buntdb.Open(filepath.Join(VolumeName, "data.db")) | |
if err != nil { | |
panic(err) | |
} | |
// disable background auto shrinking | |
var cfg buntdb.Config | |
if err := db.ReadConfig(&cfg); err != nil { | |
panic(err) | |
} | |
cfg.AutoShrinkDisabled = true | |
if err := db.SetConfig(cfg); err != nil { | |
panic(err) | |
} | |
// create a very big value. One that could not possiblly be written in a | |
// single syscall, but much smaller than the 'blank' file size | |
value := strings.Repeat("*", ValueSize) // 10MB | |
failCount := 0 | |
// start writing transactions to disk | |
println("* write transactions") | |
for i := 0; ; i++ { | |
err := db.Update(func(tx *buntdb.Tx) error { | |
key := fmt.Sprintf("key:%d", i) | |
_, _, err := tx.Set(key, value, nil) | |
return err | |
}) | |
if err != nil { | |
// A failure is probably because the file system ran out of | |
// disk space. It's possible that this was a partial write leaving | |
// the database in a potentially corrupted state. | |
failCount++ | |
if failCount == 2 { | |
println("* fail twice") | |
println("* finished writing") | |
break | |
} | |
println("* fail once") | |
// Let's delete the 'blank' file and continue writing. | |
println("* delete blank file") | |
err := os.RemoveAll(filepath.Join(VolumeName, "blank")) | |
if err != nil { | |
panic(err) | |
} | |
println("* write more transactions") | |
} | |
} | |
println("* close database") | |
if err := db.Close(); err != nil { | |
panic(err) | |
} | |
println("* reopen database") | |
db, err = buntdb.Open(filepath.Join(VolumeName, "data.db")) | |
if err != nil { | |
panic(err) | |
} | |
// disable background auto shrinking | |
if err := db.ReadConfig(&cfg); err != nil { | |
panic(err) | |
} | |
cfg.AutoShrinkDisabled = true | |
if err := db.SetConfig(cfg); err != nil { | |
panic(err) | |
} | |
if err := db.Close(); err != nil { | |
panic(err) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment