Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
State migration from hot to cold (archived) (#5076)
* Starting * Test * Tests * comments Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
- Loading branch information
1 parent
f1a42eb
commit ff4ed41
Showing
4 changed files
with
215 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package stategen | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
|
||
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers" | ||
"github.com/prysmaticlabs/prysm/beacon-chain/db/filters" | ||
"github.com/prysmaticlabs/prysm/beacon-chain/state" | ||
"github.com/prysmaticlabs/prysm/shared/bytesutil" | ||
"github.com/sirupsen/logrus" | ||
"go.opencensus.io/trace" | ||
) | ||
|
||
// MigrateToCold advances the split point in between the cold and hot state sections. | ||
// It moves the recent finalized states from the hot section to the cold section and | ||
// only preserve the ones that's on archived point. | ||
func (s *State) MigrateToCold(ctx context.Context, finalizedState *state.BeaconState, finalizedRoot [32]byte) error { | ||
ctx, span := trace.StartSpan(ctx, "stateGen.MigrateToCold") | ||
defer span.End() | ||
|
||
// Verify migration is sensible. The new finalized point must increase the current split slot, and | ||
// on an epoch boundary for hot state summary scheme to work. | ||
currentSplitSlot := s.splitInfo.slot | ||
if currentSplitSlot > finalizedState.Slot() { | ||
return nil | ||
} | ||
if !helpers.IsEpochStart(finalizedState.Slot()) { | ||
return nil | ||
} | ||
|
||
// Move the states between split slot to finalized slot from hot section to the cold section. | ||
filter := filters.NewFilter().SetStartSlot(currentSplitSlot).SetEndSlot(finalizedState.Slot() - 1) | ||
blockRoots, err := s.beaconDB.BlockRoots(ctx, filter) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, r := range blockRoots { | ||
stateSummary, err := s.beaconDB.StateSummary(ctx, r) | ||
if err != nil { | ||
return err | ||
} | ||
if stateSummary == nil || stateSummary.Slot == 0 { | ||
continue | ||
} | ||
|
||
if stateSummary.Slot%s.slotsPerArchivedPoint == 0 { | ||
archivePointIndex := stateSummary.Slot / s.slotsPerArchivedPoint | ||
if s.beaconDB.HasState(ctx, r) { | ||
hotState, err := s.beaconDB.State(ctx, r) | ||
if err != nil { | ||
return err | ||
} | ||
if err := s.beaconDB.SaveArchivedPointState(ctx, hotState.Copy(), archivePointIndex); err != nil { | ||
return err | ||
} | ||
} else { | ||
hotState, err := s.ComputeStateUpToSlot(ctx, stateSummary.Slot) | ||
if err != nil { | ||
return err | ||
} | ||
if err := s.beaconDB.SaveArchivedPointState(ctx, hotState.Copy(), archivePointIndex); err != nil { | ||
return err | ||
} | ||
} | ||
if err := s.beaconDB.SaveArchivedPointRoot(ctx, r, archivePointIndex); err != nil { | ||
return err | ||
} | ||
|
||
log.WithFields(logrus.Fields{ | ||
"slot": stateSummary.Slot, | ||
"archiveIndex": archivePointIndex, | ||
"root": hex.EncodeToString(bytesutil.Trunc(r[:])), | ||
}).Info("Saved archived point during state migration") | ||
} | ||
|
||
if s.beaconDB.HasState(ctx, r) { | ||
if err := s.beaconDB.DeleteState(ctx, r); err != nil { | ||
return err | ||
} | ||
log.WithFields(logrus.Fields{ | ||
"slot": stateSummary.Slot, | ||
"root": hex.EncodeToString(bytesutil.Trunc(r[:])), | ||
}).Info("Deleted state during migration") | ||
} | ||
|
||
s.deleteEpochBoundaryRoot(stateSummary.Slot) | ||
} | ||
|
||
// Update the split slot and root. | ||
s.splitInfo = &splitSlotAndRoot{slot: finalizedState.Slot(), root: finalizedRoot} | ||
log.WithFields(logrus.Fields{ | ||
"slot": s.splitInfo.slot, | ||
"root": hex.EncodeToString(bytesutil.Trunc(s.splitInfo.root[:])), | ||
}).Info("Set hot and cold state split point") | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package stategen | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
ethpb "github.com/prysmaticlabs/ethereumapis/eth/v1alpha1" | ||
"github.com/prysmaticlabs/go-ssz" | ||
testDB "github.com/prysmaticlabs/prysm/beacon-chain/db/testing" | ||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1" | ||
"github.com/prysmaticlabs/prysm/shared/params" | ||
"github.com/prysmaticlabs/prysm/shared/testutil" | ||
logTest "github.com/sirupsen/logrus/hooks/test" | ||
) | ||
|
||
func TestMigrateToCold_NoBlock(t *testing.T) { | ||
hook := logTest.NewGlobal() | ||
ctx := context.Background() | ||
db := testDB.SetupDB(t) | ||
defer testDB.TeardownDB(t, db) | ||
|
||
service := New(db) | ||
|
||
beaconState, _ := testutil.DeterministicGenesisState(t, 32) | ||
beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch) | ||
if err := service.MigrateToCold(ctx, beaconState, [32]byte{}); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
testutil.AssertLogsContain(t, hook, "Set hot and cold state split point") | ||
} | ||
|
||
func TestMigrateToCold_HigherSplitSlot(t *testing.T) { | ||
hook := logTest.NewGlobal() | ||
ctx := context.Background() | ||
db := testDB.SetupDB(t) | ||
defer testDB.TeardownDB(t, db) | ||
|
||
service := New(db) | ||
service.splitInfo.slot = 2 | ||
|
||
beaconState, _ := testutil.DeterministicGenesisState(t, 32) | ||
beaconState.SetSlot(1) | ||
if err := service.MigrateToCold(ctx, beaconState, [32]byte{}); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
testutil.AssertLogsDoNotContain(t, hook, "Set hot and cold state split point") | ||
} | ||
|
||
func TestMigrateToCold_NotEpochStart(t *testing.T) { | ||
hook := logTest.NewGlobal() | ||
ctx := context.Background() | ||
db := testDB.SetupDB(t) | ||
defer testDB.TeardownDB(t, db) | ||
|
||
service := New(db) | ||
|
||
beaconState, _ := testutil.DeterministicGenesisState(t, 32) | ||
beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch + 1) | ||
if err := service.MigrateToCold(ctx, beaconState, [32]byte{}); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
testutil.AssertLogsDoNotContain(t, hook, "Set hot and cold state split point") | ||
} | ||
|
||
func TestMigrateToCold_MigrationCompletes(t *testing.T) { | ||
hook := logTest.NewGlobal() | ||
ctx := context.Background() | ||
db := testDB.SetupDB(t) | ||
defer testDB.TeardownDB(t, db) | ||
|
||
service := New(db) | ||
|
||
beaconState, _ := testutil.DeterministicGenesisState(t, 32) | ||
beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch) | ||
b := ðpb.SignedBeaconBlock{ | ||
Block: ðpb.BeaconBlock{Slot: 2}, | ||
} | ||
if err := service.beaconDB.SaveBlock(ctx, b); err != nil { | ||
t.Fatal(err) | ||
} | ||
bRoot, _ := ssz.HashTreeRoot(b.Block) | ||
if err := service.beaconDB.SaveStateSummary(ctx, &pb.StateSummary{Root: bRoot[:], Slot: 2}); err != nil { | ||
t.Fatal(err) | ||
} | ||
if err := service.beaconDB.SaveState(ctx, beaconState, bRoot); err != nil { | ||
t.Fatal(err) | ||
} | ||
service.slotsPerArchivedPoint = 2 // Ensure we can land on archived point. | ||
|
||
if err := service.MigrateToCold(ctx, beaconState, [32]byte{}); err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
if !service.beaconDB.HasArchivedPoint(ctx, 1) { | ||
t.Error("Did not preserve archived point") | ||
} | ||
|
||
testutil.AssertLogsContain(t, hook, "Saved archived point during state migration") | ||
testutil.AssertLogsContain(t, hook, "Deleted state during migration") | ||
testutil.AssertLogsContain(t, hook, "Set hot and cold state split point") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters