Skip to content

Commit 13b0bad

Browse files
committed
tapdb: improve proof cache key handling and size estimation
- Replace `ProofKey` with `UniverseProofKey` for clearer cache key distinction, enabling removal of individual proofs by leaf key instead of all proofs for a universe ID. - Refactor proof cache methods: add `RemoveLeafKeyProofs` and enhance `RemoveUniverseProofs` for more granular control. - Implement deep size estimation in the `Size` method to improve memory management accuracy. - Change cache size limit from number of proofs to total memory size of all proofs, as proof sizes can vary due to inclusion of parent proof files.
1 parent 1776033 commit 13b0bad

File tree

3 files changed

+94
-59
lines changed

3 files changed

+94
-59
lines changed

tapdb/multiverse.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -834,7 +834,7 @@ func (b *MultiverseStore) UpsertProofLeaf(ctx context.Context,
834834

835835
// Invalidate the cache since we just updated the root.
836836
b.rootNodeCache.wipeCache()
837-
b.proofCache.delProofsForAsset(id)
837+
b.proofCache.RemoveLeafKeyProofs(id, key)
838838
b.leafKeysCache.wipeCache(id.String())
839839
b.syncerCache.addOrReplace(universe.Root{
840840
ID: id,
@@ -952,10 +952,14 @@ func (b *MultiverseStore) UpsertProofLeafBatch(ctx context.Context,
952952
)
953953

954954
for id := range idsToDelete {
955-
b.proofCache.Delete(id)
956955
b.leafKeysCache.wipeCache(id)
957956
}
958957

958+
for idx := range items {
959+
item := items[idx]
960+
b.proofCache.RemoveLeafKeyProofs(item.ID, item.Key)
961+
}
962+
959963
return nil
960964
}
961965

@@ -990,7 +994,7 @@ func (b *MultiverseStore) DeleteUniverse(ctx context.Context,
990994
// Wipe the cache items from this node.
991995
b.rootNodeCache.wipeCache()
992996

993-
b.proofCache.Delete(id.String())
997+
b.proofCache.RemoveUniverseProofs(id)
994998
b.leafKeysCache.wipeCache(id.String())
995999
b.syncerCache.remove(id.Key())
9961000

tapdb/multiverse_cache.go

Lines changed: 86 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,12 @@ package tapdb
22

33
import (
44
"bytes"
5-
"crypto/sha256"
65
"slices"
76
"sort"
87
"sync"
98
"sync/atomic"
109

1110
"github.com/lightninglabs/neutrino/cache/lru"
12-
"github.com/lightninglabs/taproot-assets/fn"
1311
"github.com/lightninglabs/taproot-assets/universe"
1412
"github.com/lightningnetwork/lnd/lnutils"
1513
)
@@ -67,26 +65,6 @@ func DefaultMultiverseCacheConfig() MultiverseCacheConfig {
6765
}
6866
}
6967

70-
// ProofKey is used to uniquely identify a proof within a universe. This is
71-
// used for the LRU cache for the proofs themselves, which are considered to be
72-
// immutable.
73-
type ProofKey [32]byte
74-
75-
// NewProofKey takes a universe identifier and leaf key, and returns a proof
76-
// key.
77-
func NewProofKey(id universe.Identifier, key universe.LeafKey) ProofKey {
78-
idBytes := id.Bytes()
79-
leafKeyBytes := key.UniverseKey()
80-
81-
// The proof key maps down the ID and the leaf key into a single
82-
// 32-byte value: sha256(id || leaf_key)..
83-
h := sha256.New()
84-
h.Write(idBytes[:])
85-
h.Write(leafKeyBytes[:])
86-
87-
return fn.ToArray[ProofKey](h.Sum(nil))
88-
}
89-
9068
// cachedProofs is a list of cached proof leaves.
9169
type cachedProofs []*universe.Proof
9270

@@ -95,52 +73,81 @@ type cachedProofs []*universe.Proof
9573
// if the universe key's script key isn't set. But we only want a certain number
9674
// of different keys stored in the cache.
9775
func (c *cachedProofs) Size() (uint64, error) {
98-
return 1, nil
76+
if c == nil {
77+
return 0, nil
78+
}
79+
80+
totalBytes := uint64(0)
81+
for _, proof := range *c {
82+
if proof == nil {
83+
continue
84+
}
85+
86+
totalBytes += proof.LowerBoundByteSize()
87+
}
88+
89+
return totalBytes, nil
9990
}
10091

10192
// newProofCache creates a new leaf proof cache.
102-
func newProofCache(proofCacheSize uint64) *lru.Cache[ProofKey, *cachedProofs] {
103-
return lru.NewCache[ProofKey, *cachedProofs](proofCacheSize)
93+
//
94+
// nolint: lll
95+
func newProofCache(totalCacheBytesSize uint64) *lru.Cache[UniverseProofKey, *cachedProofs] {
96+
return lru.NewCache[UniverseProofKey, *cachedProofs](
97+
totalCacheBytesSize,
98+
)
10499
}
105100

106101
// universeIDKey is a cache key used to uniquely identify a universe within a
107102
// multiverse tree cache.
108103
type universeIDKey = string
109104

105+
// UniverseProofKey houses the components of a universe proof key. All fields
106+
// must be comparable.
107+
type UniverseProofKey struct {
108+
// uniIDKey is the universe ID key to which the proof belongs.
109+
uniIDKey universe.IdentifierKey
110+
111+
// leafKey is the leaf key of the proof.
112+
leafKeyBytes [32]byte
113+
}
114+
115+
// NewUniverseProofKey creates a new universe proof key.
116+
func NewUniverseProofKey(uniID universe.Identifier,
117+
leafKey universe.LeafKey) UniverseProofKey {
118+
119+
return UniverseProofKey{
120+
uniIDKey: uniID.Key(),
121+
leafKeyBytes: leafKey.UniverseKey(),
122+
}
123+
}
124+
110125
// universeProofCache a map of proof caches for each proof type.
111126
type universeProofCache struct {
112-
proofsPerUniverse uint64
127+
// maxCacheByteSize is the maximum size of the cache in bytes.
128+
maxCacheByteSize uint64
113129

114-
lnutils.SyncMap[universeIDKey, *lru.Cache[ProofKey, *cachedProofs]]
130+
// cache is the LRU cache for the proofs themselves.
131+
cache *lru.Cache[UniverseProofKey, *cachedProofs]
115132

116133
*cacheLogger
117134
}
118135

119136
// newUniverseProofCache creates a new proof cache.
120-
func newUniverseProofCache(proofsPerUniverse uint64) *universeProofCache {
137+
func newUniverseProofCache(maxCacheByteSize uint64) *universeProofCache {
121138
return &universeProofCache{
122-
proofsPerUniverse: proofsPerUniverse,
123-
SyncMap: lnutils.SyncMap[
124-
universeIDKey, *lru.Cache[ProofKey, *cachedProofs],
125-
]{},
126-
cacheLogger: newCacheLogger("universe_proofs"),
139+
maxCacheByteSize: maxCacheByteSize,
140+
cache: newProofCache(maxCacheByteSize),
141+
cacheLogger: newCacheLogger("universe_proofs"),
127142
}
128143
}
129144

130145
// fetchProof reads the cached proof for the given ID and leaf key.
131146
func (p *universeProofCache) fetchProof(id universe.Identifier,
132147
leafKey universe.LeafKey) []*universe.Proof {
133148

134-
// First, get the sub-cache for this universe ID from the map of
135-
// caches.
136-
assetProofCache, _ := p.LoadOrStore(
137-
id.String(), newProofCache(p.proofsPerUniverse),
138-
)
139-
140-
// With that lower level cache obtained, we can check to see if we have
141-
// a hit or not.
142-
proofKey := NewProofKey(id, leafKey)
143-
proofFromCache, err := assetProofCache.Get(proofKey)
149+
uniProofKey := NewUniverseProofKey(id, leafKey)
150+
proofFromCache, err := p.cache.Get(uniProofKey)
144151
if err == nil {
145152
p.Hit()
146153
return *proofFromCache
@@ -155,28 +162,52 @@ func (p *universeProofCache) fetchProof(id universe.Identifier,
155162
func (p *universeProofCache) insertProofs(id universe.Identifier,
156163
leafKey universe.LeafKey, proofs []*universe.Proof) {
157164

158-
assetProofCache, _ := p.LoadOrStore(
159-
id.String(), newProofCache(p.proofsPerUniverse),
160-
)
161-
162-
proofKey := NewProofKey(id, leafKey)
165+
uniProofKey := NewUniverseProofKey(id, leafKey)
163166

164167
log.Debugf("Storing proof(s) in cache (universe_id=%v, leaf_key=%v, "+
165-
"proof_key=%x, count=%d)", id.StringForLog(), leafKey,
166-
proofKey[:], len(proofs))
168+
"count=%d)", id.StringForLog(), leafKey, len(proofs))
167169

168170
proofVal := cachedProofs(proofs)
169-
if _, err := assetProofCache.Put(proofKey, &proofVal); err != nil {
171+
if _, err := p.cache.Put(uniProofKey, &proofVal); err != nil {
170172
log.Errorf("Unable to insert proof into universe proof "+
171173
"cache: %v", err)
172174
}
173175
}
174176

175-
// delProofsForAsset deletes all the proofs for the given asset.
176-
func (p *universeProofCache) delProofsForAsset(id universe.Identifier) {
177-
log.Debugf("Wiping universe proof cache (universe_id=%v)", id)
177+
// RemoveUniverseProofs deletes all the proofs for the given universe ID.
178+
func (p *universeProofCache) RemoveUniverseProofs(id universe.Identifier) {
179+
log.Debugf("Removing universe proofs (universe_id=%s)",
180+
id.StringForLog())
178181

179-
p.Delete(id.String())
182+
targetIDKey := id.Key()
183+
p.cache.Range(
184+
func(key UniverseProofKey, proofs *cachedProofs) bool {
185+
if key.uniIDKey == targetIDKey {
186+
p.cache.Delete(key)
187+
}
188+
return true
189+
},
190+
)
191+
}
192+
193+
// RemoveLeafKeyProofs deletes all the proofs for the given universe ID and leaf
194+
// key.
195+
func (p *universeProofCache) RemoveLeafKeyProofs(id universe.Identifier,
196+
leafKey universe.LeafKey) {
197+
198+
log.Debugf("Removing leaf key proofs (universe_id=%s, leaf_key=%v)",
199+
id.StringForLog(), leafKey)
200+
201+
targetCacheKey := NewUniverseProofKey(id, leafKey)
202+
203+
p.cache.Range(
204+
func(key UniverseProofKey, proofs *cachedProofs) bool {
205+
if key == targetCacheKey {
206+
p.cache.Delete(key)
207+
}
208+
return true
209+
},
210+
)
180211
}
181212

182213
// rootPageQueryKey is a cache key that wraps around a query to fetch all the

tapdb/universe_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -620,7 +620,7 @@ func TestUniverseTreeIsolation(t *testing.T) {
620620
// TODO(roasbeef): need base universe -> universe cache
621621
// invalidation or delete thru multiverse
622622
multiverse.rootNodeCache.wipeCache()
623-
multiverse.proofCache.delProofsForAsset(rootNode.ID)
623+
multiverse.proofCache.RemoveUniverseProofs(rootNode.ID)
624624
}
625625

626626
// The deleted universe should not be present in the multiverse.

0 commit comments

Comments
 (0)