mirror of https://github.com/etcd-io/bbolt.git
Add tpage.put() test.
parent
044d7b7893
commit
a942c1d168
|
@ -145,7 +145,6 @@ func TestDBCorruptMeta0(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
// Transaction()
|
// Transaction()
|
||||||
//--------------------------------------
|
//--------------------------------------
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
// Only one read/write transaction can be active for a DB at a time.
|
// Only one read/write transaction can be active for a DB at a time.
|
||||||
type RWTransaction struct {
|
type RWTransaction struct {
|
||||||
Transaction
|
Transaction
|
||||||
mpages map[pgid]*mpage
|
tpages map[pgid]*tpage
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Allocate scratch meta page.
|
// TODO: Allocate scratch meta page.
|
||||||
|
@ -61,7 +61,7 @@ func (t *RWTransaction) CreateBucket(name string) error {
|
||||||
// Insert new node.
|
// Insert new node.
|
||||||
c := t.sys.cursor()
|
c := t.sys.cursor()
|
||||||
c.Goto([]byte(name))
|
c.Goto([]byte(name))
|
||||||
t.mpage(c.page().id).put([]byte(name), buf[:])
|
t.tpage(c.page().id).put([]byte(name), buf[:])
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -104,7 +104,7 @@ func (t *RWTransaction) Put(name string, key []byte, value []byte) error {
|
||||||
// Insert a new node.
|
// Insert a new node.
|
||||||
c := b.cursor()
|
c := b.cursor()
|
||||||
c.Goto(key)
|
c.Goto(key)
|
||||||
t.mpage(c.page().id).put(key, value)
|
t.tpage(c.page().id).put(key, value)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -124,18 +124,18 @@ func (t *RWTransaction) allocate(size int) (*page, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// mpage returns a deserialized leaf page.
|
// tpage returns a deserialized leaf page.
|
||||||
func (t *RWTransaction) mpage(id pgid) *mpage {
|
func (t *RWTransaction) tpage(id pgid) *tpage {
|
||||||
if t.mpages != nil {
|
if t.tpages != nil {
|
||||||
if p := t.mpages[id]; p != nil {
|
if p := t.tpages[id]; p != nil {
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read raw page and deserialize.
|
// Read raw page and deserialize.
|
||||||
p := &mpage{}
|
p := &tpage{}
|
||||||
p.read(t.page(id))
|
p.read(t.page(id))
|
||||||
t.mpages[id] = p
|
t.tpages[id] = p
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package bolt
|
package bolt
|
||||||
|
|
||||||
type mnodes []mnode
|
type tnodes []tnode
|
||||||
|
|
||||||
type mnode struct {
|
type tnode struct {
|
||||||
key []byte
|
key []byte
|
||||||
value []byte
|
value []byte
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,23 +6,25 @@ import (
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// mpage represents a deserialized leaf page.
|
// tpage represents a temporary, in-memory leaf page.
|
||||||
// It is deserialized from an memory-mapped page and is not restricted by page size.
|
// It is deserialized from an memory-mapped page and is not restricted by page size.
|
||||||
type mpage struct {
|
type tpage struct {
|
||||||
nodes mnodes
|
nodes tnodes
|
||||||
}
|
}
|
||||||
|
|
||||||
// allocator is a function that returns a set of contiguous pages.
|
// allocator is a function that returns a set of contiguous pages.
|
||||||
type allocator func(count int) (*page, error)
|
type allocator func(count int) (*page, error)
|
||||||
|
|
||||||
// put inserts or replaces a key on a leaf page.
|
// put inserts or replaces a key on a leaf page.
|
||||||
func (p *mpage) put(key []byte, value []byte) {
|
func (p *tpage) put(key []byte, value []byte) {
|
||||||
// Find insertion index.
|
// Find insertion index.
|
||||||
index := sort.Search(len(p.nodes), func(i int) bool { return bytes.Compare(p.nodes[i].key, key) != -1 })
|
index := sort.Search(len(p.nodes), func(i int) bool { return bytes.Compare(p.nodes[i].key, key) != -1 })
|
||||||
|
|
||||||
// If there is no existing key then add a new node.
|
// If there is no existing key then add a new node.
|
||||||
if len(p.nodes) == 0 || !bytes.Equal(p.nodes[index].key, key) {
|
if index == len(p.nodes) {
|
||||||
p.nodes = append(p.nodes, mnode{})
|
p.nodes = append(p.nodes, tnode{})
|
||||||
|
} else if len(p.nodes) == 0 || !bytes.Equal(p.nodes[index].key, key) {
|
||||||
|
p.nodes = append(p.nodes, tnode{})
|
||||||
copy(p.nodes[index+1:], p.nodes[index:])
|
copy(p.nodes[index+1:], p.nodes[index:])
|
||||||
}
|
}
|
||||||
p.nodes[index].key = key
|
p.nodes[index].key = key
|
||||||
|
@ -30,8 +32,8 @@ func (p *mpage) put(key []byte, value []byte) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// read initializes the node data from an on-disk page.
|
// read initializes the node data from an on-disk page.
|
||||||
func (p *mpage) read(page *page) {
|
func (p *tpage) read(page *page) {
|
||||||
p.nodes = make(mnodes, page.count)
|
p.nodes = make(tnodes, page.count)
|
||||||
lnodes := (*[maxNodesPerPage]lnode)(unsafe.Pointer(&page.ptr))
|
lnodes := (*[maxNodesPerPage]lnode)(unsafe.Pointer(&page.ptr))
|
||||||
for i := 0; i < int(page.count); i++ {
|
for i := 0; i < int(page.count); i++ {
|
||||||
lnode := lnodes[i]
|
lnode := lnodes[i]
|
||||||
|
@ -42,7 +44,7 @@ func (p *mpage) read(page *page) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// write writes the nodes onto one or more leaf pages.
|
// write writes the nodes onto one or more leaf pages.
|
||||||
func (p *mpage) write(pageSize int, allocate allocator) ([]*page, error) {
|
func (p *tpage) write(pageSize int, allocate allocator) ([]*page, error) {
|
||||||
var pages []*page
|
var pages []*page
|
||||||
|
|
||||||
for _, nodes := range p.split(pageSize) {
|
for _, nodes := range p.split(pageSize) {
|
||||||
|
@ -84,10 +86,10 @@ func (p *mpage) write(pageSize int, allocate allocator) ([]*page, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// split divides up the noes in the page into appropriately sized groups.
|
// split divides up the noes in the page into appropriately sized groups.
|
||||||
func (p *mpage) split(pageSize int) []mnodes {
|
func (p *tpage) split(pageSize int) []tnodes {
|
||||||
// If we only have enough nodes for one page then just return the nodes.
|
// If we only have enough nodes for one page then just return the nodes.
|
||||||
if len(p.nodes) <= minKeysPerPage {
|
if len(p.nodes) <= minKeysPerPage {
|
||||||
return []mnodes{p.nodes}
|
return []tnodes{p.nodes}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we're not larger than one page then just return the nodes.
|
// If we're not larger than one page then just return the nodes.
|
||||||
|
@ -96,13 +98,13 @@ func (p *mpage) split(pageSize int) []mnodes {
|
||||||
totalSize += lnodeSize + len(node.key) + len(node.value)
|
totalSize += lnodeSize + len(node.key) + len(node.value)
|
||||||
}
|
}
|
||||||
if totalSize < pageSize {
|
if totalSize < pageSize {
|
||||||
return []mnodes{p.nodes}
|
return []tnodes{p.nodes}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise group into smaller pages and target a given fill size.
|
// Otherwise group into smaller pages and target a given fill size.
|
||||||
var size int
|
var size int
|
||||||
var group mnodes
|
var group tnodes
|
||||||
var groups []mnodes
|
var groups []tnodes
|
||||||
|
|
||||||
// Set fill threshold to 25%.
|
// Set fill threshold to 25%.
|
||||||
threshold := pageSize >> 4
|
threshold := pageSize >> 4
|
||||||
|
@ -113,7 +115,7 @@ func (p *mpage) split(pageSize int) []mnodes {
|
||||||
// TODO(benbjohnson): Don't create a new group for just the last node.
|
// TODO(benbjohnson): Don't create a new group for just the last node.
|
||||||
if group == nil || (len(group) > minKeysPerPage && size+nodeSize > threshold) {
|
if group == nil || (len(group) > minKeysPerPage && size+nodeSize > threshold) {
|
||||||
size = pageHeaderSize
|
size = pageHeaderSize
|
||||||
group = make(mnodes, 0)
|
group = make(tnodes, 0)
|
||||||
groups = append(groups, group)
|
groups = append(groups, group)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
package bolt
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Ensure that a temporary page can insert a key/value.
|
||||||
|
func TestTpagePut(t *testing.T) {
|
||||||
|
p := &tpage{nodes: make(tnodes, 0)}
|
||||||
|
p.put([]byte("baz"), []byte("2"))
|
||||||
|
p.put([]byte("foo"), []byte("0"))
|
||||||
|
p.put([]byte("bar"), []byte("1"))
|
||||||
|
p.put([]byte("foo"), []byte("3"))
|
||||||
|
assert.Equal(t, len(p.nodes), 3)
|
||||||
|
assert.Equal(t, p.nodes[0], tnode{[]byte("bar"), []byte("1")})
|
||||||
|
assert.Equal(t, p.nodes[1], tnode{[]byte("baz"), []byte("2")})
|
||||||
|
assert.Equal(t, p.nodes[2], tnode{[]byte("foo"), []byte("3")})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a temporary page can deserialize from a page.
|
||||||
|
func TestTpageRead(t *testing.T) {
|
||||||
|
t.Skip("pending")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a temporary page can serialize itself.
|
||||||
|
func TestTpageWrite(t *testing.T) {
|
||||||
|
t.Skip("pending")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that a temporary page can split into appropriate subgroups.
|
||||||
|
func TestTpageSplit(t *testing.T) {
|
||||||
|
t.Skip("pending")
|
||||||
|
}
|
Loading…
Reference in New Issue