Skip to content

Commit

Permalink
First approach to deallocation
Browse files Browse the repository at this point in the history
  • Loading branch information
outofforest committed Oct 3, 2024
1 parent e16caa9 commit 1860263
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 46 deletions.
5 changes: 5 additions & 0 deletions allocator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
44 changes: 21 additions & 23 deletions list.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ type ListConfig struct {
SnapshotID SnapshotID
Item NodeAddress
NodeAllocator ListNodeAllocator
Allocator *Allocator
Deallocator Deallocator
}

Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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
}

Expand All @@ -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)
}
}
134 changes: 118 additions & 16 deletions snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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))
Expand All @@ -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,
Expand All @@ -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,
Expand Down
58 changes: 57 additions & 1 deletion space.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 {
Expand Down
14 changes: 8 additions & 6 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 1860263

Please sign in to comment.