Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify deallocation list operations #260

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 17 additions & 43 deletions db.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"unsafe"

"github.com/pkg/errors"
"github.com/samber/lo"

"github.com/outofforest/parallel"
"github.com/outofforest/photon"
Expand Down Expand Up @@ -41,12 +42,6 @@ type SpaceToCommit struct {
OriginalPointer types.Pointer
}

// ListToCommit contains cached deallocation list.
type ListToCommit struct {
List *list.List
Root types.NodeAddress
}

// New creates new database.
func New(config Config) (*DB, error) {
snapshotInfoNodeAssistant, err := space.NewDataNodeAssistant[types.SnapshotID, types.SnapshotInfo]()
Expand All @@ -67,7 +62,7 @@ func New(config Config) (*DB, error) {
singularityNode: photon.FromPointer[types.SingularityNode](config.State.Node(0)),
snapshotInfoNodeAssistant: snapshotInfoNodeAssistant,
snapshotToNodeNodeAssistant: snapshotToNodeNodeAssistant,
deallocationListsToCommit: map[types.SnapshotID]*ListToCommit{},
deallocationListsToCommit: map[types.SnapshotID]*types.NodeAddress{},
queue: queue,
queueReader: queueReader,
}
Expand Down Expand Up @@ -111,7 +106,7 @@ type DB struct {
snapshotInfoNodeAssistant *space.DataNodeAssistant[types.SnapshotID, types.SnapshotInfo]
snapshotToNodeNodeAssistant *space.DataNodeAssistant[types.SnapshotID, types.NodeAddress]

deallocationListsToCommit map[types.SnapshotID]*ListToCommit
deallocationListsToCommit map[types.SnapshotID]*types.NodeAddress

queueReader *pipeline.Reader
queue *pipeline.Pipeline
Expand Down Expand Up @@ -381,27 +376,23 @@ func (db *DB) deleteSnapshot(
if err != nil {
return err
}
listNodeAddress, err := deallocationListValue.Value(snapshotID, tx, walRecorder, allocator, deallocationHashBuff,
listRootAddress, err := deallocationListValue.Value(snapshotID, tx, walRecorder, allocator, deallocationHashBuff,
deallocationHashMatches)
if err != nil {
return err
}
list := list.New(list.Config{
Root: listNodeAddress,
State: db.config.State,
})
originalListRootAddress := listRootAddress

listRoot, err := list.Attach(nextDeallocSnapshot.Value, allocator)
if err != nil {
if err := list.Attach(&listRootAddress, nextDeallocSnapshot.Value, db.config.State, allocator); err != nil {
return err
}
if listRoot != listNodeAddress {
if listRootAddress != originalListRootAddress {
if err := deallocationListValue.Set(
snapshotID,
tx,
walRecorder,
allocator,
listRoot,
listRootAddress,
deallocationHashBuff,
deallocationHashMatches,
); err != nil {
Expand Down Expand Up @@ -489,7 +480,7 @@ func (db *DB) commit(
sort.Slice(lists, func(i, j int) bool { return lists[i] < lists[j] })

for _, snapshotID := range lists {
l := db.deallocationListsToCommit[snapshotID]
listRootAddress := db.deallocationListsToCommit[snapshotID]
var deallocationListValue space.Entry[types.SnapshotID, types.NodeAddress]
err := db.deallocationLists.Find(&deallocationListValue, commitSnapshotID, tx, walRecorder, allocator,
snapshotID, space.StageData, deallocationHashBuff, deallocationHashMatches)
Expand All @@ -507,20 +498,17 @@ func (db *DB) commit(
if err != nil {
return err
}
listRoot, err := l.List.Attach(v, allocator)
if err != nil {

if err := list.Attach(listRootAddress, v, db.config.State, allocator); err != nil {
return err
}
if listRoot != l.Root {
l.Root = listRoot
}
}
if err := deallocationListValue.Set(
commitSnapshotID,
tx,
walRecorder,
allocator,
l.Root,
*listRootAddress,
deallocationHashBuff,
deallocationHashMatches,
); err != nil {
Expand Down Expand Up @@ -1085,27 +1073,13 @@ func (db *DB) deallocateNode(
return nil
}

l, exists := db.deallocationListsToCommit[nodeSnapshotID]
if !exists {
l = &ListToCommit{
List: list.New(list.Config{
State: db.config.State,
}),
}
db.deallocationListsToCommit[nodeSnapshotID] = l
listRoot := db.deallocationListsToCommit[nodeSnapshotID]
if listRoot == nil {
listRoot = lo.ToPtr[types.NodeAddress](0)
db.deallocationListsToCommit[nodeSnapshotID] = listRoot
}

listRoot, err := l.List.Add(
nodeAddress,
allocator,
)
if err != nil {
return err
}

l.Root = listRoot

return nil
return list.Add(listRoot, nodeAddress, db.config.State, allocator)
}

func (db *DB) prepareNextSnapshot() error {
Expand Down
102 changes: 46 additions & 56 deletions list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,119 +7,109 @@ import (
"github.com/outofforest/quantum/types"
)

// Config stores list configuration.
type Config struct {
Root types.NodeAddress
State *alloc.State
}

// New creates new list.
func New(config Config) *List {
return &List{
config: config,
}
}

// List represents the list of node addresses.
type List struct {
config Config
}

// Add adds address to the list.
func (l *List) Add(nodeAddress types.NodeAddress, allocator *alloc.Allocator) (types.NodeAddress, error) {
if l.config.Root == 0 {
newNodeAddress, err := allocator.Allocate()
func Add(
listRoot *types.NodeAddress,
nodeAddress types.NodeAddress,
state *alloc.State,
allocator *alloc.Allocator,
) error {
if *listRoot == 0 {
var err error
*listRoot, err = allocator.Allocate()
if err != nil {
return 0, err
return err
}
node := ProjectNode(l.config.State.Node(newNodeAddress))
node := ProjectNode(state.Node(*listRoot))

node.Slots[0] = nodeAddress
node.NumOfPointerAddresses = 1
// This is needed because list nodes are not zeroed.
node.NumOfSideListAddresses = 0

l.config.Root = newNodeAddress

return l.config.Root, nil
return nil
}

node := ProjectNode(l.config.State.Node(l.config.Root))
node := ProjectNode(state.Node(*listRoot))
if node.NumOfPointerAddresses+node.NumOfSideListAddresses < NumOfAddresses {
node.Slots[node.NumOfPointerAddresses] = nodeAddress
node.NumOfPointerAddresses++

return l.config.Root, nil
return nil
}

newNodeAddress, err := allocator.Allocate()
if err != nil {
return 0, err
return err
}
node = ProjectNode(l.config.State.Node(newNodeAddress))
node = ProjectNode(state.Node(newNodeAddress))

node.Slots[0] = nodeAddress
node.Slots[NumOfAddresses-1] = l.config.Root
node.Slots[NumOfAddresses-1] = *listRoot
node.NumOfPointerAddresses = 1
node.NumOfSideListAddresses = 1

l.config.Root = newNodeAddress
*listRoot = newNodeAddress

return l.config.Root, nil
return nil
}

// Attach attaches another list.
func (l *List) Attach(listAddress types.NodeAddress, allocator *alloc.Allocator) (types.NodeAddress, error) {
if l.config.Root == 0 {
newNodeAddress, err := allocator.Allocate()
func Attach(
listRoot *types.NodeAddress,
listAddress types.NodeAddress,
state *alloc.State,
allocator *alloc.Allocator,
) error {
if *listRoot == 0 {
var err error
*listRoot, err = allocator.Allocate()
if err != nil {
return 0, err
return err
}
node := ProjectNode(l.config.State.Node(newNodeAddress))
node := ProjectNode(state.Node(*listRoot))

node.Slots[NumOfAddresses-1] = listAddress
node.NumOfSideListAddresses = 1
// This is needed because list nodes are not zeroed.
node.NumOfPointerAddresses = 0

l.config.Root = newNodeAddress

return l.config.Root, nil
return nil
}

node := ProjectNode(l.config.State.Node(l.config.Root))
node := ProjectNode(state.Node(*listRoot))
if node.NumOfPointerAddresses+node.NumOfSideListAddresses < NumOfAddresses {
node.NumOfSideListAddresses++
node.Slots[NumOfAddresses-node.NumOfSideListAddresses] = listAddress

return l.config.Root, nil
return nil
}

newNodeAddress, err := allocator.Allocate()
if err != nil {
return 0, err
return err
}
node = ProjectNode(l.config.State.Node(newNodeAddress))
node = ProjectNode(state.Node(newNodeAddress))

node.Slots[NumOfAddresses-2] = l.config.Root
node.Slots[NumOfAddresses-2] = *listRoot
node.Slots[NumOfAddresses-1] = listAddress
node.NumOfSideListAddresses = 2
// This is needed because list nodes are not zeroed.
node.NumOfPointerAddresses = 0

l.config.Root = newNodeAddress
*listRoot = newNodeAddress

return l.config.Root, nil
return nil
}

// Iterator iterates over items in the list.
func (l *List) Iterator() func(func(types.NodeAddress) bool) {
func Iterator(listRoot types.NodeAddress, state *alloc.State) func(func(types.NodeAddress) bool) {
return func(yield func(types.NodeAddress) bool) {
if l.config.Root == 0 {
if listRoot == 0 {
return
}

stack := []types.NodeAddress{l.config.Root}
stack := []types.NodeAddress{listRoot}
for {
if len(stack) == 0 {
return
Expand All @@ -128,7 +118,7 @@ func (l *List) Iterator() func(func(types.NodeAddress) bool) {
volatileAddress := stack[len(stack)-1]
stack = stack[:len(stack)-1]

node := ProjectNode(l.config.State.Node(volatileAddress))
node := ProjectNode(state.Node(volatileAddress))
for i := range node.NumOfPointerAddresses {
if !yield(node.Slots[i]) {
return
Expand All @@ -143,13 +133,13 @@ func (l *List) Iterator() func(func(types.NodeAddress) bool) {
}

// Nodes returns list of nodes used by the list.
func (l *List) Nodes() []types.NodeAddress {
if l.config.Root == 0 {
func Nodes(listRoot types.NodeAddress, state *alloc.State) []types.NodeAddress {
if listRoot == 0 {
return nil
}

nodes := []types.NodeAddress{}
stack := []types.NodeAddress{l.config.Root}
stack := []types.NodeAddress{listRoot}

for {
if len(stack) == 0 {
Expand All @@ -164,7 +154,7 @@ func (l *List) Nodes() []types.NodeAddress {
stack = stack[:len(stack)-1]
nodes = append(nodes, volatileAddress)

node := ProjectNode(l.config.State.Node(volatileAddress))
node := ProjectNode(state.Node(volatileAddress))
for i, j := uint16(0), NumOfAddresses-1; i < node.NumOfSideListAddresses; i, j = i+1, j-1 {
stack = append(stack, node.Slots[j])
}
Expand Down