Skip to content

Conversation

julienrbrt
Copy link

@julienrbrt julienrbrt commented Sep 9, 2025

Overview

d
Implement DeleteRange on store.
This methods truncates the head to the given height. (different from DeleteTo that truncates the tail to the given height)

@Wondertan
Copy link
Member

Wondertan commented Sep 9, 2025

One thing we should explore is making DeleteRange [from:to) instead of introducing a new method. It's gonna have better ergonomics, allow both usecase, and have less duplicated code and easier to maintain longer term.

We actually wanted to make it DeleteRange from the start, but then we selfishly moved towards DeleteTo because deleting from Tail is all we needed. This is the exact case where we should have gone for a more general solution it turns out.

@julienrbrt julienrbrt changed the title feat: Add DeleteFromHead method to Store feat: Add DeleteRange method to Store Sep 9, 2025
@julienrbrt julienrbrt marked this pull request as ready for review September 9, 2025 13:14
Copy link
Member

@Wondertan Wondertan left a comment

Choose a reason for hiding this comment

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

First pass.

I still need to sit down and verify new logic of DeleteRange

Comment on lines +395 to +411
deleteCtx := ctx
if deadline, ok := ctx.Deadline(); ok {
// allocate 95% of caller's set deadline for deletion
// and give leftover to save progress
sub := deadline.Sub(startTime) / 100 * 95
var cancel context.CancelFunc
deleteCtx, cancel = context.WithDeadlineCause(ctx, startTime.Add(sub), errDeleteTimeout)
defer cancel()
}

if to-from < deleteRangeParallelThreshold {
height, missing, err = s.deleteSequential(deleteCtx, from, to)
} else {
height, missing, err = s.deleteParallel(deleteCtx, from, to)
}

return err
Copy link
Member

Choose a reason for hiding this comment

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

Previously, updateTail was in the defer of the same function, so even if an error occurred, the partial progress was saved. This is no longer true as the call deleteRangeRaw short-circuits on error.

Copy link
Member

Choose a reason for hiding this comment

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

should we return from this method the actual values that were successfully deleted to?

@julienrbrt
Copy link
Author

Thanks! Implementing your suggestions...

github-merge-queue bot pushed a commit to evstack/ev-node that referenced this pull request Sep 17, 2025
github-merge-queue bot pushed a commit to evstack/ev-node that referenced this pull request Sep 17, 2025
This PR was blocked but got merged:
#2638.
It isn't bad to merge it, but we are relying on a fork of go-header for
the apps until celestiaorg/go-header#347 is
merged.
Let's delete it in the main go.mod
@julienrbrt
Copy link
Author

Any update on this @Wondertan?

@Wondertan
Copy link
Member

We qim to get it done by this week

for h := m.TailHeight; h < to; h++ {
func (m *Store[H]) DeleteRange(ctx context.Context, from, to uint64) error {
// Delete headers in the range [from:to)
for h := from; h < to; h++ {
Copy link
Member

Choose a reason for hiding this comment

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

let's just do quick sanity even in testing pkg to make sure from < to

}

// Update HeadHeight if we deleted from the end
if to >= m.HeadHeight {
Copy link
Member

Choose a reason for hiding this comment

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

fail out here if to > m.HeadHeight (we should catch this err case in tests)

Copy link
Member

Choose a reason for hiding this comment

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

Or actually do we have to allow this condition as we allow deletion --> beyond store head (to sync head) ?


// DeleteTo deletes the range [Tail():to).
DeleteTo(ctx context.Context, to uint64) error
// DeleteRange deletes the range [from:to).
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// DeleteRange deletes the range [from:to).
// DeleteRange deletes the range [from:to). It disallows the creation of gaps in the implementation's chain, ensuring contiguity between Tail --> Head.


// if range is empty within the current store bounds, it's a no-op
if from > head.Height() || to <= tail.Height() {
return nil
Copy link
Member

Choose a reason for hiding this comment

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

should we warn log here as passing a no-op range feels like fishy behaviour from the caller.


// Check if we're deleting all existing headers (making store empty)
// Only wipe if 'to' is exactly at head+1 (normal case) to avoid accidental wipes
if from <= tail.Height() && to == head.Height()+1 {
Copy link
Member

Choose a reason for hiding this comment

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

do we allow a case where caller specifies a from that is < tail.Height() ? I don't see why we shouldn't but just want to make sure this isn't some weirdness we should silently overlook.

}

// Update tail if we deleted from the beginning
if updateTail {
Copy link
Member

Choose a reason for hiding this comment

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

what if we wiped store (where to == head.Height+1) ? do we still need to updateTail then? won't it err out trying to get new tail getByHeight ?

"expected_to_height", to,
"actual_to_height", height,
"hdrs_not_found", missing,
"took(s)", time.Since(startTime),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"took(s)", time.Since(startTime),
"took(s)", time.Since(startTime).Seconds(),

"expected_to_height", to,
"actual_to_height", height,
"hdrs_not_found", missing,
"took(s)", time.Since(startTime),
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
"took(s)", time.Since(startTime),
"took(s)", time.Since(startTime).Seconds(),

Comment on lines +395 to +411
deleteCtx := ctx
if deadline, ok := ctx.Deadline(); ok {
// allocate 95% of caller's set deadline for deletion
// and give leftover to save progress
sub := deadline.Sub(startTime) / 100 * 95
var cancel context.CancelFunc
deleteCtx, cancel = context.WithDeadlineCause(ctx, startTime.Add(sub), errDeleteTimeout)
defer cancel()
}

if to-from < deleteRangeParallelThreshold {
height, missing, err = s.deleteSequential(deleteCtx, from, to)
} else {
height, missing, err = s.deleteParallel(deleteCtx, from, to)
}

return err
Copy link
Member

Choose a reason for hiding this comment

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

should we return from this method the actual values that were successfully deleted to?

assert.False(t, has)
}

func TestStore_DeleteRange(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

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

can we please add test cases for partial deletes here (in case of caller ctx timeout or errors) that the store recovers properly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants