From b5847680750098c000367263feb26d5e7b9cbd7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Ma=C5=82ota-W=C3=B3jcik?= <59281144+outofforest@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:05:47 +0200 Subject: [PATCH] Snapshot-level allocator (#21) --- allocator.go | 93 +++++++++++++++++++++++++++++++--------------------- db.go | 28 ++++++++-------- list.go | 17 +++++----- space.go | 24 +++++++------- 4 files changed, 88 insertions(+), 74 deletions(-) diff --git a/allocator.go b/allocator.go index 86f37c1..cbf880e 100644 --- a/allocator.go +++ b/allocator.go @@ -36,9 +36,12 @@ func (a *Allocator) Node(nodeAddress NodeAddress) []byte { return a.data[uint64(nodeAddress)*a.config.NodeSize : uint64(nodeAddress+1)*a.config.NodeSize] } -// Allocate allocates new node. -func (a *Allocator) Allocate() (NodeAddress, []byte) { - return a.allocate(a.zeroNode) +// Allocate allocates node and copies data into it. +func (a *Allocator) Allocate(copyFrom []byte) (NodeAddress, []byte) { + a.lastAllocatedNode++ + node := a.Node(a.lastAllocatedNode) + copy(node, copyFrom) + return a.lastAllocatedNode, node } // Deallocate deallocates node. @@ -46,58 +49,72 @@ 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) -} - -func (a *Allocator) allocate(copyFrom []byte) (NodeAddress, []byte) { - a.lastAllocatedNode++ - node := a.Node(a.lastAllocatedNode) - copy(node, copyFrom) - return a.lastAllocatedNode, node -} - -// NewDeallocator returns new deallocator. -func NewDeallocator( +// NewSnapshotAllocator returns snapshot-level allocator. +func NewSnapshotAllocator( snapshotID SnapshotID, + allocator *Allocator, deallocationLists *Space[SnapshotID, NodeAddress], listNodeAllocator ListNodeAllocator, -) Deallocator { - return Deallocator{ +) SnapshotAllocator { + return SnapshotAllocator{ snapshotID: snapshotID, + allocator: allocator, deallocationLists: deallocationLists, listNodeAllocator: listNodeAllocator, + dirtyNodes: map[NodeAddress]struct{}{}, } } -// Deallocator tracks nodes to deallocate. -type Deallocator struct { +// SnapshotAllocator allocates memory on behalf of snapshot. +type SnapshotAllocator struct { snapshotID SnapshotID + allocator *Allocator deallocationLists *Space[SnapshotID, NodeAddress] listNodeAllocator ListNodeAllocator + + dirtyNodes map[NodeAddress]struct{} +} + +// Allocate allocates new node. +func (sa SnapshotAllocator) Allocate() (NodeAddress, []byte) { + nodeAddress, node := sa.allocator.Allocate(sa.allocator.zeroNode) + sa.dirtyNodes[nodeAddress] = struct{}{} + return nodeAddress, node } -// Deallocate adds node to the deallocation list. -func (d Deallocator) Deallocate(nodeAddress NodeAddress, srcSnapshotID SnapshotID) { - if srcSnapshotID == d.snapshotID { - // FIXME (wojciech): Deallocate immediately +// Copy allocates new node and copies content from existing one. +func (sa SnapshotAllocator) Copy(data []byte) (NodeAddress, []byte) { + nodeAddress, node := sa.allocator.Allocate(data) + sa.dirtyNodes[nodeAddress] = struct{}{} + return nodeAddress, node +} + +// Deallocate marks node for deallocation. +func (sa SnapshotAllocator) Deallocate(nodeAddress NodeAddress, srcSnapshotID SnapshotID) { + if srcSnapshotID == sa.snapshotID { + sa.DeallocateImmediately(nodeAddress) return } - listNodeAddress, _ := d.deallocationLists.Get(srcSnapshotID) + listNodeAddress, _ := sa.deallocationLists.Get(srcSnapshotID) list := NewList(ListConfig{ - SnapshotID: d.snapshotID, + SnapshotID: sa.snapshotID, Item: listNodeAddress, - NodeAllocator: d.listNodeAllocator, - Deallocator: d, + NodeAllocator: sa.listNodeAllocator, + Allocator: sa, }) list.Add(nodeAddress) if list.config.Item != listNodeAddress { - d.deallocationLists.Set(srcSnapshotID, list.config.Item) + sa.deallocationLists.Set(srcSnapshotID, list.config.Item) } } +// DeallocateImmediately dealocates node immediately. +func (sa SnapshotAllocator) DeallocateImmediately(nodeAddress NodeAddress) { + delete(sa.dirtyNodes, nodeAddress) + sa.allocator.Deallocate(nodeAddress) +} + // NewSpaceNodeAllocator creates new space node allocator. func NewSpaceNodeAllocator[T comparable](allocator *Allocator) (SpaceNodeAllocator[T], error) { headerSize := uint64(unsafe.Sizeof(SpaceNodeHeader{})) @@ -154,14 +171,14 @@ func (na SpaceNodeAllocator[T]) Get(nodeAddress NodeAddress) ([]byte, SpaceNode[ } // Allocate allocates new object. -func (na SpaceNodeAllocator[T]) Allocate() (NodeAddress, SpaceNode[T]) { - n, node := na.allocator.Allocate() +func (na SpaceNodeAllocator[T]) Allocate(allocator SnapshotAllocator) (NodeAddress, SpaceNode[T]) { + n, node := allocator.Allocate() return n, na.project(node) } // Copy allocates copy of existing object. -func (na SpaceNodeAllocator[T]) Copy(data []byte) (NodeAddress, SpaceNode[T]) { - n, node := na.allocator.Copy(data) +func (na SpaceNodeAllocator[T]) Copy(allocator SnapshotAllocator, data []byte) (NodeAddress, SpaceNode[T]) { + n, node := allocator.Copy(data) return n, na.project(node) } @@ -220,14 +237,14 @@ func (na ListNodeAllocator) Get(nodeAddress NodeAddress) ([]byte, ListNode) { } // Allocate allocates new object. -func (na ListNodeAllocator) Allocate() (NodeAddress, ListNode) { - n, node := na.allocator.Allocate() +func (na ListNodeAllocator) Allocate(allocator SnapshotAllocator) (NodeAddress, ListNode) { + n, node := allocator.Allocate() return n, na.project(node) } // Copy allocates copy of existing object. -func (na ListNodeAllocator) Copy(data []byte) (NodeAddress, ListNode) { - n, node := na.allocator.Copy(data) +func (na ListNodeAllocator) Copy(allocator SnapshotAllocator, data []byte) (NodeAddress, ListNode) { + n, node := allocator.Copy(data) return n, na.project(node) } diff --git a/db.go b/db.go index bb88f70..515bd14 100644 --- a/db.go +++ b/db.go @@ -29,7 +29,7 @@ type Snapshot struct { Snapshots *Space[SnapshotID, SnapshotInfo] Spaces *Space[SpaceID, SpaceInfo] SpacesToCommit map[SpaceID]SpaceToCommit - Deallocator Deallocator + Allocator SnapshotAllocator } // New creates new database. @@ -106,18 +106,20 @@ func (db *DB) DeleteSnapshot(snapshotID SnapshotID) error { State: lo.ToPtr(snapshotInfo.DeallocationRoot.State), Item: lo.ToPtr(snapshotInfo.DeallocationRoot.Node), }, + PointerNodeAllocator: db.pointerNodeAllocator, + DataNodeAllocator: db.snapshotToNodeNodeAllocator, }) 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), + State: &nextSnapshotInfo.DeallocationRoot.State, + Item: &nextSnapshotInfo.DeallocationRoot.Node, }, PointerNodeAllocator: db.pointerNodeAllocator, DataNodeAllocator: db.snapshotToNodeNodeAllocator, - Deallocator: db.nextSnapshot.Deallocator, + Allocator: db.nextSnapshot.Allocator, }) var startSnapshotID SnapshotID @@ -159,11 +161,6 @@ func (db *DB) DeleteSnapshot(snapshotID SnapshotID) error { } nextSnapshotInfo.PreviousSnapshotID = snapshotInfo.PreviousSnapshotID - nextSnapshotInfo.DeallocationRoot = SpaceInfo{ - State: *nextDeallocationLists.config.SpaceRoot.State, - Node: *nextDeallocationLists.config.SpaceRoot.Item, - HashMod: *nextDeallocationLists.config.HashMod, - } db.nextSnapshot.Snapshots.Set(snapshotInfo.NextSnapshotID, nextSnapshotInfo) } @@ -223,8 +220,9 @@ func (db *DB) prepareNextSnapshot(singularityNode SingularityNode) error { NextSnapshotID: snapshotID + 1, } - deallocator := NewDeallocator( + allocator := NewSnapshotAllocator( snapshotID, + db.config.Allocator, NewSpace[SnapshotID, NodeAddress](SpaceConfig[SnapshotID, NodeAddress]{ SnapshotID: snapshotID, HashMod: &snapshotInfo.DeallocationRoot.HashMod, @@ -237,7 +235,7 @@ func (db *DB) prepareNextSnapshot(singularityNode SingularityNode) error { }), db.listNodeAllocator, ) - deallocator.deallocationLists.config.Deallocator = deallocator + allocator.deallocationLists.config.Allocator = allocator snapshots := NewSpace[SnapshotID, SnapshotInfo](SpaceConfig[SnapshotID, SnapshotInfo]{ SnapshotID: snapshotID, @@ -248,7 +246,7 @@ func (db *DB) prepareNextSnapshot(singularityNode SingularityNode) error { }, PointerNodeAllocator: db.pointerNodeAllocator, DataNodeAllocator: db.snapshotInfoNodeAllocator, - Deallocator: deallocator, + Allocator: allocator, }) if singularityNode.SnapshotRoot.State != stateFree { @@ -276,10 +274,10 @@ func (db *DB) prepareNextSnapshot(singularityNode SingularityNode) error { }, PointerNodeAllocator: db.pointerNodeAllocator, DataNodeAllocator: db.spaceInfoNodeAllocator, - Deallocator: deallocator, + Allocator: allocator, }), SpacesToCommit: map[SpaceID]SpaceToCommit{}, - Deallocator: deallocator, + Allocator: allocator, } return nil @@ -312,6 +310,6 @@ func GetSpace[K, V comparable](spaceID SpaceID, db *DB) (*Space[K, V], error) { SpaceRoot: space.PInfo, PointerNodeAllocator: db.pointerNodeAllocator, DataNodeAllocator: dataNodeAllocator, - Deallocator: db.nextSnapshot.Deallocator, + Allocator: db.nextSnapshot.Allocator, }), nil } diff --git a/list.go b/list.go index 69952c9..dbc088a 100644 --- a/list.go +++ b/list.go @@ -5,8 +5,7 @@ type ListConfig struct { SnapshotID SnapshotID Item NodeAddress NodeAllocator ListNodeAllocator - Allocator *Allocator - Deallocator Deallocator + Allocator SnapshotAllocator } // NewList creates new list. @@ -24,7 +23,7 @@ type List struct { // Add adds address to the list. func (l *List) Add(nodeAddress NodeAddress) { if l.config.Item == 0 { - newNodeAddress, newNode := l.config.NodeAllocator.Allocate() + newNodeAddress, newNode := l.config.NodeAllocator.Allocate(l.config.Allocator) newNode.Items[0] = nodeAddress newNode.Header.SnapshotID = l.config.SnapshotID newNode.Header.NumOfItems = 1 @@ -35,11 +34,11 @@ func (l *List) Add(nodeAddress NodeAddress) { listNodeData, listNode := l.config.NodeAllocator.Get(l.config.Item) if listNode.Header.NumOfItems+listNode.Header.NumOfSideLists < uint64(len(listNode.Items)) { if listNode.Header.SnapshotID < l.config.SnapshotID { - newNodeAddress, newNode := l.config.NodeAllocator.Copy(listNodeData) + newNodeAddress, newNode := l.config.NodeAllocator.Copy(l.config.Allocator, listNodeData) newNode.Header.SnapshotID = l.config.SnapshotID oldNodeAddress := l.config.Item l.config.Item = newNodeAddress - l.config.Allocator.Deallocate(oldNodeAddress) + l.config.Allocator.DeallocateImmediately(oldNodeAddress) listNode = newNode } @@ -49,7 +48,7 @@ func (l *List) Add(nodeAddress NodeAddress) { return } - newNodeAddress, newNode := l.config.NodeAllocator.Allocate() + newNodeAddress, newNode := l.config.NodeAllocator.Allocate(l.config.Allocator) newNode.Items[0] = nodeAddress newNode.Items[len(newNode.Items)-1] = l.config.Item newNode.Header.SnapshotID = l.config.SnapshotID @@ -67,11 +66,11 @@ func (l *List) Attach(nodeAddress NodeAddress) { listNodeData, listNode := l.config.NodeAllocator.Get(l.config.Item) if listNode.Header.NumOfItems+listNode.Header.NumOfSideLists < uint64(len(listNode.Items)) { if listNode.Header.SnapshotID < l.config.SnapshotID { - newNodeAddress, newNode := l.config.NodeAllocator.Copy(listNodeData) + newNodeAddress, newNode := l.config.NodeAllocator.Copy(l.config.Allocator, listNodeData) newNode.Header.SnapshotID = l.config.SnapshotID oldNodeAddress := l.config.Item l.config.Item = newNodeAddress - l.config.Allocator.Deallocate(oldNodeAddress) + l.config.Allocator.DeallocateImmediately(oldNodeAddress) listNode = newNode } @@ -81,7 +80,7 @@ func (l *List) Attach(nodeAddress NodeAddress) { return } - newNodeAddress, newNode := l.config.NodeAllocator.Allocate() + newNodeAddress, newNode := l.config.NodeAllocator.Allocate(l.config.Allocator) newNode.Items[uint64(len(listNode.Items))-1] = l.config.Item newNode.Items[uint64(len(listNode.Items))-2] = nodeAddress newNode.Header.SnapshotID = l.config.SnapshotID diff --git a/space.go b/space.go index 76b97a3..7ec171a 100644 --- a/space.go +++ b/space.go @@ -13,7 +13,7 @@ type SpaceConfig[K, V comparable] struct { SpaceRoot ParentInfo PointerNodeAllocator SpaceNodeAllocator[NodeAddress] DataNodeAllocator SpaceNodeAllocator[DataItem[K, V]] - Deallocator Deallocator + Allocator SnapshotAllocator } // NewSpace creates new space. @@ -95,11 +95,11 @@ func (s *Space[K, V]) Delete(key K) { dataNodeData, dataNode := s.config.DataNodeAllocator.Get(*pInfo.Item) if dataNode.Header.SnapshotID < s.config.SnapshotID { - newNodeAddress, newNode := s.config.DataNodeAllocator.Copy(dataNodeData) + newNodeAddress, newNode := s.config.DataNodeAllocator.Copy(s.config.Allocator, dataNodeData) newNode.Header.SnapshotID = s.config.SnapshotID oldNodeAddress := *pInfo.Item *pInfo.Item = newNodeAddress - s.config.Deallocator.Deallocate(oldNodeAddress, dataNode.Header.SnapshotID) + s.config.Allocator.Deallocate(oldNodeAddress, dataNode.Header.SnapshotID) dataNode = newNode } if dataNode.Header.HashMod > 0 { @@ -115,11 +115,11 @@ func (s *Space[K, V]) Delete(key K) { default: pointerNodeData, pointerNode := s.config.PointerNodeAllocator.Get(*pInfo.Item) if pointerNode.Header.SnapshotID < s.config.SnapshotID { - newNodeAddress, newNode := s.config.PointerNodeAllocator.Copy(pointerNodeData) + newNodeAddress, newNode := s.config.PointerNodeAllocator.Copy(s.config.Allocator, pointerNodeData) newNode.Header.SnapshotID = s.config.SnapshotID oldNodeAddress := *pInfo.Item *pInfo.Item = newNodeAddress - s.config.Deallocator.Deallocate(oldNodeAddress, pointerNode.Header.SnapshotID) + s.config.Allocator.Deallocate(oldNodeAddress, pointerNode.Header.SnapshotID) pointerNode = newNode } if pointerNode.Header.HashMod > 0 { @@ -141,7 +141,7 @@ func (s *Space[K, V]) set(pInfo ParentInfo, item DataItem[K, V]) { for { switch *pInfo.State { case stateFree: - dataNodeAddress, dataNode := s.config.DataNodeAllocator.Allocate() + dataNodeAddress, dataNode := s.config.DataNodeAllocator.Allocate(s.config.Allocator) dataNode.Header.SnapshotID = s.config.SnapshotID *pInfo.State = stateData @@ -157,11 +157,11 @@ func (s *Space[K, V]) set(pInfo ParentInfo, item DataItem[K, V]) { dataNodeData, dataNode := s.config.DataNodeAllocator.Get(*pInfo.Item) if dataNode.Header.SnapshotID < s.config.SnapshotID { - newNodeAddress, newNode := s.config.DataNodeAllocator.Copy(dataNodeData) + newNodeAddress, newNode := s.config.DataNodeAllocator.Copy(s.config.Allocator, dataNodeData) newNode.Header.SnapshotID = s.config.SnapshotID oldNodeAddress := *pInfo.Item *pInfo.Item = newNodeAddress - s.config.Deallocator.Deallocate(oldNodeAddress, dataNode.Header.SnapshotID) + s.config.Allocator.Deallocate(oldNodeAddress, dataNode.Header.SnapshotID) dataNode = newNode } if dataNode.Header.HashMod > 0 { @@ -195,11 +195,11 @@ func (s *Space[K, V]) set(pInfo ParentInfo, item DataItem[K, V]) { default: pointerNodeData, pointerNode := s.config.PointerNodeAllocator.Get(*pInfo.Item) if pointerNode.Header.SnapshotID < s.config.SnapshotID { - newNodeAddress, newNode := s.config.PointerNodeAllocator.Copy(pointerNodeData) + newNodeAddress, newNode := s.config.PointerNodeAllocator.Copy(s.config.Allocator, pointerNodeData) newNode.Header.SnapshotID = s.config.SnapshotID oldNodeAddress := *pInfo.Item *pInfo.Item = newNodeAddress - s.config.Deallocator.Deallocate(oldNodeAddress, pointerNode.Header.SnapshotID) + s.config.Allocator.Deallocate(oldNodeAddress, pointerNode.Header.SnapshotID) pointerNode = newNode } if pointerNode.Header.HashMod > 0 { @@ -221,7 +221,7 @@ func (s *Space[K, V]) redistributeNode(pInfo ParentInfo) { dataNodeAddress := *pInfo.Item _, dataNode := s.config.DataNodeAllocator.Get(dataNodeAddress) - pointerNodeAddress, pointerNode := s.config.PointerNodeAllocator.Allocate() + pointerNodeAddress, pointerNode := s.config.PointerNodeAllocator.Allocate(s.config.Allocator) *pointerNode.Header = SpaceNodeHeader{ SnapshotID: s.config.SnapshotID, HashMod: dataNode.Header.HashMod, @@ -238,7 +238,7 @@ func (s *Space[K, V]) redistributeNode(pInfo ParentInfo) { s.set(pInfo, dataNode.Items[i]) } - s.config.Deallocator.Deallocate(dataNodeAddress, dataNode.Header.SnapshotID) + s.config.Allocator.Deallocate(dataNodeAddress, dataNode.Header.SnapshotID) } func hashKey[K comparable](key K, hashMod uint64) Hash {