diff --git a/epochStart/bootstrap/disabled/disabledAccountsAdapter.go b/epochStart/bootstrap/disabled/disabledAccountsAdapter.go index da86e186d00..88569525b46 100644 --- a/epochStart/bootstrap/disabled/disabledAccountsAdapter.go +++ b/epochStart/bootstrap/disabled/disabledAccountsAdapter.go @@ -17,6 +17,10 @@ func NewAccountsAdapter() *accountsAdapter { return &accountsAdapter{} } +// StartSnapshotIfNeeded - +func (a *accountsAdapter) StartSnapshotIfNeeded() { +} + // GetTrie - func (a *accountsAdapter) GetTrie(_ []byte) (common.Trie, error) { return nil, nil diff --git a/node/nodeRunner.go b/node/nodeRunner.go index cd412ef077e..3ab174b9251 100644 --- a/node/nodeRunner.go +++ b/node/nodeRunner.go @@ -384,6 +384,14 @@ func (nr *nodeRunner) executeOneComponentCreationCycle( return true, err } + selfId := managedBootstrapComponents.ShardCoordinator().SelfId() + if selfId == core.MetachainShardId { + managedStateComponents.PeerAccounts().StartSnapshotIfNeeded() + managedStateComponents.AccountsAdapter().StartSnapshotIfNeeded() + } else { + managedStateComponents.AccountsAdapter().StartSnapshotIfNeeded() + } + hardforkTrigger := managedProcessComponents.HardforkTrigger() err = hardforkTrigger.AddCloser(nodesShufflerOut) if err != nil { diff --git a/process/txsimulator/wrappedAccountsDB.go b/process/txsimulator/wrappedAccountsDB.go index 856e9495614..8f34ed877bd 100644 --- a/process/txsimulator/wrappedAccountsDB.go +++ b/process/txsimulator/wrappedAccountsDB.go @@ -24,6 +24,10 @@ func NewReadOnlyAccountsDB(accountsDB state.AccountsAdapter) (*readOnlyAccountsD return &readOnlyAccountsDB{originalAccounts: accountsDB}, nil } +// StartSnapshotIfNeeded does nothing for this implementation +func (r *readOnlyAccountsDB) StartSnapshotIfNeeded() { +} + // GetCode returns the code for the given account func (r *readOnlyAccountsDB) GetCode(codeHash []byte) []byte { return r.originalAccounts.GetCode(codeHash) diff --git a/state/accountsDB.go b/state/accountsDB.go index dc7a130e5cf..f4994e029d7 100644 --- a/state/accountsDB.go +++ b/state/accountsDB.go @@ -128,12 +128,6 @@ func NewAccountsDB(args ArgsAccountsDB) (*AccountsDB, error) { processStatusHandler: args.ProcessStatusHandler, } - trieStorageManager := adb.mainTrie.GetStorageManager() - val, err := trieStorageManager.GetFromCurrentEpoch([]byte(common.ActiveDBKey)) - if err != nil || !bytes.Equal(val, []byte(common.ActiveDBVal)) { - startSnapshotAfterRestart(adb, args) - } - return adb, nil } @@ -160,17 +154,16 @@ func checkArgsAccountsDB(args ArgsAccountsDB) error { return nil } -func startSnapshotAfterRestart(adb AccountsAdapter, args ArgsAccountsDB) { - tsm := args.Trie.GetStorageManager() +func startSnapshotAfterRestart(adb AccountsAdapter, tsm common.StorageManager, processingMode common.NodeProcessingMode) { epoch, err := tsm.GetLatestStorageEpoch() if err != nil { log.Error("could not get latest storage epoch") } putActiveDBMarker := epoch == 0 && err == nil - isInImportDBMode := args.ProcessingMode == common.ImportDb + isInImportDBMode := processingMode == common.ImportDb putActiveDBMarker = putActiveDBMarker || isInImportDBMode if putActiveDBMarker { - log.Debug("marking activeDB", "epoch", epoch, "error", err, "processing mode", args.ProcessingMode) + log.Debug("marking activeDB", "epoch", epoch, "error", err, "processing mode", processingMode) err = tsm.Put([]byte(common.ActiveDBKey), []byte(common.ActiveDBVal)) handleLoggingWhenError("error while putting active DB value into main storer", err) return @@ -204,6 +197,22 @@ func handleLoggingWhenError(message string, err error, extraArguments ...interfa log.Warn(message, append(args, extraArguments...)...) } +// StartSnapshotIfNeeded starts the snapshot if the previous snapshot process was not fully completed +func (adb *AccountsDB) StartSnapshotIfNeeded() { + startSnapshotIfNeeded(adb, adb.mainTrie.GetStorageManager(), adb.processingMode) +} + +func startSnapshotIfNeeded( + adb AccountsAdapter, + trieStorageManager common.StorageManager, + processingMode common.NodeProcessingMode, +) { + val, err := trieStorageManager.GetFromCurrentEpoch([]byte(common.ActiveDBKey)) + if err != nil || !bytes.Equal(val, []byte(common.ActiveDBVal)) { + startSnapshotAfterRestart(adb, trieStorageManager, processingMode) + } +} + // GetCode returns the code for the given account func (adb *AccountsDB) GetCode(codeHash []byte) []byte { if len(codeHash) == 0 { diff --git a/state/accountsDBApi.go b/state/accountsDBApi.go index e5b32a9a62e..8bd4599b03f 100644 --- a/state/accountsDBApi.go +++ b/state/accountsDBApi.go @@ -75,6 +75,10 @@ func (accountsDB *accountsDBApi) doRecreateTrieWithBlockInfo(newBlockInfo common return newBlockInfo, nil } +// StartSnapshotIfNeeded does nothing for this implementation +func (accountsDB *accountsDBApi) StartSnapshotIfNeeded() { +} + // GetExistingAccount will call the inner accountsAdapter method after trying to recreate the trie func (accountsDB *accountsDBApi) GetExistingAccount(address []byte) (vmcommon.AccountHandler, error) { account, _, err := accountsDB.GetAccountWithBlockInfo(address, holders.NewRootHashHolderAsEmpty()) diff --git a/state/accountsDBApiWithHistory.go b/state/accountsDBApiWithHistory.go index df8125acc78..fb924227be8 100644 --- a/state/accountsDBApiWithHistory.go +++ b/state/accountsDBApiWithHistory.go @@ -29,6 +29,10 @@ func NewAccountsDBApiWithHistory(innerAccountsAdapter AccountsAdapter) (*account }, nil } +// StartSnapshotIfNeeded is a not permitted operation in this implementation and thus, does nothing +func (accountsDB *accountsDBApiWithHistory) StartSnapshotIfNeeded() { +} + // GetExistingAccount will return an error func (accountsDB *accountsDBApiWithHistory) GetExistingAccount(_ []byte) (vmcommon.AccountHandler, error) { return nil, ErrFunctionalityNotImplemented diff --git a/state/accountsDB_test.go b/state/accountsDB_test.go index 84058529f47..0433674cbc0 100644 --- a/state/accountsDB_test.go +++ b/state/accountsDB_test.go @@ -12,7 +12,6 @@ import ( "time" "github.com/ElrondNetwork/elrond-go-core/core" - atomicFlag "github.com/ElrondNetwork/elrond-go-core/core/atomic" "github.com/ElrondNetwork/elrond-go-core/core/check" "github.com/ElrondNetwork/elrond-go-core/marshal" "github.com/ElrondNetwork/elrond-go/common" @@ -2359,136 +2358,6 @@ func TestAccountsDB_GetAccountFromBytesShouldLoadDataTrie(t *testing.T) { assert.Equal(t, dataTrie, account.DataTrie()) } -func TestAccountsDB_NewAccountsDbShouldSetActiveDB(t *testing.T) { - t.Parallel() - - rootHash := []byte("rootHash") - expectedErr := errors.New("expected error") - t.Run("epoch 0", func(t *testing.T) { - putCalled := false - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - ShouldTakeSnapshotCalled: func() bool { - return true - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 0, nil - }, - PutCalled: func(key []byte, val []byte) error { - assert.Equal(t, []byte(common.ActiveDBKey), key) - assert.Equal(t, []byte(common.ActiveDBVal), val) - - putCalled = true - - return nil - }, - } - }, - } - - _ = generateAccountDBFromTrie(trieStub) - - assert.True(t, putCalled) - }) - t.Run("epoch 0, GetLatestStorageEpoch errors should not put", func(t *testing.T) { - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - ShouldTakeSnapshotCalled: func() bool { - return true - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 0, expectedErr - }, - PutCalled: func(key []byte, val []byte) error { - assert.Fail(t, "should have not called put") - - return nil - }, - } - }, - } - - _ = generateAccountDBFromTrie(trieStub) - }) - t.Run("in import DB mode", func(t *testing.T) { - putCalled := false - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - ShouldTakeSnapshotCalled: func() bool { - return true - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 1, nil - }, - PutCalled: func(key []byte, val []byte) error { - assert.Equal(t, []byte(common.ActiveDBKey), key) - assert.Equal(t, []byte(common.ActiveDBVal), val) - - putCalled = true - - return nil - }, - } - }, - } - - args := createMockAccountsDBArgs() - args.ProcessingMode = common.ImportDb - args.Trie = trieStub - - _, _ = state.NewAccountsDB(args) - - assert.True(t, putCalled) - }) -} - -func TestAccountsDB_NewAccountsDbStartsSnapshotAfterRestart(t *testing.T) { - t.Parallel() - - rootHash := []byte("rootHash") - takeSnapshotCalled := atomicFlag.Flag{} - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - GetCalled: func(key []byte) ([]byte, error) { - if bytes.Equal(key, []byte(common.ActiveDBKey)) { - return nil, fmt.Errorf("key not found") - } - return []byte("rootHash"), nil - }, - ShouldTakeSnapshotCalled: func() bool { - return true - }, - TakeSnapshotCalled: func(_ []byte, _ []byte, _ chan core.KeyValueHolder, _ chan error, _ common.SnapshotStatisticsHandler, _ uint32) { - takeSnapshotCalled.SetValue(true) - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 1, nil - }, - } - }, - } - - _ = generateAccountDBFromTrie(trieStub) - time.Sleep(time.Second) - assert.True(t, takeSnapshotCalled.IsSet()) -} - func BenchmarkAccountsDb_GetCodeEntry(b *testing.B) { maxTrieLevelInMemory := uint(5) marshaller := &testscommon.MarshalizerMock{} diff --git a/state/interface.go b/state/interface.go index 54d4e9d9528..fdf44ba53e9 100644 --- a/state/interface.go +++ b/state/interface.go @@ -125,6 +125,7 @@ type AccountsAdapter interface { RecreateAllTries(rootHash []byte) (map[string]common.Trie, error) GetTrie(rootHash []byte) (common.Trie, error) GetStackDebugFirstEntry() []byte + StartSnapshotIfNeeded() Close() error IsInterfaceNil() bool } diff --git a/state/peerAccountsDB.go b/state/peerAccountsDB.go index c90e0053097..3b3020665db 100644 --- a/state/peerAccountsDB.go +++ b/state/peerAccountsDB.go @@ -1,7 +1,6 @@ package state import ( - "bytes" "fmt" "sync" @@ -39,15 +38,14 @@ func NewPeerAccountsDB(args ArgsAccountsDB) (*PeerAccountsDB, error) { }, } - trieStorageManager := adb.mainTrie.GetStorageManager() - val, err := trieStorageManager.GetFromCurrentEpoch([]byte(common.ActiveDBKey)) - if err != nil || !bytes.Equal(val, []byte(common.ActiveDBVal)) { - startSnapshotAfterRestart(adb, args) - } - return adb, nil } +// StartSnapshotIfNeeded starts the snapshot if the previous snapshot process was not fully completed +func (adb *PeerAccountsDB) StartSnapshotIfNeeded() { + startSnapshotIfNeeded(adb, adb.mainTrie.GetStorageManager(), adb.processingMode) +} + // MarkSnapshotDone will mark that the snapshot process has been completed func (adb *PeerAccountsDB) MarkSnapshotDone() { trieStorageManager, epoch, err := adb.getTrieStorageManagerAndLatestEpoch() diff --git a/state/peerAccountsDB_test.go b/state/peerAccountsDB_test.go index 765ea67283d..507d70fe7c0 100644 --- a/state/peerAccountsDB_test.go +++ b/state/peerAccountsDB_test.go @@ -1,7 +1,6 @@ package state_test import ( - "bytes" "errors" "fmt" "sync" @@ -187,51 +186,6 @@ func TestNewPeerAccountsDB_RecreateAllTries(t *testing.T) { assert.True(t, recreateCalled) } -func TestPeerAccountsDB_NewAccountsDbStartsSnapshotAfterRestart(t *testing.T) { - t.Parallel() - - rootHash := []byte("rootHash") - mutex := sync.RWMutex{} - takeSnapshotCalled := false - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - GetCalled: func(key []byte) ([]byte, error) { - if bytes.Equal(key, []byte(common.ActiveDBKey)) { - return nil, fmt.Errorf("key not found") - } - return []byte("rootHash"), nil - }, - ShouldTakeSnapshotCalled: func() bool { - return true - }, - TakeSnapshotCalled: func(_ []byte, _ []byte, _ chan core.KeyValueHolder, _ chan error, _ common.SnapshotStatisticsHandler, _ uint32) { - mutex.Lock() - takeSnapshotCalled = true - mutex.Unlock() - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 1, nil - }, - } - }, - } - - args := createMockAccountsDBArgs() - args.Trie = trieStub - adb, err := state.NewPeerAccountsDB(args) - assert.Nil(t, err) - assert.NotNil(t, adb) - - time.Sleep(time.Second) - mutex.RLock() - assert.True(t, takeSnapshotCalled) - mutex.RUnlock() -} - func TestPeerAccountsDB_MarkSnapshotDone(t *testing.T) { t.Parallel() @@ -321,104 +275,6 @@ func TestPeerAccountsDB_MarkSnapshotDone(t *testing.T) { } -func TestPeerAccountsDB_NewAccountsDbShouldSetActiveDB(t *testing.T) { - t.Parallel() - - rootHash := []byte("rootHash") - expectedErr := errors.New("expected error") - t.Run("epoch 0", func(t *testing.T) { - putCalled := false - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - ShouldTakeSnapshotCalled: func() bool { - return true - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 0, nil - }, - PutCalled: func(key []byte, val []byte) error { - assert.Equal(t, []byte(common.ActiveDBKey), key) - assert.Equal(t, []byte(common.ActiveDBVal), val) - - putCalled = true - - return nil - }, - } - }, - } - - args := createMockAccountsDBArgs() - args.Trie = trieStub - _, _ = state.NewPeerAccountsDB(args) - - assert.True(t, putCalled) - }) - t.Run("epoch 0, GetLatestStorageEpoch errors should not put", func(t *testing.T) { - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - ShouldTakeSnapshotCalled: func() bool { - return true - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 0, expectedErr - }, - PutCalled: func(key []byte, val []byte) error { - assert.Fail(t, "should have not called put") - - return nil - }, - } - }, - } - - args := createMockAccountsDBArgs() - args.Trie = trieStub - _, _ = state.NewPeerAccountsDB(args) - }) - t.Run("in import DB mode", func(t *testing.T) { - putCalled := false - trieStub := &trieMock.TrieStub{ - RootCalled: func() ([]byte, error) { - return rootHash, nil - }, - GetStorageManagerCalled: func() common.StorageManager { - return &testscommon.StorageManagerStub{ - ShouldTakeSnapshotCalled: func() bool { - return true - }, - GetLatestStorageEpochCalled: func() (uint32, error) { - return 1, nil - }, - PutCalled: func(key []byte, val []byte) error { - assert.Equal(t, []byte(common.ActiveDBKey), key) - assert.Equal(t, []byte(common.ActiveDBVal), val) - - putCalled = true - - return nil - }, - } - }, - } - - args := createMockAccountsDBArgs() - args.ProcessingMode = common.ImportDb - args.Trie = trieStub - _, _ = state.NewPeerAccountsDB(args) - - assert.True(t, putCalled) - }) -} - func TestPeerAccountsDB_SnapshotStateOnAClosedStorageManagerShouldNotMarkActiveDB(t *testing.T) { t.Parallel() diff --git a/testscommon/state/accountsAdapterStub.go b/testscommon/state/accountsAdapterStub.go index 908941b5cb3..d59708d9dea 100644 --- a/testscommon/state/accountsAdapterStub.go +++ b/testscommon/state/accountsAdapterStub.go @@ -39,6 +39,14 @@ type AccountsStub struct { GetAccountWithBlockInfoCalled func(address []byte, options common.RootHashHolder) (vmcommon.AccountHandler, common.BlockInfo, error) GetCodeWithBlockInfoCalled func(codeHash []byte, options common.RootHashHolder) ([]byte, common.BlockInfo, error) CloseCalled func() error + StartSnapshotIfNeededCalled func() +} + +// StartSnapshotIfNeeded - +func (as *AccountsStub) StartSnapshotIfNeeded() { + if as.StartSnapshotIfNeededCalled != nil { + as.StartSnapshotIfNeededCalled() + } } // GetTrie -