From 18602636c8d447673ad70365d190e0bcd1839312 Mon Sep 17 00:00:00 2001 From: Wojciech Malota-Wojcik Date: Thu, 3 Oct 2024 15:40:07 +0200 Subject: [PATCH] First approach to deallocation --- allocator.go | 5 ++ list.go | 44 ++++++++--------- snapshot.go | 134 +++++++++++++++++++++++++++++++++++++++++++++------ space.go | 58 +++++++++++++++++++++- types.go | 14 +++--- 5 files changed, 209 insertions(+), 46 deletions(-) diff --git a/allocator.go b/allocator.go index b7b4af8..d68ffbe 100644 --- a/allocator.go +++ b/allocator.go @@ -41,6 +41,11 @@ func (a *Allocator) Allocate() (NodeAddress, []byte) { return a.allocate(a.zeroNode) } +// Deallocate deallocates node. +func (a *Allocator) Deallocate(nodeAddress NodeAddress) { + +} + // Copy allocates new node and copies content from existing one. func (a *Allocator) Copy(data []byte) (NodeAddress, []byte) { return a.allocate(data) diff --git a/list.go b/list.go index 3b22487..69952c9 100644 --- a/list.go +++ b/list.go @@ -5,6 +5,7 @@ type ListConfig struct { SnapshotID SnapshotID Item NodeAddress NodeAllocator ListNodeAllocator + Allocator *Allocator Deallocator Deallocator } @@ -38,7 +39,7 @@ func (l *List) Add(nodeAddress NodeAddress) { newNode.Header.SnapshotID = l.config.SnapshotID oldNodeAddress := l.config.Item l.config.Item = newNodeAddress - l.config.Deallocator.Deallocate(oldNodeAddress, listNode.Header.SnapshotID) + l.config.Allocator.Deallocate(oldNodeAddress) listNode = newNode } @@ -70,7 +71,7 @@ func (l *List) Attach(nodeAddress NodeAddress) { newNode.Header.SnapshotID = l.config.SnapshotID oldNodeAddress := l.config.Item l.config.Item = newNodeAddress - l.config.Deallocator.Deallocate(oldNodeAddress, listNode.Header.SnapshotID) + l.config.Allocator.Deallocate(oldNodeAddress) listNode = newNode } @@ -88,31 +89,28 @@ func (l *List) Attach(nodeAddress NodeAddress) { l.config.Item = newNodeAddress } -// Iterator returns iterator iterating over elements in the list. -func (l *List) Iterator() func(func(NodeAddress) bool) { - return func(yield func(NodeAddress) bool) { - if l.config.Item == 0 { +// Deallocate deallocates nodes referenced by the list. +func (l *List) Deallocate(allocator *Allocator) { + if l.config.Item == 0 { + return + } + + // FIXME (wojciech): Optimize heap allocations. + stack := []NodeAddress{l.config.Item} + for { + if len(stack) == 0 { return } - // FIXME (wojciech): Optimize heap allocations. - stack := []NodeAddress{l.config.Item} - for { - if len(stack) == 0 { - return - } + n := stack[len(stack)-1] + stack = stack[:len(stack)-1] - n := stack[len(stack)-1] - stack = stack[:len(stack)-1] - - _, listNode := l.config.NodeAllocator.Get(n) - for i := range listNode.Header.NumOfItems { - if !yield(listNode.Items[i]) { - return - } - } - - stack = append(stack, listNode.Items[uint64(len(listNode.Items))-listNode.Header.NumOfSideLists:]...) + _, listNode := l.config.NodeAllocator.Get(n) + for i := range listNode.Header.NumOfItems { + allocator.Deallocate(listNode.Items[i]) } + + stack = append(stack, listNode.Items[uint64(len(listNode.Items))-listNode.Header.NumOfSideLists:]...) + allocator.Deallocate(n) } } diff --git a/snapshot.go b/snapshot.go index bddd2ab..a337f2a 100644 --- a/snapshot.go +++ b/snapshot.go @@ -31,7 +31,7 @@ func NewSnapshot(config SnapshotConfig) (Snapshot, error) { singularityNode := *photon.FromBytes[SingularityNode](config.Allocator.Node(0)) if config.SnapshotID > 0 && - (singularityNode.SnapshotRoot.State == stateFree || singularityNode.SnapshotID < config.SnapshotID-1) { + (singularityNode.SnapshotRoot.State == stateFree || singularityNode.LastSnapshotID < config.SnapshotID-1) { return Snapshot{}, errors.Errorf("snapshot %d does not exist", config.SnapshotID) } @@ -74,8 +74,8 @@ func NewSnapshot(config SnapshotConfig) (Snapshot, error) { var snapshotInfo SnapshotInfo if singularityNode.SnapshotRoot.State != stateFree { snapshotID := config.SnapshotID - if singularityNode.SnapshotID == snapshotID-1 { - snapshotID = singularityNode.SnapshotID + if singularityNode.LastSnapshotID == snapshotID-1 { + snapshotID = singularityNode.LastSnapshotID } var exists bool @@ -115,23 +115,27 @@ func NewSnapshot(config SnapshotConfig) (Snapshot, error) { }) s := Snapshot{ - config: config, - snapshots: snapshots, - spaces: spaces, - deallocator: deallocator, - pointerNodeAllocator: pointerNodeAllocator, - spacesToCommit: map[SpaceID]spaceToCommit{}, + config: config, + snapshots: snapshots, + spaces: spaces, + deallocator: deallocator, + pointerNodeAllocator: pointerNodeAllocator, + snapshotToNodeNodeAllocator: snapshotToNodeNodeAllocator, + singularityNode: singularityNode, + spacesToCommit: map[SpaceID]spaceToCommit{}, } return s, nil } // Snapshot represents the state at particular point in time. type Snapshot struct { - config SnapshotConfig - snapshots *Space[SnapshotID, SnapshotInfo] - spaces *Space[SpaceID, SpaceInfo] - deallocator Deallocator - pointerNodeAllocator SpaceNodeAllocator[NodeAddress] + config SnapshotConfig + singularityNode SingularityNode + snapshots *Space[SnapshotID, SnapshotInfo] + spaces *Space[SpaceID, SpaceInfo] + deallocator Deallocator + pointerNodeAllocator SpaceNodeAllocator[NodeAddress] + snapshotToNodeNodeAllocator SpaceNodeAllocator[DataItem[SnapshotID, NodeAddress]] spacesToCommit map[SpaceID]spaceToCommit } @@ -146,6 +150,102 @@ func (s Snapshot) Space(spaceID SpaceID) SpaceInfo { return spaceRootInfo } +func (s Snapshot) DeleteSnapshot(snapshotID SnapshotID) error { + // FIXME (wojciech): Deallocation of last snapshot + + snapshotInfo, exists := s.snapshots.Get(snapshotID) + if !exists { + return errors.Errorf("snapshot %d does not exist", snapshotID) + } + if snapshotInfo.NextSnapshotID <= s.singularityNode.LastSnapshotID { + nextSnapshotInfo, exists := s.snapshots.Get(snapshotInfo.NextSnapshotID) + if !exists { + return errors.Errorf("snapshot %d does not exist", snapshotID) + } + + deallocationLists := NewSpace[SnapshotID, NodeAddress](SpaceConfig[SnapshotID, NodeAddress]{ + SpaceRoot: ParentInfo{ + State: lo.ToPtr(snapshotInfo.DeallocationRoot.State), + Item: lo.ToPtr(snapshotInfo.DeallocationRoot.Node), + }, + }) + + nextDeallocationLists := NewSpace[SnapshotID, NodeAddress](SpaceConfig[SnapshotID, NodeAddress]{ + SnapshotID: snapshotInfo.NextSnapshotID, + HashMod: &nextSnapshotInfo.DeallocationRoot.HashMod, + SpaceRoot: ParentInfo{ + State: lo.ToPtr(nextSnapshotInfo.DeallocationRoot.State), + Item: lo.ToPtr(nextSnapshotInfo.DeallocationRoot.Node), + }, + PointerNodeAllocator: s.pointerNodeAllocator, + DataNodeAllocator: s.snapshotToNodeNodeAllocator, + Deallocator: s.deallocator, + }) + + var startSnapshotID SnapshotID + if snapshotID == s.singularityNode.FirstSnapshotID { + startSnapshotID = snapshotID + } else { + startSnapshotID = snapshotInfo.PreviousSnapshotID + 1 + } + + for sID := startSnapshotID; sID <= snapshotID; sID++ { + listNodeAddress, exists := nextDeallocationLists.Get(sID) + if !exists { + continue + } + + list := NewList(ListConfig{ + Item: listNodeAddress, + }) + list.Deallocate(s.config.Allocator) + nextDeallocationLists.Delete(sID) + } + + // FIXME (wojciech): Iterate over space instead + for sID := s.singularityNode.FirstSnapshotID; sID < snapshotID; sID++ { + listNodeAddress, exists := deallocationLists.Get(sID) + if !exists { + continue + } + + nextListNodeAddress, _ := nextDeallocationLists.Get(sID) + nextList := NewList(ListConfig{ + Item: nextListNodeAddress, + }) + nextList.Attach(listNodeAddress) + if nextList.config.Item != nextListNodeAddress { + nextDeallocationLists.Set(sID, nextList.config.Item) + } + } + + nextSnapshotInfo.PreviousSnapshotID = snapshotInfo.PreviousSnapshotID + nextSnapshotInfo.DeallocationRoot = SpaceInfo{ + State: *nextDeallocationLists.config.SpaceRoot.State, + Node: *nextDeallocationLists.config.SpaceRoot.Item, + HashMod: *nextDeallocationLists.config.HashMod, + } + s.snapshots.Set(snapshotInfo.NextSnapshotID, nextSnapshotInfo) + } + + if snapshotInfo.PreviousSnapshotID > s.singularityNode.FirstSnapshotID { + previousSnapshotInfo, exists := s.snapshots.Get(snapshotInfo.PreviousSnapshotID) + if !exists { + return errors.Errorf("snapshot %d does not exist", snapshotID) + } + previousSnapshotInfo.NextSnapshotID = snapshotInfo.NextSnapshotID + s.snapshots.Set(snapshotInfo.PreviousSnapshotID, previousSnapshotInfo) + } + + if snapshotID == s.singularityNode.FirstSnapshotID { + s.singularityNode.FirstSnapshotID = snapshotInfo.NextSnapshotID + } + + // FIXME (wojciech): Deallocate nodes used by deleted snapshot (DeallocationRoot). + + return nil +} + // Commit commits current snapshot and returns next one. func (s Snapshot) Commit() (Snapshot, error) { spaces := make([]SpaceID, 0, len(s.spacesToCommit)) @@ -169,7 +269,8 @@ func (s Snapshot) Commit() (Snapshot, error) { clear(s.spacesToCommit) s.snapshots.Set(s.config.SnapshotID, SnapshotInfo{ - SnapshotID: s.config.SnapshotID, + PreviousSnapshotID: s.singularityNode.LastSnapshotID, + NextSnapshotID: s.config.SnapshotID + 1, DeallocationRoot: SpaceInfo{ HashMod: *s.deallocator.deallocationLists.config.HashMod, State: *s.deallocator.deallocationLists.config.SpaceRoot.State, @@ -183,7 +284,8 @@ func (s Snapshot) Commit() (Snapshot, error) { }) *photon.FromBytes[SingularityNode](s.config.Allocator.Node(0)) = SingularityNode{ - SnapshotID: s.config.SnapshotID, + FirstSnapshotID: s.singularityNode.FirstSnapshotID, + LastSnapshotID: s.config.SnapshotID, SnapshotRoot: SpaceInfo{ HashMod: *s.snapshots.config.HashMod, State: *s.snapshots.config.SpaceRoot.State, diff --git a/space.go b/space.go index b20e9b1..76b97a3 100644 --- a/space.go +++ b/space.go @@ -31,7 +31,7 @@ type Space[K, V comparable] struct { } // Get gets the value of the key. -func (s *Space[K, V]) Get(key K) (value V, exists bool) { +func (s *Space[K, V]) Get(key K) (V, bool) { h := hashKey(key, 0) pInfo := s.config.SpaceRoot @@ -81,6 +81,62 @@ func (s *Space[K, V]) Set(key K, value V) { }) } +// Delete deletes the key from space. +func (s *Space[K, V]) Delete(key K) { + h := hashKey(key, 0) + pInfo := s.config.SpaceRoot + + for { + switch *pInfo.State { + case stateFree: + return + case stateData: + // FIXME (wojciech): Don't copy the node if split is required. + + dataNodeData, dataNode := s.config.DataNodeAllocator.Get(*pInfo.Item) + if dataNode.Header.SnapshotID < s.config.SnapshotID { + newNodeAddress, newNode := s.config.DataNodeAllocator.Copy(dataNodeData) + newNode.Header.SnapshotID = s.config.SnapshotID + oldNodeAddress := *pInfo.Item + *pInfo.Item = newNodeAddress + s.config.Deallocator.Deallocate(oldNodeAddress, dataNode.Header.SnapshotID) + dataNode = newNode + } + if dataNode.Header.HashMod > 0 { + h = hashKey(key, dataNode.Header.HashMod) + } + + index := s.config.DataNodeAllocator.Index(h) + if dataNode.States[index] == stateData && h == dataNode.Items[index].Hash && key == dataNode.Items[index].Key { + dataNode.States[index] = stateFree + } + + return + default: + pointerNodeData, pointerNode := s.config.PointerNodeAllocator.Get(*pInfo.Item) + if pointerNode.Header.SnapshotID < s.config.SnapshotID { + newNodeAddress, newNode := s.config.PointerNodeAllocator.Copy(pointerNodeData) + newNode.Header.SnapshotID = s.config.SnapshotID + oldNodeAddress := *pInfo.Item + *pInfo.Item = newNodeAddress + s.config.Deallocator.Deallocate(oldNodeAddress, pointerNode.Header.SnapshotID) + pointerNode = newNode + } + if pointerNode.Header.HashMod > 0 { + h = hashKey(key, pointerNode.Header.HashMod) + } + + index := s.config.PointerNodeAllocator.Index(h) + h = s.config.PointerNodeAllocator.Shift(h) + + pInfo = ParentInfo{ + State: &pointerNode.States[index], + Item: &pointerNode.Items[index], + } + } + } +} + func (s *Space[K, V]) set(pInfo ParentInfo, item DataItem[K, V]) { for { switch *pInfo.State { diff --git a/types.go b/types.go index 94f0676..367e096 100644 --- a/types.go +++ b/types.go @@ -75,14 +75,16 @@ type SpaceInfo struct { // SnapshotInfo stores information required to retrieve snapshot. type SnapshotInfo struct { - SnapshotID SnapshotID - DeallocationRoot SpaceInfo - SpaceRoot SpaceInfo + PreviousSnapshotID SnapshotID + NextSnapshotID SnapshotID + DeallocationRoot SpaceInfo + SpaceRoot SpaceInfo } // SingularityNode is the root of the store. type SingularityNode struct { - Version uint64 - SnapshotID SnapshotID - SnapshotRoot SpaceInfo + Version uint64 + FirstSnapshotID SnapshotID + LastSnapshotID SnapshotID + SnapshotRoot SpaceInfo }