Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion block/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,9 @@
return nil, err
}

if s.DAHeight < config.DA.StartHeight {
// Only use config.DA.StartHeight for fresh initialization (when DAHeight is 0)
// This allows resuming from the last queried DA height on restart
if s.DAHeight == 0 && config.DA.StartHeight > 0 {
s.DAHeight = config.DA.StartHeight
}

Expand Down Expand Up @@ -459,6 +461,27 @@
return m.lastState
}

// persistDAHeight updates the DAHeight in the persistent state.
// This ensures that the last queried DA height is preserved across restarts.
func (m *Manager) persistDAHeight(ctx context.Context, newDAHeight uint64) error {
m.lastStateMtx.Lock()
defer m.lastStateMtx.Unlock()

Check failure on line 469 in block/manager.go

View workflow job for this annotation

GitHub Actions / lint / golangci-lint

File is not properly formatted (gofmt)
// Create an updated state with the new DA height
updatedState := m.lastState
updatedState.DAHeight = newDAHeight

// Persist the updated state
err := m.store.UpdateState(ctx, updatedState)
if err != nil {
return fmt.Errorf("failed to update state with new DA height %d: %w", newDAHeight, err)
}

// Update the in-memory state
m.lastState = updatedState
return nil
}

// GetDAIncludedHeight returns the height at which all blocks have been
// included in the DA
func (m *Manager) GetDAIncludedHeight() uint64 {
Expand Down
8 changes: 7 additions & 1 deletion block/retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@
case blobsFoundCh <- struct{}{}:
default:
}
m.daHeight.Store(daHeight + 1)
newDAHeight := daHeight + 1
m.daHeight.Store(newDAHeight)

Check failure on line 52 in block/retriever.go

View workflow job for this annotation

GitHub Actions / lint / golangci-lint

File is not properly formatted (gofmt)
// Persist the updated DA height in the state to survive restarts
if err := m.persistDAHeight(ctx, newDAHeight); err != nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@claude this is a write per read, is there anyway you can optimise it, even if we write only every 50 or 100 blocks it should be fine since we are not going back thousands of blocks only 100

@claude claude Bot Aug 8, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude finished @tac0turtle's task —— View job


Optimizing DA Height Persistence ✅

I've successfully optimized the DA height persistence to reduce write frequency as requested. The current implementation was writing to disk on every DA query (write per read), but now it batches writes to reduce I/O while maintaining reasonable restart recovery.

Todo List:

  • Read current implementation in changed files
  • Design batched persistence strategy
  • Implement optimized persistence logic
  • Add configuration for batch size
  • Test the changes
  • Run linting and tests

Solution Summary:

🚀 Performance Optimization:

  • Before: Wrote to disk on every DA block processed (every 6 seconds)
  • After: Batches writes every N blocks (default: 100 blocks = ~10 minutes)
  • Result: ~99% reduction in disk writes for DA height persistence

⚙️ Configuration:

  • Added rollkit.da.persist_interval config option (default: 100)
  • Configurable via --rollkit.da.persist_interval flag
  • Set to 50-100 as recommended for balance between performance and recovery

🔒 Safety Features:

  • Persists on context cancellation/shutdown to minimize data loss
  • Maintains separate tracking of lastPersistedDAHeight vs currentDAHeight
  • Maximum rollback on restart is now persist_interval blocks (100 by default)
  • Backward compatible: setting to 1 or 0 gives old behavior

🧪 Testing:

  • Updated all test files to handle new Manager field
  • Updated configuration tests for new option
  • Maintains existing behavior when interval = 1

The optimization addresses your performance concern while keeping the restart recovery within acceptable bounds (~100 blocks maximum rollback vs thousands previously).

m.logger.Error().Err(err).Uint64("newDAHeight", newDAHeight).Msg("failed to persist DA height")
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

There's a potential race condition here. The in-memory atomic DA height (m.daHeight) is updated before persistDAHeight is called. If persistDAHeight fails, the in-memory daHeight will be ahead of the persisted state. In the next loop iteration, the retriever will try to process the next DA height, even though the progress for the previous height wasn't saved.

This can lead to a situation where the node keeps processing new DA heights in-memory while failing to persist its progress. On a restart, it would revert to the last successfully persisted height, potentially causing it to re-process a large number of blocks.

To ensure consistency, the atomic m.daHeight should only be updated after the state has been successfully persisted.

newDAHeight := daHeight + 1

// Persist the updated DA height in the state to survive restarts.
// Only advance the in-memory DA height after successful persistence
// to ensure that we retry the same height if persistence fails.
if err := m.persistDAHeight(ctx, newDAHeight); err != nil {
	m.logger.Error().Err(err).Uint64("newDAHeight", newDAHeight).Msg("failed to persist DA height")
} else {
	m.daHeight.Store(newDAHeight)
}

}
}

Expand Down
Loading