mirror of https://github.com/etcd-io/bbolt.git
commit
2bfb1b3db5
|
@ -154,6 +154,7 @@ func (b *Bucket) CreateBucket(key []byte) (*Bucket, error) {
|
|||
bucket.root = p.id
|
||||
|
||||
// Insert into node.
|
||||
key = cloneBytes(key)
|
||||
c.node().put(key, key, value, 0, bucketLeafFlag)
|
||||
|
||||
return b.Bucket(key), nil
|
||||
|
@ -262,6 +263,7 @@ func (b *Bucket) Put(key []byte, value []byte) error {
|
|||
}
|
||||
|
||||
// Insert into node.
|
||||
key = cloneBytes(key)
|
||||
c.node().put(key, key, value, 0, 0)
|
||||
|
||||
return nil
|
||||
|
@ -533,3 +535,10 @@ type BucketStats struct {
|
|||
LeafAlloc int // bytes allocated for physical leaf pages
|
||||
LeafInuse int // bytes actually used for leaf data
|
||||
}
|
||||
|
||||
// cloneBytes returns a copy of a given slice.
|
||||
func cloneBytes(v []byte) []byte {
|
||||
var clone = make([]byte, len(v))
|
||||
copy(clone, v)
|
||||
return clone
|
||||
}
|
||||
|
|
|
@ -745,7 +745,7 @@ func ExampleBucket_Put() {
|
|||
// Read value back in a different read-only transaction.
|
||||
db.Update(func(tx *Tx) error {
|
||||
value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
|
||||
fmt.Printf("The value of 'foo' is: %s\n", string(value))
|
||||
fmt.Printf("The value of 'foo' is: %s\n", value)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -770,7 +770,7 @@ func ExampleBucket_Delete() {
|
|||
|
||||
// Retrieve the key back from the database and verify it.
|
||||
value := b.Get([]byte("foo"))
|
||||
fmt.Printf("The value of 'foo' was: %s\n", string(value))
|
||||
fmt.Printf("The value of 'foo' was: %s\n", value)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -809,7 +809,7 @@ func ExampleBucket_ForEach() {
|
|||
|
||||
// Iterate over items in sorted key order.
|
||||
b.ForEach(func(k, v []byte) error {
|
||||
fmt.Printf("A %s is %s.\n", string(k), string(v))
|
||||
fmt.Printf("A %s is %s.\n", k, v)
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
|
|
|
@ -23,6 +23,13 @@ var benchBucketName = []byte("bench")
|
|||
func Bench(options *BenchOptions) {
|
||||
var results BenchResults
|
||||
|
||||
// Validate options.
|
||||
if options.BatchSize == 0 {
|
||||
options.BatchSize = options.Iterations
|
||||
} else if options.Iterations%options.BatchSize != 0 {
|
||||
fatal("number of iterations must be divisible by the batch size")
|
||||
}
|
||||
|
||||
// Find temporary location.
|
||||
path := tempfile()
|
||||
defer os.Remove(path)
|
||||
|
@ -232,9 +239,9 @@ type BenchOptions struct {
|
|||
WriteMode string
|
||||
ReadMode string
|
||||
Iterations int
|
||||
BatchSize int
|
||||
KeySize int
|
||||
ValueSize int
|
||||
BatchSize int
|
||||
CPUProfile string
|
||||
MemProfile string
|
||||
BlockProfile string
|
||||
|
|
|
@ -98,26 +98,22 @@ func NewApp() *cli.App {
|
|||
&cli.StringFlag{Name: "write-mode", Value: "seq", Usage: "Write mode"},
|
||||
&cli.StringFlag{Name: "read-mode", Value: "seq", Usage: "Read mode"},
|
||||
&cli.IntFlag{Name: "count", Value: 1000, Usage: "Item count"},
|
||||
&cli.IntFlag{Name: "batch-size", Usage: "Write batch size"},
|
||||
&cli.IntFlag{Name: "key-size", Value: 8, Usage: "Key size"},
|
||||
&cli.IntFlag{Name: "value-size", Value: 32, Usage: "Value size"},
|
||||
&cli.IntFlag{Name: "batch-size", Value: 0, Usage: "Write batch size"},
|
||||
&cli.StringFlag{Name: "cpuprofile", Usage: "CPU profile output path"},
|
||||
&cli.StringFlag{Name: "memprofile", Usage: "Memory profile output path"},
|
||||
&cli.StringFlag{Name: "blockprofile", Usage: "Block profile output path"},
|
||||
},
|
||||
Action: func(c *cli.Context) {
|
||||
bs := c.Int("batch-size")
|
||||
if bs == 0 {
|
||||
bs = c.Int("count")
|
||||
}
|
||||
Bench(&BenchOptions{
|
||||
ProfileMode: c.String("profile-mode"),
|
||||
WriteMode: c.String("write-mode"),
|
||||
ReadMode: c.String("read-mode"),
|
||||
Iterations: c.Int("count"),
|
||||
BatchSize: c.Int("batch-size"),
|
||||
KeySize: c.Int("key-size"),
|
||||
ValueSize: c.Int("value-size"),
|
||||
BatchSize: bs,
|
||||
CPUProfile: c.String("cpuprofile"),
|
||||
MemProfile: c.String("memprofile"),
|
||||
BlockProfile: c.String("blockprofile"),
|
||||
|
|
|
@ -375,7 +375,7 @@ func ExampleDB_Update() {
|
|||
if err == nil {
|
||||
db.View(func(tx *Tx) error {
|
||||
value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
|
||||
fmt.Printf("The value of 'foo' is: %s\n", string(value))
|
||||
fmt.Printf("The value of 'foo' is: %s\n", value)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -402,7 +402,7 @@ func ExampleDB_View() {
|
|||
// Access data from within a read-only transactional block.
|
||||
db.View(func(tx *Tx) error {
|
||||
v := tx.Bucket([]byte("people")).Get([]byte("john"))
|
||||
fmt.Printf("John's last name is %s.\n", string(v))
|
||||
fmt.Printf("John's last name is %s.\n", v)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
@ -434,7 +434,7 @@ func ExampleDB_Begin_ReadOnly() {
|
|||
tx, _ = db.Begin(false)
|
||||
c := tx.Bucket([]byte("widgets")).Cursor()
|
||||
for k, v := c.First(); k != nil; k, v = c.Next() {
|
||||
fmt.Printf("%s likes %s\n", string(k), string(v))
|
||||
fmt.Printf("%s likes %s\n", k, v)
|
||||
}
|
||||
tx.Rollback()
|
||||
|
||||
|
@ -469,7 +469,7 @@ func ExampleDB_CopyFile() {
|
|||
// Ensure that the key exists in the copy.
|
||||
db2.View(func(tx *Tx) error {
|
||||
value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
|
||||
fmt.Printf("The value for 'foo' in the clone is: %s\n", string(value))
|
||||
fmt.Printf("The value for 'foo' in the clone is: %s\n", value)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
8
tx.go
8
tx.go
|
@ -75,6 +75,14 @@ func (tx *Tx) Writable() bool {
|
|||
return tx.writable
|
||||
}
|
||||
|
||||
// Cursor creates a cursor associated with the root bucket.
|
||||
// All items in the cursor will return a nil value because all root bucket keys point to buckets.
|
||||
// The cursor is only valid as long as the transaction is open.
|
||||
// Do not use a cursor after the transaction is closed.
|
||||
func (tx *Tx) Cursor() *Cursor {
|
||||
return tx.root.Cursor()
|
||||
}
|
||||
|
||||
// Stats retrieves a copy of the current transaction statistics.
|
||||
func (tx *Tx) Stats() TxStats {
|
||||
return tx.stats
|
||||
|
|
27
tx_test.go
27
tx_test.go
|
@ -36,6 +36,31 @@ func TestTx_Commit_ReadOnly(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
// Ensure that a transaction can retrieve a cursor on the root bucket.
|
||||
func TestTx_Cursor(t *testing.T) {
|
||||
withOpenDB(func(db *DB, path string) {
|
||||
db.Update(func(tx *Tx) error {
|
||||
tx.CreateBucket([]byte("widgets"))
|
||||
tx.CreateBucket([]byte("woojits"))
|
||||
c := tx.Cursor()
|
||||
|
||||
k, v := c.First()
|
||||
assert.Equal(t, "widgets", string(k))
|
||||
assert.Nil(t, v)
|
||||
|
||||
k, v = c.Next()
|
||||
assert.Equal(t, "woojits", string(k))
|
||||
assert.Nil(t, v)
|
||||
|
||||
k, v = c.Next()
|
||||
assert.Nil(t, k)
|
||||
assert.Nil(t, v)
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// Ensure that creating a bucket with a read-only transaction returns an error.
|
||||
func TestTx_CreateBucket_ReadOnly(t *testing.T) {
|
||||
withOpenDB(func(db *DB, path string) {
|
||||
|
@ -289,7 +314,7 @@ func ExampleTx_Rollback() {
|
|||
// Ensure that our original value is still set.
|
||||
db.View(func(tx *Tx) error {
|
||||
value := tx.Bucket([]byte("widgets")).Get([]byte("foo"))
|
||||
fmt.Printf("The value for 'foo' is still: %s\n", string(value))
|
||||
fmt.Printf("The value for 'foo' is still: %s\n", value)
|
||||
return nil
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue