Skip to content

Commit 2f51301

Browse files
authored
Merge pull request #9 from C-Pro/feature/alloc-test
deeper trie cleanup on kv.Del
2 parents be00428 + 81a740b commit 2f51301

File tree

2 files changed

+61
-16
lines changed

2 files changed

+61
-16
lines changed

kv.go

+28-11
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,10 @@ func (n *trieNode) addToList(node *trieNode) *trieNode {
6262

6363
// Removes node from the linked list.
6464
// Returns the new head (if it has changed).
65+
// Returns true if the list is now empty.
6566
// Should be called on the head node.
6667
// Will loop forever if node is not in the list.
67-
func (n *trieNode) removeFromList(c byte) *trieNode {
68+
func (n *trieNode) removeFromList(c byte) (*trieNode, bool) {
6869
curr := n
6970
for {
7071
if curr.c == c {
@@ -78,10 +79,14 @@ func (n *trieNode) removeFromList(c byte) *trieNode {
7879

7980
if curr.prev == nil {
8081
// Head has changed.
81-
return curr.next
82+
if curr.next == nil {
83+
// List is now empty.
84+
return nil, true
85+
}
86+
return curr.next, false
8287
}
8388

84-
return nil
89+
return nil, false
8590
}
8691

8792
curr = curr.next
@@ -249,25 +254,37 @@ func (kv *KV[V]) Del(key string) error {
249254
defer kv.mux.Unlock()
250255

251256
node := kv.trie
252-
var prev *trieNode
257+
stack := []*trieNode{}
253258
for i := 0; i < len(key); i++ {
254259
next := node.down[key[i]]
255260
if next == nil {
256-
return nil
261+
// If we are here, the key does not exist.
262+
return kv.data.Del(key)
257263
}
258264

259-
prev = node
265+
stack = append(stack, node)
260266
node = next
261267
}
262268

263269
node.terminal = false
264270

265-
if node.nextLevelHead == nil && prev != nil {
266-
head := prev.nextLevelHead.removeFromList(node.c)
267-
if head != nil {
268-
prev.nextLevelHead = head
271+
// Go back the stack removing nodes with no descendants.
272+
for i := len(stack) - 1; i >= 0; i-- {
273+
prev := stack[i]
274+
stack = stack[:i]
275+
if node.nextLevelHead == nil {
276+
head, empty := prev.nextLevelHead.removeFromList(node.c)
277+
if head != nil || (head == nil && empty) {
278+
prev.nextLevelHead = head
279+
}
280+
delete(prev.down, node.c)
281+
}
282+
283+
if prev.terminal || len(prev.down) > 0 && prev == kv.trie {
284+
break
269285
}
270-
delete(prev.down, node.c)
286+
287+
node = prev
271288
}
272289

273290
return kv.data.Del(key)

kv_test.go

+33-5
Original file line numberDiff line numberDiff line change
@@ -349,19 +349,47 @@ func TestKVAlloc(t *testing.T) {
349349
t.Logf("memIncrease: %d", mAfter.HeapAlloc-mBefore.HeapAlloc)
350350
t.Logf("memIncreaseRatio: %d", int(float64(mAfter.HeapAlloc-mBefore.HeapAlloc)/float64(rawDataLen)))
351351

352-
if keys, err := kv.ListByPrefix(""); err != nil {
353-
for _, key := range keys {
354-
_ = kv.Del(key)
355-
}
352+
keys, err := kv.ListByPrefix("")
353+
if err != nil {
354+
t.Fatalf("unexpected error in ListByPrefix: %v", err)
355+
}
356+
357+
for _, key := range keys {
358+
_ = kv.Del(key)
356359
}
357360

358361
runtime.GC()
359362
runtime.ReadMemStats(&mAfter)
360363
t.Logf("memIncreaseAfterDel: %d", mAfter.HeapAlloc-mBefore.HeapAlloc)
361364

362365
if mAfter.HeapAlloc > mBefore.HeapAlloc {
363-
if mAfter.HeapAlloc-mBefore.HeapAlloc > uint64(rawDataLen/10) {
366+
if mAfter.HeapAlloc-mBefore.HeapAlloc > uint64(rawDataLen) {
364367
t.Errorf("memory increase is too big")
365368
}
366369
}
370+
371+
if len(kv.trie.down) > 0 {
372+
t.Log(kv.trie.down)
373+
t.Errorf("trie is not empty")
374+
}
375+
}
376+
377+
func TestKVDel(t *testing.T) {
378+
cache := NewMapCache[string, string]()
379+
kv := NewKV[string](cache)
380+
381+
kv.Set("foo", "bar")
382+
_ = kv.Del("foo")
383+
384+
if len(kv.trie.down) > 0 {
385+
t.Error("trie is not empty")
386+
}
387+
388+
kv.Set("fo", "bar")
389+
kv.Set("food", "bar")
390+
_ = kv.Del("food")
391+
392+
if len(kv.trie.down) != 1 {
393+
t.Errorf("expectedf root trie to have 1 element, got %d", len(kv.trie.down))
394+
}
367395
}

0 commit comments

Comments
 (0)