Skip to content

Commit faabb32

Browse files
authored
refactor: retrieve highest da height in cache (#2870)
1 parent 6badca1 commit faabb32

File tree

6 files changed

+79
-8
lines changed

6 files changed

+79
-8
lines changed

block/internal/cache/generic_cache.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"os"
77
"path/filepath"
88
"sync"
9+
"sync/atomic"
910
)
1011

1112
// Cache is a generic cache that maintains items that are seen and hard confirmed
@@ -18,6 +19,8 @@ type Cache[T any] struct {
1819
daIncluded *sync.Map
1920
// hashByHeight tracks the hash associated with each height for pruning
2021
hashByHeight *sync.Map
22+
// maxDAHeight tracks the maximum DA height seen
23+
maxDAHeight *atomic.Uint64
2124
}
2225

2326
// NewCache returns a new Cache struct
@@ -27,6 +30,7 @@ func NewCache[T any]() *Cache[T] {
2730
hashes: new(sync.Map),
2831
daIncluded: new(sync.Map),
2932
hashByHeight: new(sync.Map),
33+
maxDAHeight: &atomic.Uint64{},
3034
}
3135
}
3236

@@ -91,13 +95,25 @@ func (c *Cache[T]) getDAIncluded(hash string) (uint64, bool) {
9195
func (c *Cache[T]) setDAIncluded(hash string, daHeight uint64, blockHeight uint64) {
9296
c.daIncluded.Store(hash, daHeight)
9397
c.hashByHeight.Store(blockHeight, hash)
98+
99+
// Update max DA height if necessary
100+
current := c.maxDAHeight.Load()
101+
if daHeight >= current {
102+
_ = c.maxDAHeight.CompareAndSwap(current, daHeight)
103+
}
94104
}
95105

96106
// removeDAIncluded removes the DA-included status of the hash
97107
func (c *Cache[T]) removeDAIncluded(hash string) {
98108
c.daIncluded.Delete(hash)
99109
}
100110

111+
// daHeight returns the maximum DA height from all DA-included items.
112+
// Returns 0 if no items are DA-included.
113+
func (c *Cache[T]) daHeight() uint64 {
114+
return c.maxDAHeight.Load()
115+
}
116+
101117
// deleteAllForHeight removes all items and their associated data from the cache at the given height
102118
func (c *Cache[T]) deleteAllForHeight(height uint64) {
103119
c.itemsByHeight.Delete(height)
@@ -231,6 +247,11 @@ func (c *Cache[T]) LoadFromDisk(folderPath string) error {
231247
}
232248
for k, v := range daIncludedMap {
233249
c.daIncluded.Store(k, v)
250+
// Update max DA height during load
251+
current := c.maxDAHeight.Load()
252+
if v > current {
253+
_ = c.maxDAHeight.CompareAndSwap(current, v)
254+
}
234255
}
235256

236257
return nil

block/internal/cache/generic_cache_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,47 @@ func init() {
1414
gob.Register(&testItem{})
1515
}
1616

17+
// TestCache_MaxDAHeight verifies that daHeight tracks the maximum DA height
18+
func TestCache_MaxDAHeight(t *testing.T) {
19+
c := NewCache[testItem]()
20+
21+
// Initially should be 0
22+
if got := c.daHeight(); got != 0 {
23+
t.Errorf("initial daHeight = %d, want 0", got)
24+
}
25+
26+
// Set items with increasing DA heights
27+
c.setDAIncluded("hash1", 100, 1)
28+
if got := c.daHeight(); got != 100 {
29+
t.Errorf("after setDAIncluded(100): daHeight = %d, want 100", got)
30+
}
31+
32+
c.setDAIncluded("hash2", 50, 2) // Lower height shouldn't change max
33+
if got := c.daHeight(); got != 100 {
34+
t.Errorf("after setDAIncluded(50): daHeight = %d, want 100", got)
35+
}
36+
37+
c.setDAIncluded("hash3", 200, 3)
38+
if got := c.daHeight(); got != 200 {
39+
t.Errorf("after setDAIncluded(200): daHeight = %d, want 200", got)
40+
}
41+
42+
// Test persistence
43+
dir := t.TempDir()
44+
if err := c.SaveToDisk(dir); err != nil {
45+
t.Fatalf("SaveToDisk failed: %v", err)
46+
}
47+
48+
c2 := NewCache[testItem]()
49+
if err := c2.LoadFromDisk(dir); err != nil {
50+
t.Fatalf("LoadFromDisk failed: %v", err)
51+
}
52+
53+
if got := c2.daHeight(); got != 200 {
54+
t.Errorf("after load: daHeight = %d, want 200", got)
55+
}
56+
}
57+
1758
// TestCache_SaveLoad_ErrorPaths covers SaveToDisk and LoadFromDisk error scenarios.
1859
func TestCache_SaveLoad_ErrorPaths(t *testing.T) {
1960
c := NewCache[testItem]()

block/internal/cache/manager.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ type Manager interface {
4949
GetHeaderDAIncluded(hash string) (uint64, bool)
5050
SetHeaderDAIncluded(hash string, daHeight uint64, blockHeight uint64)
5151
RemoveHeaderDAIncluded(hash string)
52+
DaHeight() uint64
5253

5354
// Data operations
5455
IsDataSeen(hash string) bool
@@ -165,6 +166,11 @@ func (m *implementation) RemoveHeaderDAIncluded(hash string) {
165166
m.headerCache.removeDAIncluded(hash)
166167
}
167168

169+
// DaHeight fetches the heights da height contained in the processed cache.
170+
func (m *implementation) DaHeight() uint64 {
171+
return max(m.headerCache.daHeight(), m.dataCache.daHeight())
172+
}
173+
168174
// Data operations
169175
func (m *implementation) IsDataSeen(hash string) bool {
170176
return m.dataCache.isSeen(hash)

block/internal/syncing/syncer.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func (s *Syncer) SetLastState(state types.State) {
177177

178178
// GetDAHeight returns the current DA height
179179
func (s *Syncer) GetDAHeight() uint64 {
180-
return s.daHeight.Load()
180+
return max(s.daHeight.Load(), s.cache.DaHeight())
181181
}
182182

183183
// SetDAHeight updates the DA height
@@ -217,7 +217,7 @@ func (s *Syncer) initializeState() error {
217217
s.SetLastState(state)
218218

219219
// Set DA height
220-
s.SetDAHeight(state.DAHeight)
220+
s.SetDAHeight(max(s.genesis.DAStartHeight, s.cache.DaHeight()))
221221

222222
s.logger.Info().
223223
Uint64("height", state.LastBlockHeight).

block/internal/syncing/syncer_test.go

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,6 @@ func TestSyncLoopPersistState(t *testing.T) {
433433

434434
t.Log("sync workers on instance1 completed")
435435
require.Equal(t, myFutureDAHeight, syncerInst1.GetDAHeight())
436-
lastStateDAHeight := syncerInst1.GetLastState().DAHeight
437436

438437
// wait for all events consumed
439438
require.NoError(t, cacheMgr.SaveToDisk())
@@ -470,7 +469,6 @@ func TestSyncLoopPersistState(t *testing.T) {
470469
make(chan error, 1),
471470
)
472471
require.NoError(t, syncerInst2.initializeState())
473-
require.Equal(t, lastStateDAHeight, syncerInst2.GetDAHeight())
474472

475473
ctx, cancel = context.WithCancel(t.Context())
476474
t.Cleanup(cancel)
@@ -484,7 +482,7 @@ func TestSyncLoopPersistState(t *testing.T) {
484482
Run(func(arg mock.Arguments) {
485483
cancel()
486484
// retrieve last one again
487-
assert.Equal(t, lastStateDAHeight, arg.Get(1).(uint64))
485+
assert.Equal(t, syncerInst2.GetDAHeight(), arg.Get(1).(uint64))
488486
}).
489487
Return(nil, nil)
490488

@@ -589,6 +587,11 @@ func TestSyncer_InitializeState_CallsReplayer(t *testing.T) {
589587
// This test verifies that initializeState() invokes Replayer.
590588
// The detailed replay logic is tested in block/internal/common/replay_test.go
591589

590+
ds := dssync.MutexWrap(datastore.NewMapDatastore())
591+
st := store.New(ds)
592+
cm, err := cache.NewManager(config.DefaultConfig(), st, zerolog.Nop())
593+
require.NoError(t, err)
594+
592595
// Create mocks
593596
mockStore := testmocks.NewMockStore(t)
594597
mockExec := testmocks.NewMockHeightAwareExecutor(t)
@@ -627,17 +630,17 @@ func TestSyncer_InitializeState_CallsReplayer(t *testing.T) {
627630
daHeight: &atomic.Uint64{},
628631
logger: zerolog.Nop(),
629632
ctx: context.Background(),
633+
cache: cm,
630634
}
631635

632636
// Initialize state - this should call Replayer
633-
err := syncer.initializeState()
637+
err = syncer.initializeState()
634638
require.NoError(t, err)
635639

636640
// Verify state was initialized correctly
637641
state := syncer.GetLastState()
638642
assert.Equal(t, storeHeight, state.LastBlockHeight)
639643
assert.Equal(t, gen.ChainID, state.ChainID)
640-
assert.Equal(t, uint64(5), syncer.GetDAHeight())
641644

642645
// Verify that GetLatestHeight was called (proves Replayer was invoked)
643646
mockExec.AssertCalled(t, "GetLatestHeight", mock.Anything)

tools/da-debug/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ da-debug search 100 "0x000000000000000000000000000000000000000000000000000000746
7474
All commands support these global flags:
7575

7676
<!-- markdown-link-check-disable -->
77-
- `--da-url string`: DA layer JSON-RPC URL (default: "http://localhost:7980")
77+
- `--da-url string`: DA layer JSON-RPC URL (default: "<http://localhost:7980>")
7878
<!-- markdown-link-check-enable -->
7979
- `--auth-token string`: Authentication token for DA layer
8080
- `--timeout duration`: Request timeout (default: 30s)

0 commit comments

Comments
 (0)