Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
marcopeereboom committed Jan 9, 2025
1 parent ee0ffd2 commit 5fac4b0
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 12 deletions.
46 changes: 34 additions & 12 deletions database/tbcd/level/blockcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,32 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
)

const blockSize = 1677721 // ~1.6MB rough size of a mainnet block as of Jan 2025
var blockSize = 1677721 // ~1.6MB rough size of a mainnet block as of Jan 2025

type timeBlock struct {
type blockElement struct {
element *list.Element
block []byte
}

type CacheStats struct {
Hits int
Misses int
Purges int
}

type lowIQLRU struct {
mtx sync.RWMutex

size int // this is the approximate max size

m map[chainhash.Hash]timeBlock
m map[chainhash.Hash]blockElement
totalSize int

// lru list
// lru list, when used move to back of the list
l *list.List

// stats
c CacheStats
}

func (l *lowIQLRU) Put(v *btcutil.Block) {
Expand All @@ -43,26 +52,29 @@ func (l *lowIQLRU) Put(v *btcutil.Block) {

block, err := v.Bytes()
if err != nil {
// data corruption, panic
panic(err)
// XXX don't cache but panic for now for diagnostic
}

// evict first element in list
if l.totalSize+len(block) >= l.size {
if l.totalSize+len(block) > l.size {
// LET THEM EAT PANIC
re := l.l.Front()
rha := l.l.Remove(re)
rh := rha.(chainhash.Hash)
l.totalSize -= len(l.m[rh].block)
delete(l.m, rh)
// fmt.Printf("rha %v\n", spew.Sdump(rha))
// fmt.Printf("==== re %T rha %T\n", re, rha)
rh := rha.(*list.Element).Value.(*chainhash.Hash)
l.totalSize -= len(l.m[*rh].block)
delete(l.m, *rh)
l.c.Purges++
}

// lru list
element := &list.Element{Value: hash}
l.l.PushBack(element)

// block lookup
l.m[*hash] = timeBlock{element: element, block: block}
l.m[*hash] = blockElement{element: element, block: block}
l.totalSize += len(block)
}

Expand All @@ -72,19 +84,29 @@ func (l *lowIQLRU) Get(k *chainhash.Hash) (*btcutil.Block, bool) {

be, ok := l.m[*k]
if !ok {
l.c.Misses++
return nil, false
}
b, err := btcutil.NewBlockFromBytes(be.block)
if err != nil {
panic(err) // XXX delete from cache and return nil, false but panic for diagnostics at this time
// panic for diagnostics at this time
panic(err)
}

// update access
l.l.MoveToBack(be.element)

l.c.Hits++

return b, true
}

func (l *lowIQLRU) Stats() CacheStats {
l.mtx.RLock()
defer l.mtx.RUnlock()
return l.c
}

func lowIQLRUNewSize(size int) (*lowIQLRU, error) {
if size <= 0 {
return nil, fmt.Errorf("invalid size: %v", size)
Expand All @@ -96,7 +118,7 @@ func lowIQLRUNewSize(size int) (*lowIQLRU, error) {
}
return &lowIQLRU{
size: size,
m: make(map[chainhash.Hash]timeBlock, count),
m: make(map[chainhash.Hash]blockElement, count),
l: list.New(),
}, nil
}
65 changes: 65 additions & 0 deletions database/tbcd/level/cache_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package level

import (
"testing"

"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/davecgh/go-spew/spew"
)

func newBlock(prevHash *chainhash.Hash, nonce uint32) (chainhash.Hash, *btcutil.Block) {
bh := wire.NewBlockHeader(0, prevHash, &chainhash.Hash{}, 0, uint32(nonce))
b := wire.NewMsgBlock(bh)
return bh.BlockHash(), btcutil.NewBlock(b)
}

func TestLRUCache(t *testing.T) {
maxCache := 10
blockSize = 81 // we'll use empty blocks
l, err := lowIQLRUNewSize(blockSize * maxCache)
if err != nil {
t.Fatal(err)
}

prevHash := chainhash.Hash{} // genesis
blocks := make([]chainhash.Hash, 0, maxCache*2)
for i := 0; i < maxCache; i++ {
h, b := newBlock(&prevHash, uint32(i))
t.Logf("%v: %v", i, h)
blocks = append(blocks, h)
l.Put(b)
prevHash = h
}

// verify stats are 0
s := l.Stats()
if s.Hits != 0 && s.Misses != 0 && s.Purges != 0 {
t.Fatal(spew.Sdump(s))
}

// retrieve all blocks
for k := range blocks {
if _, ok := l.Get(&blocks[k]); !ok {
t.Fatalf("block not found: %v", blocks[k])
}
}

// verify hits are maxBlocks
s = l.Stats()
if s.Hits != 10 && s.Misses != 0 && s.Purges != 0 {
t.Fatal(spew.Sdump(s))
}

// purge oldest cache entries
for i := maxCache; i < maxCache*2; i++ {
h, b := newBlock(&prevHash, uint32(i))
t.Logf("%v: %v", i, h)
blocks = append(blocks, h)
l.Put(b)
prevHash = h
}

t.Logf("%v", spew.Sdump(l.Stats()))
}

0 comments on commit 5fac4b0

Please sign in to comment.