-
Notifications
You must be signed in to change notification settings - Fork 26
feat: Add DeleteRange
method to Store
#347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
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 |
DeleteFromHead
method to StoreDeleteRange
method to Store
There was a problem hiding this 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
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 |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
Thanks! Implementing your suggestions... |
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
Any update on this @Wondertan? |
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++ { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// 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 |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"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), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"took(s)", time.Since(startTime), | |
"took(s)", time.Since(startTime).Seconds(), |
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 |
There was a problem hiding this comment.
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) { |
There was a problem hiding this comment.
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?
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)