From 8eea7c794a89d315c823c345c64993f4b94849e7 Mon Sep 17 00:00:00 2001 From: Daniel Linsley Date: Wed, 21 Oct 2026 14:35:06 -0700 Subject: [PATCH] Fix Meta() panic when a source is truncated mid-call Store.Meta() made two passes over storageIndex. The second pass re-loaded each source ID and asserted the result to *storage without checking whether the entry still existed. If the truncation loop deleted a source between the two passes, Load returned nil and the type assertion panicked. The second pass existed only because meta.Count was never populated; it overwrote Count with tree.Size(). Maintain meta.Count on the storage whenever the tree changes (insertOrSwap and removeOldestEnvelope) and read it directly in Meta(). Meta() now makes a single pass and no longer re-loads from storageIndex. Also resolved the goal of the TODO comment --- src/internal/cache/store/store.go | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/internal/cache/store/store.go b/src/internal/cache/store/store.go index 125e7cb6..657c8131 100644 --- a/src/internal/cache/store/store.go +++ b/src/internal/cache/store/store.go @@ -173,6 +173,7 @@ func (storage *storage) insertOrSwap(store *Store, e *loggregator_v2.Envelope) { } storage.Put(e.Timestamp+timestampFudge, e) + storage.meta.Count = int64(storage.Size()) if e.Timestamp > storage.meta.NewestTimestamp { storage.meta.NewestTimestamp = e.Timestamp @@ -313,6 +314,7 @@ func (store *Store) removeOldestEnvelope(treeToPrune *storage, sourceId string) oldestEnvelope := treeToPrune.Left() treeToPrune.Remove(oldestEnvelope.Key.(int64)) + treeToPrune.meta.Count = int64(treeToPrune.Size()) if treeToPrune.Size() == 0 { store.storageIndex.Delete(sourceId) @@ -535,7 +537,7 @@ func (store *Store) Meta() map[string]logcache_v1.MetaInfo { store.storageIndex.Range(func(sourceId interface{}, tree interface{}) bool { tree.(*storage).RLock() metaReport[sourceId.(string)] = logcache_v1.MetaInfo{ - Count: int64(tree.(*storage).meta.GetCount()), + Count: tree.(*storage).meta.GetCount(), Expired: tree.(*storage).meta.GetExpired(), OldestTimestamp: tree.(*storage).meta.GetOldestTimestamp(), NewestTimestamp: tree.(*storage).meta.GetNewestTimestamp(), @@ -545,20 +547,6 @@ func (store *Store) Meta() map[string]logcache_v1.MetaInfo { return true }) - // Range over our local copy of metaReport - // TODO - shouldn't we just maintain Count on metaReport..?! - for sourceId := range metaReport { - tree, _ := store.storageIndex.Load(sourceId) - - tree.(*storage).RLock() - metaReport[sourceId] = logcache_v1.MetaInfo{ - Count: int64(tree.(*storage).Size()), - Expired: tree.(*storage).meta.GetExpired(), - OldestTimestamp: tree.(*storage).meta.GetOldestTimestamp(), - NewestTimestamp: tree.(*storage).meta.GetNewestTimestamp(), - } - tree.(*storage).RUnlock() - } return metaReport }