Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Resolve invalid snapshot pruning #791

Merged
merged 1 commit into from
Oct 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
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
44 changes: 40 additions & 4 deletions validators/store/snapshot/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,47 @@ func (s *snapshotStore) deleteLower(num uint64) {
s.Lock()
defer s.Unlock()

i := sort.Search(len(s.list), func(i int) bool {
return s.list[i].Number >= num
})
pruneIndex := s.findClosestSnapshotIndex(num)
s.list = s.list[pruneIndex:]
}

// findClosestSnapshotIndex finds the closest snapshot index for the specified
// block number
func (s *snapshotStore) findClosestSnapshotIndex(blockNum uint64) int {
// Check if the block number is lower than the highest saved snapshot
if blockNum < s.list[0].Number {
return 0
}

// Check if the block number if higher than the highest saved snapshot
if blockNum > s.list[len(s.list)-1].Number {
return len(s.list) - 1
}

var (
low = 0
high = len(s.list) - 1
)

// Find the closest value using binary search
for low <= high {
mid := (high + low) / 2

if blockNum < s.list[mid].Number {
high = mid - 1
} else if blockNum > s.list[mid].Number {
low = mid + 1
} else {
return mid
}
}

// Check which of the two positions is closest (and has a higher block num)
if s.list[low].Number-blockNum < blockNum-s.list[high].Number {
return high
}

s.list = s.list[i:]
return low
}

// find returns the index of the first closest snapshot to the number specified
Expand Down
99 changes: 69 additions & 30 deletions validators/store/snapshot/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1096,41 +1096,80 @@ func Test_snapshotStore_updateLastBlock(t *testing.T) {
func Test_snapshotStore_deleteLower(t *testing.T) {
t.Parallel()

var (
metadata = &SnapshotMetadata{
LastBlock: 10,
}
metadata := &SnapshotMetadata{
LastBlock: 10,
}

snapshots = []*Snapshot{
{Number: 10},
{Number: 19},
{Number: 20},
{Number: 21},
{Number: 30},
}
testTable := []struct {
name string
snapshots []*Snapshot
boundary uint64
expectedSnapshots []*Snapshot
}{
{
"Drop lower-number snapshots",
[]*Snapshot{
{Number: 10},
{Number: 19},
{Number: 25},
{Number: 30},
},
uint64(20),
[]*Snapshot{
{Number: 25},
{Number: 30},
},
},
{
"Higher block value",
[]*Snapshot{
{Number: 10},
{Number: 11},
{Number: 12},
{Number: 13},
{Number: 14},
},
uint64(15),
[]*Snapshot{
{Number: 14},
},
},
{
// Single snapshots shouldn't be dropped
"Single snapshot",
[]*Snapshot{
{Number: 10},
},
uint64(15),
[]*Snapshot{
{Number: 10},
},
},
}

boundary = uint64(20)
)
for _, testCase := range testTable {
testCase := testCase

store := newSnapshotStore(
metadata,
snapshots,
)
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()

store.deleteLower(boundary)
store := newSnapshotStore(
metadata,
testCase.snapshots,
)

assert.Equal(
t,
&snapshotStore{
lastNumber: metadata.LastBlock,
list: []*Snapshot{
{Number: 20},
{Number: 21},
{Number: 30},
},
},
store,
)
store.deleteLower(testCase.boundary)

assert.Equal(
t,
&snapshotStore{
lastNumber: metadata.LastBlock,
list: testCase.expectedSnapshots,
},
store,
)
})
}
}

func Test_snapshotStore_find(t *testing.T) {
Expand Down