ipld-eth-server/vendor/github.com/dgraph-io/badger/txn_test.go

846 lines
21 KiB
Go
Raw Normal View History

/*
* Copyright 2017 Dgraph Labs, Inc. and Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package badger
import (
"fmt"
"io/ioutil"
"math/rand"
"os"
"strconv"
"sync"
"testing"
"time"
"gx/ipfs/QmU4emVTYFKnoJ5yK3pPEN9joyEx6U7y892PDx26ZtNxQd/badger/options"
"gx/ipfs/QmU4emVTYFKnoJ5yK3pPEN9joyEx6U7y892PDx26ZtNxQd/badger/y"
"github.com/stretchr/testify/require"
)
func TestTxnSimple(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
txn := db.NewTransaction(true)
for i := 0; i < 10; i++ {
k := []byte(fmt.Sprintf("key=%d", i))
v := []byte(fmt.Sprintf("val=%d", i))
txn.Set(k, v)
}
item, err := txn.Get([]byte("key=8"))
require.NoError(t, err)
require.NoError(t, item.Value(func(val []byte) error {
require.Equal(t, []byte("val=8"), val)
return nil
}))
require.Panics(t, func() { txn.CommitAt(100, nil) })
require.NoError(t, txn.Commit())
})
}
func TestTxnReadAfterWrite(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
var wg sync.WaitGroup
N := 100
wg.Add(N)
for i := 0; i < N; i++ {
go func(i int) {
defer wg.Done()
key := []byte(fmt.Sprintf("key%d", i))
err := db.Update(func(tx *Txn) error {
return tx.Set(key, key)
})
require.NoError(t, err)
err = db.View(func(tx *Txn) error {
item, err := tx.Get(key)
require.NoError(t, err)
val, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, val, key)
return nil
})
require.NoError(t, err)
}(i)
}
wg.Wait()
})
}
func TestTxnCommitAsync(t *testing.T) {
key := func(i int) []byte {
return []byte(fmt.Sprintf("key=%d", i))
}
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
txn := db.NewTransaction(true)
for i := 0; i < 40; i++ {
err := txn.Set(key(i), []byte(strconv.Itoa(100)))
require.NoError(t, err)
}
require.NoError(t, txn.Commit())
txn.Discard()
closer := y.NewCloser(1)
go func() {
defer closer.Done()
for {
select {
case <-closer.HasBeenClosed():
return
default:
}
// Keep checking balance variant
txn := db.NewTransaction(false)
totalBalance := 0
for i := 0; i < 40; i++ {
item, err := txn.Get(key(i))
require.NoError(t, err)
val, err := item.ValueCopy(nil)
require.NoError(t, err)
bal, err := strconv.Atoi(string(val))
require.NoError(t, err)
totalBalance += bal
}
require.Equal(t, totalBalance, 4000)
txn.Discard()
}
}()
var wg sync.WaitGroup
wg.Add(100)
for i := 0; i < 100; i++ {
go func() {
txn := db.NewTransaction(true)
delta := rand.Intn(100)
for i := 0; i < 20; i++ {
err := txn.Set(key(i), []byte(strconv.Itoa(100-delta)))
require.NoError(t, err)
}
for i := 20; i < 40; i++ {
err := txn.Set(key(i), []byte(strconv.Itoa(100+delta)))
require.NoError(t, err)
}
// We are only doing writes, so there won't be any conflicts.
txn.CommitWith(func(err error) {})
txn.Discard()
wg.Done()
}()
}
wg.Wait()
closer.SignalAndWait()
time.Sleep(time.Millisecond * 10) // allow goroutine to complete.
})
}
func TestTxnVersions(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
k := []byte("key")
for i := 1; i < 10; i++ {
txn := db.NewTransaction(true)
txn.Set(k, []byte(fmt.Sprintf("valversion=%d", i)))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(i), db.orc.readTs())
}
checkIterator := func(itr *Iterator, i int) {
defer itr.Close()
count := 0
for itr.Rewind(); itr.Valid(); itr.Next() {
item := itr.Item()
require.Equal(t, k, item.Key())
val, err := item.ValueCopy(nil)
require.NoError(t, err)
exp := fmt.Sprintf("valversion=%d", i)
require.Equal(t, exp, string(val), "i=%d", i)
count++
}
require.Equal(t, 1, count, "i=%d", i) // Should only loop once.
}
checkAllVersions := func(itr *Iterator, i int) {
var version uint64
if itr.opt.Reverse {
version = 1
} else {
version = uint64(i)
}
count := 0
for itr.Rewind(); itr.Valid(); itr.Next() {
item := itr.Item()
require.Equal(t, k, item.Key())
require.Equal(t, version, item.Version())
val, err := item.ValueCopy(nil)
require.NoError(t, err)
exp := fmt.Sprintf("valversion=%d", version)
require.Equal(t, exp, string(val), "v=%d", version)
count++
if itr.opt.Reverse {
version++
} else {
version--
}
}
require.Equal(t, i, count, "i=%d", i) // Should loop as many times as i.
}
for i := 1; i < 10; i++ {
txn := db.NewTransaction(true)
txn.readTs = uint64(i) // Read version at i.
item, err := txn.Get(k)
require.NoError(t, err)
val, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, []byte(fmt.Sprintf("valversion=%d", i)), val,
"Expected versions to match up at i=%d", i)
// Try retrieving the latest version forward and reverse.
itr := txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, i)
opt := DefaultIteratorOptions
opt.Reverse = true
itr = txn.NewIterator(opt)
checkIterator(itr, i)
// Now try retrieving all versions forward and reverse.
opt = DefaultIteratorOptions
opt.AllVersions = true
itr = txn.NewIterator(opt)
checkAllVersions(itr, i)
itr.Close()
opt = DefaultIteratorOptions
opt.AllVersions = true
opt.Reverse = true
itr = txn.NewIterator(opt)
checkAllVersions(itr, i)
itr.Close()
txn.Discard()
}
txn := db.NewTransaction(true)
defer txn.Discard()
item, err := txn.Get(k)
require.NoError(t, err)
val, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, []byte("valversion=9"), val)
})
}
func TestTxnWriteSkew(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
// Accounts
ax := []byte("x")
ay := []byte("y")
// Set balance to $100 in each account.
txn := db.NewTransaction(true)
defer txn.Discard()
val := []byte(strconv.Itoa(100))
txn.Set(ax, val)
txn.Set(ay, val)
require.NoError(t, txn.Commit())
require.Equal(t, uint64(1), db.orc.readTs())
getBal := func(txn *Txn, key []byte) (bal int) {
item, err := txn.Get(key)
require.NoError(t, err)
val, err := item.ValueCopy(nil)
require.NoError(t, err)
bal, err = strconv.Atoi(string(val))
require.NoError(t, err)
return bal
}
// Start two transactions, each would read both accounts and deduct from one account.
txn1 := db.NewTransaction(true)
sum := getBal(txn1, ax)
sum += getBal(txn1, ay)
require.Equal(t, 200, sum)
txn1.Set(ax, []byte("0")) // Deduct 100 from ax.
// Let's read this back.
sum = getBal(txn1, ax)
require.Equal(t, 0, sum)
sum += getBal(txn1, ay)
require.Equal(t, 100, sum)
// Don't commit yet.
txn2 := db.NewTransaction(true)
sum = getBal(txn2, ax)
sum += getBal(txn2, ay)
require.Equal(t, 200, sum)
txn2.Set(ay, []byte("0")) // Deduct 100 from ay.
// Let's read this back.
sum = getBal(txn2, ax)
require.Equal(t, 100, sum)
sum += getBal(txn2, ay)
require.Equal(t, 100, sum)
// Commit both now.
require.NoError(t, txn1.Commit())
require.Error(t, txn2.Commit()) // This should fail.
require.Equal(t, uint64(2), db.orc.readTs())
})
}
// a3, a2, b4 (del), b3, c2, c1
// Read at ts=4 -> a3, c2
// Read at ts=4(Uncommitted) -> a3, b4
// Read at ts=3 -> a3, b3, c2
// Read at ts=2 -> a2, c2
// Read at ts=1 -> c1
func TestTxnIterationEdgeCase(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
ka := []byte("a")
kb := []byte("b")
kc := []byte("c")
// c1
txn := db.NewTransaction(true)
txn.Set(kc, []byte("c1"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(1), db.orc.readTs())
// a2, c2
txn = db.NewTransaction(true)
txn.Set(ka, []byte("a2"))
txn.Set(kc, []byte("c2"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(2), db.orc.readTs())
// b3
txn = db.NewTransaction(true)
txn.Set(ka, []byte("a3"))
txn.Set(kb, []byte("b3"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(3), db.orc.readTs())
// b4, c4(del) (Uncommitted)
txn4 := db.NewTransaction(true)
require.NoError(t, txn4.Set(kb, []byte("b4")))
require.NoError(t, txn4.Delete(kc))
require.Equal(t, uint64(3), db.orc.readTs())
// b4 (del)
txn = db.NewTransaction(true)
txn.Delete(kb)
require.NoError(t, txn.Commit())
require.Equal(t, uint64(4), db.orc.readTs())
checkIterator := func(itr *Iterator, expected []string) {
defer itr.Close()
var i int
for itr.Rewind(); itr.Valid(); itr.Next() {
item := itr.Item()
val, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, expected[i], string(val), "readts=%d", itr.readTs)
i++
}
require.Equal(t, len(expected), i)
}
txn = db.NewTransaction(true)
defer txn.Discard()
itr := txn.NewIterator(DefaultIteratorOptions)
itr5 := txn4.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"a3", "c2"})
checkIterator(itr5, []string{"a3", "b4"})
rev := DefaultIteratorOptions
rev.Reverse = true
itr = txn.NewIterator(rev)
itr5 = txn4.NewIterator(rev)
checkIterator(itr, []string{"c2", "a3"})
checkIterator(itr5, []string{"b4", "a3"})
txn.readTs = 3
itr = txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"a3", "b3", "c2"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c2", "b3", "a3"})
txn.readTs = 2
itr = txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"a2", "c2"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c2", "a2"})
txn.readTs = 1
itr = txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"c1"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c1"})
})
}
// a2, a3, b4 (del), b3, c2, c1
// Read at ts=4 -> a3, c2
// Read at ts=3 -> a3, b3, c2
// Read at ts=2 -> a2, c2
// Read at ts=1 -> c1
func TestTxnIterationEdgeCase2(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
ka := []byte("a")
kb := []byte("aa")
kc := []byte("aaa")
// c1
txn := db.NewTransaction(true)
txn.Set(kc, []byte("c1"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(1), db.orc.readTs())
// a2, c2
txn = db.NewTransaction(true)
txn.Set(ka, []byte("a2"))
txn.Set(kc, []byte("c2"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(2), db.orc.readTs())
// b3
txn = db.NewTransaction(true)
txn.Set(ka, []byte("a3"))
txn.Set(kb, []byte("b3"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(3), db.orc.readTs())
// b4 (del)
txn = db.NewTransaction(true)
txn.Delete(kb)
require.NoError(t, txn.Commit())
require.Equal(t, uint64(4), db.orc.readTs())
checkIterator := func(itr *Iterator, expected []string) {
defer itr.Close()
var i int
for itr.Rewind(); itr.Valid(); itr.Next() {
item := itr.Item()
val, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, expected[i], string(val), "readts=%d", itr.readTs)
i++
}
require.Equal(t, len(expected), i)
}
txn = db.NewTransaction(true)
defer txn.Discard()
rev := DefaultIteratorOptions
rev.Reverse = true
itr := txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"a3", "c2"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c2", "a3"})
txn.readTs = 5
itr = txn.NewIterator(DefaultIteratorOptions)
itr.Seek(ka)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), ka)
itr.Seek(kc)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Close()
itr = txn.NewIterator(rev)
itr.Seek(ka)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), ka)
itr.Seek(kc)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Close()
txn.readTs = 3
itr = txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"a3", "b3", "c2"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c2", "b3", "a3"})
txn.readTs = 2
itr = txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"a2", "c2"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c2", "a2"})
txn.readTs = 1
itr = txn.NewIterator(DefaultIteratorOptions)
checkIterator(itr, []string{"c1"})
itr = txn.NewIterator(rev)
checkIterator(itr, []string{"c1"})
})
}
func TestTxnIterationEdgeCase3(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
kb := []byte("abc")
kc := []byte("acd")
kd := []byte("ade")
// c1
txn := db.NewTransaction(true)
txn.Set(kc, []byte("c1"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(1), db.orc.readTs())
// b2
txn = db.NewTransaction(true)
txn.Set(kb, []byte("b2"))
require.NoError(t, txn.Commit())
require.Equal(t, uint64(2), db.orc.readTs())
txn2 := db.NewTransaction(true)
require.NoError(t, txn2.Set(kd, []byte("d2")))
require.NoError(t, txn2.Delete(kc))
txn = db.NewTransaction(true)
defer txn.Discard()
rev := DefaultIteratorOptions
rev.Reverse = true
itr := txn.NewIterator(DefaultIteratorOptions)
itr.Seek([]byte("ab"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ac"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Seek(nil)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ac"))
itr.Rewind()
itr.Seek(nil)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ac"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Close()
// Keys: "abc", "ade"
// Read pending writes.
itr = txn2.NewIterator(DefaultIteratorOptions)
itr.Seek([]byte("ab"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ac"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kd)
itr.Seek(nil)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ac"))
itr.Rewind()
itr.Seek(nil)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ad"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kd)
itr.Close()
itr = txn.NewIterator(rev)
itr.Seek([]byte("ac"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ad"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Seek(nil)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Seek([]byte("ac"))
itr.Rewind()
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Seek([]byte("ad"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kc)
itr.Close()
// Keys: "abc", "ade"
itr = txn2.NewIterator(rev)
itr.Seek([]byte("ad"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Seek([]byte("ae"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kd)
itr.Seek(nil)
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kd)
itr.Seek([]byte("ab"))
itr.Rewind()
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kd)
itr.Seek([]byte("ac"))
require.True(t, itr.Valid())
require.Equal(t, itr.item.Key(), kb)
itr.Close()
})
}
func TestIteratorAllVersionsWithDeleted(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
// Write two keys
err := db.Update(func(txn *Txn) error {
txn.Set([]byte("answer1"), []byte("42"))
txn.Set([]byte("answer2"), []byte("43"))
return nil
})
require.NoError(t, err)
// Delete the specific key version from underlying db directly
err = db.View(func(txn *Txn) error {
item, err := txn.Get([]byte("answer1"))
require.NoError(t, err)
err = txn.db.batchSet([]*Entry{
{
Key: y.KeyWithTs(item.key, item.version),
meta: bitDelete,
},
})
require.NoError(t, err)
return err
})
require.NoError(t, err)
opts := DefaultIteratorOptions
opts.AllVersions = true
opts.PrefetchValues = false
// Verify that deleted shows up when AllVersions is set.
err = db.View(func(txn *Txn) error {
it := txn.NewIterator(opts)
defer it.Close()
var count int
for it.Rewind(); it.Valid(); it.Next() {
count++
item := it.Item()
if count == 1 {
require.Equal(t, []byte("answer1"), item.Key())
require.True(t, item.meta&bitDelete > 0)
} else {
require.Equal(t, []byte("answer2"), item.Key())
}
}
require.Equal(t, 2, count)
return nil
})
require.NoError(t, err)
})
}
func TestIteratorAllVersionsWithDeleted2(t *testing.T) {
runBadgerTest(t, nil, func(t *testing.T, db *DB) {
// Set and delete alternatively
for i := 0; i < 4; i++ {
err := db.Update(func(txn *Txn) error {
if i%2 == 0 {
txn.Set([]byte("key"), []byte("value"))
return nil
}
txn.Delete([]byte("key"))
return nil
})
require.NoError(t, err)
}
opts := DefaultIteratorOptions
opts.AllVersions = true
opts.PrefetchValues = false
// Verify that deleted shows up when AllVersions is set.
err := db.View(func(txn *Txn) error {
it := txn.NewIterator(opts)
defer it.Close()
var count int
for it.Rewind(); it.Valid(); it.Next() {
item := it.Item()
require.Equal(t, []byte("key"), item.Key())
if count%2 != 0 {
val, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, val, []byte("value"))
} else {
require.True(t, item.meta&bitDelete > 0)
}
count++
}
require.Equal(t, 4, count)
return nil
})
require.NoError(t, err)
})
}
func TestManagedDB(t *testing.T) {
dir, err := ioutil.TempDir("", "badger")
require.NoError(t, err)
defer os.RemoveAll(dir)
opt := getTestOptions(dir)
opt.managedTxns = true
db, err := Open(opt)
require.NoError(t, err)
defer db.Close()
key := func(i int) []byte {
return []byte(fmt.Sprintf("key-%02d", i))
}
val := func(i int) []byte {
return []byte(fmt.Sprintf("val-%d", i))
}
require.Panics(t, func() {
db.Update(func(tx *Txn) error { return nil })
})
err = db.View(func(tx *Txn) error { return nil })
require.NoError(t, err)
// Write data at t=3.
txn := db.NewTransactionAt(3, true)
for i := 0; i <= 3; i++ {
require.NoError(t, txn.Set(key(i), val(i)))
}
require.Panics(t, func() { txn.Commit() })
require.NoError(t, txn.CommitAt(3, nil))
// Read data at t=2.
txn = db.NewTransactionAt(2, false)
for i := 0; i <= 3; i++ {
_, err := txn.Get(key(i))
require.Equal(t, ErrKeyNotFound, err)
}
txn.Discard()
// Read data at t=3.
txn = db.NewTransactionAt(3, false)
for i := 0; i <= 3; i++ {
item, err := txn.Get(key(i))
require.NoError(t, err)
require.Equal(t, uint64(3), item.Version())
v, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, val(i), v)
}
txn.Discard()
// Write data at t=7.
txn = db.NewTransactionAt(6, true)
for i := 0; i <= 7; i++ {
_, err := txn.Get(key(i))
if err == nil {
continue // Don't overwrite existing keys.
}
require.NoError(t, txn.Set(key(i), val(i)))
}
require.NoError(t, txn.CommitAt(7, nil))
// Read data at t=9.
txn = db.NewTransactionAt(9, false)
for i := 0; i <= 9; i++ {
item, err := txn.Get(key(i))
if i <= 7 {
require.NoError(t, err)
} else {
require.Equal(t, ErrKeyNotFound, err)
}
if i <= 3 {
require.Equal(t, uint64(3), item.Version())
} else if i <= 7 {
require.Equal(t, uint64(7), item.Version())
}
if i <= 7 {
v, err := item.ValueCopy(nil)
require.NoError(t, err)
require.Equal(t, val(i), v)
}
}
txn.Discard()
}
func TestArmV7Issue311Fix(t *testing.T) {
dir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
config := DefaultOptions
config.TableLoadingMode = options.MemoryMap
config.ValueLogFileSize = 16 << 20
config.LevelOneSize = 8 << 20
config.MaxTableSize = 2 << 20
config.Dir = dir
config.ValueDir = dir
config.SyncWrites = false
db, err := Open(config)
if err != nil {
t.Fatalf("cannot open db at location %s: %v", dir, err)
}
err = db.View(func(txn *Txn) error { return nil })
if err != nil {
t.Fatal(err)
}
err = db.Update(func(txn *Txn) error {
return txn.Set([]byte{0x11}, []byte{0x22})
})
if err != nil {
t.Fatal(err)
}
err = db.Update(func(txn *Txn) error {
return txn.Set([]byte{0x11}, []byte{0x22})
})
if err != nil {
t.Fatal(err)
}
if err = db.Close(); err != nil {
t.Fatal(err)
}
}