From bed8f52aa7e5b6175162abe4434a1e70ebc6dff7 Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Fri, 30 Jun 2023 17:32:32 +0300 Subject: [PATCH 01/13] [WIP] snapshotless observers support --- api/groups/baseNetworkGroup.go | 5 +- cmd/proxy/config/config.toml | 2 + common/options.go | 9 ++ data/observer.go | 20 ++- observer/baseNodeProvider.go | 41 +++++- observer/baseNodeProvider_test.go | 89 +++++++++++- observer/circularQueueNodesProvider.go | 8 +- observer/circularQueueNodesProvider_test.go | 26 ++-- observer/disabledNodesProvider.go | 4 +- observer/errors.go | 3 + observer/interface.go | 4 +- observer/simpleNodesProvider.go | 8 +- observer/simpleNodesProvider_test.go | 12 +- process/aboutInfoProcessor.go | 2 +- process/aboutInfoProcessor_test.go | 6 +- process/accountProcessor.go | 45 ++++-- process/accountProcessor_test.go | 26 ++-- process/baseProcessor.go | 34 ++--- process/baseProcessor_test.go | 26 ++-- process/blockProcessor.go | 8 +- process/blockProcessor_test.go | 130 +++++++++--------- process/blocksProcessor.go | 2 +- process/blocksProcessor_test.go | 6 +- process/economicMetrics.go | 2 +- process/economicMetrics_test.go | 4 +- process/esdtSupplyProcessor.go | 2 +- process/esdtSupplyProcessor_test.go | 4 +- process/factory/interface.go | 12 +- process/interface.go | 12 +- process/mock/observersProviderStub.go | 14 +- process/mock/processorStub.go | 36 ++--- process/nodeGroupProcessor.go | 4 +- process/nodeGroupProcessor_test.go | 18 +-- process/nodeStatusProcessor.go | 24 ++-- process/nodeStatusProcessor_test.go | 60 ++++---- process/proofProcessor.go | 2 +- process/proofProcessor_test.go | 8 +- process/scQueryProcessor.go | 3 +- process/scQueryProcessor_test.go | 10 +- process/transactionProcessor.go | 14 +- process/transactionProcessor_test.go | 52 +++---- process/txcost/transactionCostProcessor.go | 6 +- .../txcost/transactionCostProcessor_test.go | 2 +- process/validatorStatisticsProcessor.go | 2 +- process/validatorStatisticsProcessor_test.go | 8 +- 45 files changed, 486 insertions(+), 329 deletions(-) diff --git a/api/groups/baseNetworkGroup.go b/api/groups/baseNetworkGroup.go index 34f6c2f3..8c4956f5 100644 --- a/api/groups/baseNetworkGroup.go +++ b/api/groups/baseNetworkGroup.go @@ -8,7 +8,6 @@ import ( "github.com/multiversx/mx-chain-proxy-go/api/errors" "github.com/multiversx/mx-chain-proxy-go/api/shared" "github.com/multiversx/mx-chain-proxy-go/data" - "github.com/multiversx/mx-chain-proxy-go/process" ) type networkGroup struct { @@ -55,7 +54,7 @@ func NewNetworkGroup(facadeHandler data.FacadeHandler) (*networkGroup, error) { func (group *networkGroup) getNetworkStatusData(c *gin.Context) { shardIDUint, err := shared.FetchShardIDFromRequest(c) if err != nil { - shared.RespondWith(c, http.StatusBadRequest, nil, process.ErrInvalidShardId.Error(), data.ReturnCodeRequestError) + shared.RespondWith(c, http.StatusBadRequest, nil, errors.ErrInvalidShardIDParam.Error(), data.ReturnCodeRequestError) return } @@ -204,7 +203,7 @@ func (group *networkGroup) getGasConfigs(c *gin.Context) { func (group *networkGroup) getTrieStatistics(c *gin.Context) { shardID, err := shared.FetchShardIDFromRequest(c) if err != nil { - shared.RespondWith(c, http.StatusBadRequest, nil, process.ErrInvalidShardId.Error(), data.ReturnCodeRequestError) + shared.RespondWith(c, http.StatusBadRequest, nil, errors.ErrInvalidShardIDParam.Error(), data.ReturnCodeRequestError) return } diff --git a/cmd/proxy/config/config.toml b/cmd/proxy/config/config.toml index d61623a5..1ff36457 100644 --- a/cmd/proxy/config/config.toml +++ b/cmd/proxy/config/config.toml @@ -65,9 +65,11 @@ # List of Observers. If you want to define a metachain observer (needed for validator statistics route) use # shard id 4294967295 # Fallback observers which are only used when regular ones are offline should have IsFallback = true +# Snapshotless observers are observers that can only respond to real-time requests, such as vm queries. They should have IsSnapshotless = true [[Observers]] ShardId = 0 Address = "http://127.0.0.1:8081" + IsSnapshotless = true [[Observers]] ShardId = 1 diff --git a/common/options.go b/common/options.go index 59885ad7..48cd999a 100644 --- a/common/options.go +++ b/common/options.go @@ -111,6 +111,15 @@ type AccountQueryOptions struct { HintEpoch core.OptionalUint32 } +// AreHistoricalCoordinatesSet returns true if historical block coordinates are set +func (a AccountQueryOptions) AreHistoricalCoordinatesSet() bool { + return a.BlockNonce.HasValue || + a.OnStartOfEpoch.HasValue || + a.HintEpoch.HasValue || + len(a.BlockHash) > 0 || + len(a.BlockRootHash) > 0 +} + // BuildUrlWithAccountQueryOptions builds an URL with block query parameters func BuildUrlWithAccountQueryOptions(path string, options AccountQueryOptions) string { u := url.URL{Path: path} diff --git a/data/observer.go b/data/observer.go index 7d3620d8..f87b075d 100644 --- a/data/observer.go +++ b/data/observer.go @@ -2,10 +2,11 @@ package data // NodeData holds an observer data type NodeData struct { - ShardId uint32 - Address string - IsSynced bool - IsFallback bool + ShardId uint32 + Address string + IsSynced bool + IsFallback bool + IsSnapshotless bool } // NodesReloadResponse is a DTO that holds details about nodes reloading @@ -25,3 +26,14 @@ const ( // FullHistoryNode identifier a node that has full history mode enabled FullHistoryNode NodeType = "full history" ) + +// ObserverDataAvailabilityType represents the type to be used for the observers' data availability +type ObserverDataAvailabilityType string + +const ( + // AvailabilityAll mean that the observer can be used for both real-time and historical requests + AvailabilityAll ObserverDataAvailabilityType = "all" + + // AvailabilityRecent means that the observer can be used only for recent data + AvailabilityRecent ObserverDataAvailabilityType = "recent" +) diff --git a/observer/baseNodeProvider.go b/observer/baseNodeProvider.go index 07f475df..121bcebd 100644 --- a/observer/baseNodeProvider.go +++ b/observer/baseNodeProvider.go @@ -29,10 +29,18 @@ func (bnp *baseNodeProvider) initNodes(nodes []*data.NodeData) error { newNodes := make(map[uint32][]*data.NodeData) for _, observer := range nodes { + if observer.IsFallback && observer.IsSnapshotless { + return ErrObserverCannotBeBothFallbackAndSnapshotless + } shardId := observer.ShardId newNodes[shardId] = append(newNodes[shardId], observer) } + err := checkNodesInShards(newNodes) + if err != nil { + return err + } + bnp.mutNodes.Lock() bnp.shardIds = getSortedShardIDsSlice(newNodes) bnp.syncedNodes, bnp.syncedFallbackNodes = initAllNodesSlice(newNodes) @@ -44,6 +52,23 @@ func (bnp *baseNodeProvider) initNodes(nodes []*data.NodeData) error { return nil } +func checkNodesInShards(nodes map[uint32][]*data.NodeData) error { + for shardID, nodesInShard := range nodes { + atLeastOneRegularNode := false + for _, node := range nodesInShard { + if !node.IsSnapshotless { + atLeastOneRegularNode = true + break + } + } + if !atLeastOneRegularNode { + return fmt.Errorf("observers for shard %d must include at least one historical (non-snapshotless) observer", shardID) + } + } + + return nil +} + // GetAllNodesWithSyncState will return the merged list of active observers and out of sync observers func (bnp *baseNodeProvider) GetAllNodesWithSyncState() []*data.NodeData { bnp.mutNodes.RLock() @@ -415,12 +440,18 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo } } -func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32) ([]*data.NodeData, error) { +func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { syncedNodes := make([]*data.NodeData, 0) for _, node := range bnp.syncedNodes { - if node.ShardId == shardId { - syncedNodes = append(syncedNodes, node) + if node.ShardId != shardId { + continue + } + // TODO: analyze if only snapshotless observers should be returned for recent availability endpoints + // currently, we only restrict snapshotless observers not to be used for historical requests + if dataAvailability == data.AvailabilityAll && node.IsSnapshotless { + continue } + syncedNodes = append(syncedNodes, node) } if len(syncedNodes) != 0 { return syncedNodes, nil @@ -443,10 +474,10 @@ func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32) ( return nil, ErrShardNotAvailable } -func (bnp *baseNodeProvider) getSyncedNodesUnprotected() ([]*data.NodeData, error) { +func (bnp *baseNodeProvider) getSyncedNodesUnprotected(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { syncedNodes := make([]*data.NodeData, 0) for _, shardId := range bnp.shardIds { - syncedShardNodes, err := bnp.getSyncedNodesForShardUnprotected(shardId) + syncedShardNodes, err := bnp.getSyncedNodesForShardUnprotected(shardId, dataAvailability) if err != nil { return nil, fmt.Errorf("%w for shard %d", err, shardId) } diff --git a/observer/baseNodeProvider_test.go b/observer/baseNodeProvider_test.go index 440622f2..fe743709 100644 --- a/observer/baseNodeProvider_test.go +++ b/observer/baseNodeProvider_test.go @@ -16,6 +16,58 @@ import ( // full history observers const configurationPath = "testdata/config.toml" +func TestBaseNodeProvider_InvalidNodesConfiguration(t *testing.T) { + t.Parallel() + + t.Run("node both snapshotless and fallback", func(t *testing.T) { + t.Parallel() + + nodes := []*data.NodeData{ + { + Address: "addr", + ShardId: 0, + IsFallback: true, + IsSnapshotless: true, + }, + } + + bnp := baseNodeProvider{} + err := bnp.initNodes(nodes) + require.Equal(t, ErrObserverCannotBeBothFallbackAndSnapshotless, err) + }) + + t.Run("nodes in shard should not be snapshotless only", func(t *testing.T) { + t.Parallel() + + nodes := []*data.NodeData{ + { + Address: "addr0", + ShardId: 0, + IsSnapshotless: false, + }, + { + Address: "addr1", + ShardId: 0, + IsSnapshotless: true, + }, + { + Address: "addr2", + ShardId: 1, + IsSnapshotless: true, + }, + { + Address: "addr3", + ShardId: 1, + IsSnapshotless: true, + }, + } + + bnp := baseNodeProvider{} + err := bnp.initNodes(nodes) + require.Contains(t, err.Error(), "observers for shard 1 must include at least one historical (non-snapshotless) observer") + }) +} + func TestBaseNodeProvider_ReloadNodesDifferentNumberOfNewShard(t *testing.T) { bnp := &baseNodeProvider{ configurationFilePath: configurationPath, @@ -307,7 +359,7 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldNotRemoveNodeIfNotEno {Address: "addr2", ShardId: 1, IsSynced: false}, }, convertAndSortSlice(bnp.outOfSyncNodes)) - syncedNodes, err := bnp.getSyncedNodesUnprotected() + syncedNodes, err := bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: true}, @@ -329,7 +381,7 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldNotRemoveNodeIfNotEno require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) require.Equal(t, data.NodeData{Address: "addr3", ShardId: 1, IsSynced: false}, *bnp.lastSyncedNodes[1]) - syncedNodes, err = bnp.getSyncedNodesUnprotected() + syncedNodes, err = bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: false}, @@ -364,7 +416,7 @@ func TestBaseNodeProvider_getSyncedNodesUnprotectedShouldWork(t *testing.T) { {Address: "addr0", ShardId: 0, IsSynced: false}, }, convertAndSortSlice(bnp.outOfSyncNodes)) - syncedNodes, err := bnp.getSyncedNodesUnprotected() + syncedNodes, err := bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: true}, @@ -387,7 +439,7 @@ func TestBaseNodeProvider_getSyncedNodesUnprotectedShouldWork(t *testing.T) { }, convertAndSortSlice(bnp.outOfSyncNodes)) require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - syncedNodes, err = bnp.getSyncedNodesUnprotected() + syncedNodes, err = bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: false}, @@ -905,6 +957,35 @@ func convertAndSortSlice(nodes []*data.NodeData) []data.NodeData { return newSlice } +func TestBaseNodeProvider_GetNodesShouldWorkAccordingToTheAvailability(t *testing.T) { + t.Parallel() + + nodes := []*data.NodeData{ + { + Address: "addr0", + ShardId: 1, + IsSnapshotless: true, + }, + { + Address: "addr1", + ShardId: 1, + IsSnapshotless: false, + }, + } + bnp := &baseNodeProvider{ + syncedNodes: nodes, + } + + returnedNodes, err := bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) + require.NoError(t, err) + require.Equal(t, "addr0", returnedNodes[0].Address) + require.Equal(t, "addr1", returnedNodes[1].Address) + + returnedNodes, err = bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityAll) + require.NoError(t, err) + require.Equal(t, "addr1", returnedNodes[0].Address) +} + func TestComputeSyncAndOutOfSyncNodes(t *testing.T) { t.Parallel() diff --git a/observer/circularQueueNodesProvider.go b/observer/circularQueueNodesProvider.go index d4f07936..ec5f1a16 100644 --- a/observer/circularQueueNodesProvider.go +++ b/observer/circularQueueNodesProvider.go @@ -35,11 +35,11 @@ func NewCircularQueueNodesProvider(observers []*data.NodeData, configurationFile } // GetNodesByShardId will return a slice of observers for the given shard -func (cqnp *circularQueueNodesProvider) GetNodesByShardId(shardId uint32) ([]*data.NodeData, error) { +func (cqnp *circularQueueNodesProvider) GetNodesByShardId(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { cqnp.mutNodes.Lock() defer cqnp.mutNodes.Unlock() - syncedNodesForShard, err := cqnp.getSyncedNodesForShardUnprotected(shardId) + syncedNodesForShard, err := cqnp.getSyncedNodesForShardUnprotected(shardId, dataAvailability) if err != nil { return nil, err } @@ -51,11 +51,11 @@ func (cqnp *circularQueueNodesProvider) GetNodesByShardId(shardId uint32) ([]*da } // GetAllNodes will return a slice containing all observers -func (cqnp *circularQueueNodesProvider) GetAllNodes() ([]*data.NodeData, error) { +func (cqnp *circularQueueNodesProvider) GetAllNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { cqnp.mutNodes.Lock() defer cqnp.mutNodes.Unlock() - allNodes, err := cqnp.getSyncedNodesUnprotected() + allNodes, err := cqnp.getSyncedNodesUnprotected(dataAvailability) if err != nil { return nil, err } diff --git a/observer/circularQueueNodesProvider_test.go b/observer/circularQueueNodesProvider_test.go index 7f7691a0..209978d7 100644 --- a/observer/circularQueueNodesProvider_test.go +++ b/observer/circularQueueNodesProvider_test.go @@ -52,7 +52,7 @@ func TestCircularQueueObserversProvider_GetObserversByShardIdShouldWork(t *testi cfg := getDummyConfig() cqop, _ := NewCircularQueueNodesProvider(cfg.Observers, "path") - res, err := cqop.GetNodesByShardId(shardId) + res, err := cqop.GetNodesByShardId(shardId, "") assert.Nil(t, err) assert.Equal(t, 1, len(res)) } @@ -79,14 +79,14 @@ func TestCircularQueueObserversProvider_GetObserversByShardIdShouldBalanceObserv } cqop, _ := NewCircularQueueNodesProvider(cfg.Observers, "path") - res1, _ := cqop.GetNodesByShardId(shardId) - res2, _ := cqop.GetNodesByShardId(shardId) + res1, _ := cqop.GetNodesByShardId(shardId, "") + res2, _ := cqop.GetNodesByShardId(shardId, "") assert.NotEqual(t, res1, res2) // there are 3 observers. so after 3 steps, the queue should be the same as the original - _, _ = cqop.GetNodesByShardId(shardId) + _, _ = cqop.GetNodesByShardId(shardId, "") - res4, _ := cqop.GetNodesByShardId(shardId) + res4, _ := cqop.GetNodesByShardId(shardId, "") assert.Equal(t, res1, res4) } @@ -96,7 +96,7 @@ func TestCircularQueueObserversProvider_GetAllObserversShouldWork(t *testing.T) cfg := getDummyConfig() cqop, _ := NewCircularQueueNodesProvider(cfg.Observers, "path") - res, err := cqop.GetAllNodes() + res, err := cqop.GetAllNodes("") assert.NoError(t, err) assert.Equal(t, 2, len(res)) } @@ -122,14 +122,14 @@ func TestCircularQueueObserversProvider_GetAllObserversShouldWorkAndBalanceObser } cqop, _ := NewCircularQueueNodesProvider(cfg.Observers, "path") - res1, _ := cqop.GetAllNodes() - res2, _ := cqop.GetAllNodes() + res1, _ := cqop.GetAllNodes("") + res2, _ := cqop.GetAllNodes("") assert.NotEqual(t, res1, res2) // there are 3 observers. so after 3 steps, the queue should be the same as the original - _, _ = cqop.GetAllNodes() + _, _ = cqop.GetAllNodes("") - res4, _ := cqop.GetAllNodes() + res4, _ := cqop.GetAllNodes("") assert.Equal(t, res1, res4) } @@ -172,7 +172,7 @@ func TestCircularQueueObserversProvider_GetAllObservers_ConcurrentSafe(t *testin for i := 0; i < numOfGoRoutinesToStart; i++ { for j := 0; j < numOfTimesToCallForEachRoutine; j++ { go func(mutMap *sync.RWMutex, mapCalledObs map[string]int) { - obs, _ := cqop.GetAllNodes() + obs, _ := cqop.GetAllNodes("") mutMap.Lock() mapCalledObs[obs[0].Address]++ mutMap.Unlock() @@ -232,8 +232,8 @@ func TestCircularQueueObserversProvider_GetObserversByShardId_ConcurrentSafe(t * for i := 0; i < numOfGoRoutinesToStart; i++ { for j := 0; j < numOfTimesToCallForEachRoutine; j++ { go func(mutMap *sync.RWMutex, mapCalledObs map[string]int) { - obsSh0, _ := cqop.GetNodesByShardId(shardId0) - obsSh1, _ := cqop.GetNodesByShardId(shardId1) + obsSh0, _ := cqop.GetNodesByShardId(shardId0, "") + obsSh1, _ := cqop.GetNodesByShardId(shardId1, "") mutMap.Lock() mapCalledObs[obsSh0[0].Address]++ mapCalledObs[obsSh1[0].Address]++ diff --git a/observer/disabledNodesProvider.go b/observer/disabledNodesProvider.go index e5f817ff..5256fe07 100644 --- a/observer/disabledNodesProvider.go +++ b/observer/disabledNodesProvider.go @@ -30,12 +30,12 @@ func (d *disabledNodesProvider) GetAllNodesWithSyncState() []*data.NodeData { } // GetNodesByShardId returns the desired return message as an error -func (d *disabledNodesProvider) GetNodesByShardId(_ uint32) ([]*data.NodeData, error) { +func (d *disabledNodesProvider) GetNodesByShardId(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, errors.New(d.returnMessage) } // GetAllNodes returns the desired return message as an error -func (d *disabledNodesProvider) GetAllNodes() ([]*data.NodeData, error) { +func (d *disabledNodesProvider) GetAllNodes(_ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, errors.New(d.returnMessage) } diff --git a/observer/errors.go b/observer/errors.go index 84e40c49..5b7f8556 100644 --- a/observer/errors.go +++ b/observer/errors.go @@ -10,3 +10,6 @@ var ErrShardNotAvailable = errors.New("the specified shard ID does not exist in // ErrWrongObserversConfiguration signals an invalid observers configuration var ErrWrongObserversConfiguration = errors.New("wrong observers configuration") + +// ErrObserverCannotBeBothFallbackAndSnapshotless signals an invalid configuration for one of the configuration nodes +var ErrObserverCannotBeBothFallbackAndSnapshotless = errors.New("observer cannot be both fallback and snapshotless") diff --git a/observer/interface.go b/observer/interface.go index 32d23f19..0adae03a 100644 --- a/observer/interface.go +++ b/observer/interface.go @@ -4,8 +4,8 @@ import "github.com/multiversx/mx-chain-proxy-go/data" // NodesProviderHandler defines what a nodes provider should be able to do type NodesProviderHandler interface { - GetNodesByShardId(shardId uint32) ([]*data.NodeData, error) - GetAllNodes() ([]*data.NodeData, error) + GetNodesByShardId(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) UpdateNodesBasedOnSyncState(nodesWithSyncStatus []*data.NodeData) GetAllNodesWithSyncState() []*data.NodeData ReloadNodes(nodesType data.NodeType) data.NodesReloadResponse diff --git a/observer/simpleNodesProvider.go b/observer/simpleNodesProvider.go index 139f885a..e3e095a4 100644 --- a/observer/simpleNodesProvider.go +++ b/observer/simpleNodesProvider.go @@ -27,19 +27,19 @@ func NewSimpleNodesProvider(observers []*data.NodeData, configurationFilePath st } // GetNodesByShardId will return a slice of the nodes for the given shard -func (snp *simpleNodesProvider) GetNodesByShardId(shardId uint32) ([]*data.NodeData, error) { +func (snp *simpleNodesProvider) GetNodesByShardId(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { snp.mutNodes.RLock() defer snp.mutNodes.RUnlock() - return snp.getSyncedNodesForShardUnprotected(shardId) + return snp.getSyncedNodesForShardUnprotected(shardId, dataAvailability) } // GetAllNodes will return a slice containing all the nodes -func (snp *simpleNodesProvider) GetAllNodes() ([]*data.NodeData, error) { +func (snp *simpleNodesProvider) GetAllNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { snp.mutNodes.RLock() defer snp.mutNodes.RUnlock() - return snp.getSyncedNodesUnprotected() + return snp.getSyncedNodesUnprotected(dataAvailability) } // IsInterfaceNil returns true if there is no value under the interface diff --git a/observer/simpleNodesProvider_test.go b/observer/simpleNodesProvider_test.go index e66cda46..8169954c 100644 --- a/observer/simpleNodesProvider_test.go +++ b/observer/simpleNodesProvider_test.go @@ -37,7 +37,7 @@ func TestSimpleObserversProvider_GetObserversByShardIdShouldErrBecauseInvalidSha cfg := getDummyConfig() cqop, _ := NewSimpleNodesProvider(cfg.Observers, "path") - res, err := cqop.GetNodesByShardId(invalidShardId) + res, err := cqop.GetNodesByShardId(invalidShardId, "") assert.Nil(t, res) assert.Equal(t, ErrShardNotAvailable, err) } @@ -49,7 +49,7 @@ func TestSimpleObserversProvider_GetObserversByShardIdShouldWork(t *testing.T) { cfg := getDummyConfig() cqop, _ := NewSimpleNodesProvider(cfg.Observers, "path") - res, err := cqop.GetNodesByShardId(shardId) + res, err := cqop.GetNodesByShardId(shardId, "") assert.Nil(t, err) assert.Equal(t, 1, len(res)) } @@ -60,7 +60,7 @@ func TestSimpleObserversProvider_GetAllObserversShouldWork(t *testing.T) { cfg := getDummyConfig() cqop, _ := NewSimpleNodesProvider(cfg.Observers, "path") - res, _ := cqop.GetAllNodes() + res, _ := cqop.GetAllNodes("") assert.Equal(t, 2, len(res)) } @@ -111,8 +111,8 @@ func TestSimpleObserversProvider_GetObserversByShardId_ConcurrentSafe(t *testing for i := 0; i < numOfGoRoutinesToStart; i++ { for j := 0; j < numOfTimesToCallForEachRoutine; j++ { go func(mutMap *sync.RWMutex, mapCalledObs map[string]int) { - obsSh0, _ := sop.GetNodesByShardId(shardId0) - obsSh1, _ := sop.GetNodesByShardId(shardId1) + obsSh0, _ := sop.GetNodesByShardId(shardId0, "") + obsSh1, _ := sop.GetNodesByShardId(shardId1, "") mutMap.Lock() mapCalledObs[obsSh0[0].Address]++ mapCalledObs[obsSh1[0].Address]++ @@ -175,7 +175,7 @@ func TestSimpleObserversProvider_GetAllObservers_ConcurrentSafe(t *testing.T) { for i := 0; i < numOfGoRoutinesToStart; i++ { for j := 0; j < numOfTimesToCallForEachRoutine; j++ { go func(mutMap *sync.RWMutex, mapCalledObs map[string]int) { - obs, _ := sop.GetAllNodes() + obs, _ := sop.GetAllNodes("") mutMap.Lock() mapCalledObs[obs[0].Address]++ mutMap.Unlock() diff --git a/process/aboutInfoProcessor.go b/process/aboutInfoProcessor.go index 61d2a722..d89346b5 100644 --- a/process/aboutInfoProcessor.go +++ b/process/aboutInfoProcessor.go @@ -62,7 +62,7 @@ func (ap *aboutProcessor) GetAboutInfo() *data.GenericAPIResponse { // GetNodesVersions will return the versions of the nodes behind proxy func (ap *aboutProcessor) GetNodesVersions() (*data.GenericAPIResponse, error) { versionsMap := make(map[uint32][]string) - allObservers, err := ap.baseProc.GetAllObservers() + allObservers, err := ap.baseProc.GetAllObservers(data.AvailabilityRecent) if err != nil { return nil, err } diff --git a/process/aboutInfoProcessor_test.go b/process/aboutInfoProcessor_test.go index b00176be..7a384523 100644 --- a/process/aboutInfoProcessor_test.go +++ b/process/aboutInfoProcessor_test.go @@ -84,7 +84,7 @@ func TestAboutProcessor_GetNodesVersions(t *testing.T) { t.Parallel() proc := &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "addr0", @@ -110,7 +110,7 @@ func TestAboutProcessor_GetNodesVersions(t *testing.T) { expectedErr := errors.New("request error") proc := &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "addr0", @@ -134,7 +134,7 @@ func TestAboutProcessor_GetNodesVersions(t *testing.T) { t.Parallel() proc := &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "addrSh0", diff --git a/process/accountProcessor.go b/process/accountProcessor.go index fd8c214a..cde0bf71 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -52,7 +52,8 @@ func (ap *AccountProcessor) GetShardIDForAddress(address string) (uint32, error) // GetAccount resolves the request by sending the request to the right observer and replies back the answer func (ap *AccountProcessor) GetAccount(address string, options common.AccountQueryOptions) (*data.AccountModel, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -75,7 +76,8 @@ func (ap *AccountProcessor) GetAccount(address string, options common.AccountQue // GetValueForKey returns the value for the given address and key func (ap *AccountProcessor) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return "", err } @@ -106,7 +108,8 @@ func (ap *AccountProcessor) GetValueForKey(address string, key string, options c // GetESDTTokenData returns the token data for a token with the given name func (ap *AccountProcessor) GetESDTTokenData(address string, key string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -138,7 +141,8 @@ func (ap *AccountProcessor) GetESDTTokenData(address string, key string, options // GetESDTsWithRole returns the token identifiers where the given address has the given role assigned func (ap *AccountProcessor) GetESDTsWithRole(address string, role string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.proc.GetObservers(core.MetachainShardId) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err } @@ -170,7 +174,8 @@ func (ap *AccountProcessor) GetESDTsWithRole(address string, role string, option // GetESDTsRoles returns all the tokens and their roles for a given address func (ap *AccountProcessor) GetESDTsRoles(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.proc.GetObservers(core.MetachainShardId) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err } @@ -203,7 +208,8 @@ func (ap *AccountProcessor) GetESDTsRoles(address string, options common.Account func (ap *AccountProcessor) GetNFTTokenIDsRegisteredByAddress(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { //TODO: refactor the entire proxy so endpoints like this which simply forward the response will use a common // component, as described in task EN-9857. - observers, err := ap.proc.GetObservers(core.MetachainShardId) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err } @@ -234,7 +240,8 @@ func (ap *AccountProcessor) GetNFTTokenIDsRegisteredByAddress(address string, op // GetESDTNftTokenData returns the nft token data for a token with the given identifier and nonce func (ap *AccountProcessor) GetESDTNftTokenData(address string, key string, nonce uint64, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -267,7 +274,8 @@ func (ap *AccountProcessor) GetESDTNftTokenData(address string, key string, nonc // GetAllESDTTokens returns all the tokens for a given address func (ap *AccountProcessor) GetAllESDTTokens(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -298,7 +306,8 @@ func (ap *AccountProcessor) GetAllESDTTokens(address string, options common.Acco // GetKeyValuePairs returns all the key-value pairs for a given address func (ap *AccountProcessor) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -329,7 +338,8 @@ func (ap *AccountProcessor) GetKeyValuePairs(address string, options common.Acco // GetGuardianData returns the guardian data for the given address func (ap *AccountProcessor) GetGuardianData(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -369,7 +379,8 @@ func (ap *AccountProcessor) GetTransactions(address string) ([]data.DatabaseTran // GetCodeHash returns the code hash for a given address func (ap *AccountProcessor) GetCodeHash(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + availability := getAvailabilityBasedOnAccountQueryOptions(options) + observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err } @@ -398,7 +409,7 @@ func (ap *AccountProcessor) GetCodeHash(address string, options common.AccountQu return nil, ErrSendingRequest } -func (ap *AccountProcessor) getObserversForAddress(address string) ([]*data.NodeData, error) { +func (ap *AccountProcessor) getObserversForAddress(address string, availability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { addressBytes, err := ap.pubKeyConverter.Decode(address) if err != nil { return nil, err @@ -409,7 +420,7 @@ func (ap *AccountProcessor) getObserversForAddress(address string) ([]*data.Node return nil, err } - observers, err := ap.proc.GetObservers(shardID) + observers, err := ap.proc.GetObservers(shardID, availability) if err != nil { return nil, err } @@ -421,3 +432,11 @@ func (ap *AccountProcessor) getObserversForAddress(address string) ([]*data.Node func (ap *AccountProcessor) GetBaseProcessor() Processor { return ap.proc } + +func getAvailabilityBasedOnAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { + availability := data.AvailabilityRecent + if options.AreHistoricalCoordinatesSet() { + availability = data.AvailabilityAll + } + return availability +} diff --git a/process/accountProcessor_test.go b/process/accountProcessor_test.go index a23d48b9..cc94f0c0 100644 --- a/process/accountProcessor_test.go +++ b/process/accountProcessor_test.go @@ -87,7 +87,7 @@ func TestAccountProcessor_GetAccountGetObserversFailsShouldErr(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return nil, errExpected }, }, @@ -110,7 +110,7 @@ func TestAccountProcessor_GetAccountSendingFailsOnAllObserversShouldErr(t *testi ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -145,7 +145,7 @@ func TestAccountProcessor_GetAccountSendingFailsOnFirstObserverShouldStillSend(t ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: addressFail, ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -180,7 +180,7 @@ func TestAccountProcessor_GetValueForAKeyShouldWork(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address", ShardId: 0}, }, nil @@ -211,7 +211,7 @@ func TestAccountProcessor_GetValueForAKeyShouldError(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address", ShardId: 0}, }, nil @@ -250,7 +250,7 @@ func TestAccountProcessor_GetShardIForAddressShouldWork(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return shardC.ComputeId(addressBuff), nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return observers, nil }, }, @@ -313,7 +313,7 @@ func TestAccountProcessor_GetESDTsWithRoleGetObserversFails(t *testing.T) { expectedErr := errors.New("cannot get observers") ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, expectedErr }, }, @@ -332,7 +332,7 @@ func TestAccountProcessor_GetESDTsWithRoleApiCallFails(t *testing.T) { expectedApiErr := errors.New("cannot get observers") ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "observer0", @@ -363,7 +363,7 @@ func TestAccountProcessor_GetESDTsWithRoleShouldWork(t *testing.T) { ComputeShardIdCalled: func(_ []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address", ShardId: 0}, }, nil @@ -389,7 +389,7 @@ func TestAccountProcessor_GetESDTsRolesGetObserversFails(t *testing.T) { expectedErr := errors.New("cannot get observers") ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, expectedErr }, }, @@ -408,7 +408,7 @@ func TestAccountProcessor_GetESDTsRolesApiCallFails(t *testing.T) { expectedApiErr := errors.New("cannot get observers") ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "observer0", @@ -439,7 +439,7 @@ func TestAccountProcessor_GetESDTsRolesShouldWork(t *testing.T) { ComputeShardIdCalled: func(_ []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address", ShardId: 0}, }, nil @@ -467,7 +467,7 @@ func TestAccountProcessor_GetCodeHash(t *testing.T) { ComputeShardIdCalled: func(_ []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address", ShardId: 0}, }, nil diff --git a/process/baseProcessor.go b/process/baseProcessor.go index 87c603d2..80fde998 100644 --- a/process/baseProcessor.go +++ b/process/baseProcessor.go @@ -30,8 +30,7 @@ const ( timeoutDurationForNodeStatus = 2 * time.Second ) -// BaseProcessor represents an implementation of CoreProcessor that helps -// processing requests +// BaseProcessor represents an implementation of CoreProcessor that helps to process requests type BaseProcessor struct { mutState sync.RWMutex shardCoordinator common.Coordinator @@ -120,43 +119,44 @@ func (bp *BaseProcessor) ReloadFullHistoryObservers() proxyData.NodesReloadRespo } // GetObservers returns the registered observers on a shard -func (bp *BaseProcessor) GetObservers(shardID uint32) ([]*proxyData.NodeData, error) { - return bp.observersProvider.GetNodesByShardId(shardID) +func (bp *BaseProcessor) GetObservers(shardID uint32, dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error) { + return bp.observersProvider.GetNodesByShardId(shardID, dataAvailability) } // GetAllObservers will return all the observers, regardless of shard ID -func (bp *BaseProcessor) GetAllObservers() ([]*proxyData.NodeData, error) { - return bp.observersProvider.GetAllNodes() +func (bp *BaseProcessor) GetAllObservers(dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error) { + return bp.observersProvider.GetAllNodes(dataAvailability) } // GetObserversOnePerShard will return a slice containing an observer for each shard -func (bp *BaseProcessor) GetObserversOnePerShard() ([]*proxyData.NodeData, error) { - return bp.getNodesOnePerShard(bp.observersProvider.GetNodesByShardId) +func (bp *BaseProcessor) GetObserversOnePerShard(dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error) { + return bp.getNodesOnePerShard(bp.observersProvider.GetNodesByShardId, dataAvailability) } // GetFullHistoryNodes returns the registered full history nodes on a shard -func (bp *BaseProcessor) GetFullHistoryNodes(shardID uint32) ([]*proxyData.NodeData, error) { - return bp.fullHistoryNodesProvider.GetNodesByShardId(shardID) +func (bp *BaseProcessor) GetFullHistoryNodes(shardID uint32, dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error) { + return bp.fullHistoryNodesProvider.GetNodesByShardId(shardID, dataAvailability) } // GetAllFullHistoryNodes will return all the full history nodes, regardless of shard ID -func (bp *BaseProcessor) GetAllFullHistoryNodes() ([]*proxyData.NodeData, error) { - return bp.fullHistoryNodesProvider.GetAllNodes() +func (bp *BaseProcessor) GetAllFullHistoryNodes(dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error) { + return bp.fullHistoryNodesProvider.GetAllNodes(dataAvailability) } // GetFullHistoryNodesOnePerShard will return a slice containing a full history node for each shard -func (bp *BaseProcessor) GetFullHistoryNodesOnePerShard() ([]*proxyData.NodeData, error) { - return bp.getNodesOnePerShard(bp.fullHistoryNodesProvider.GetNodesByShardId) +func (bp *BaseProcessor) GetFullHistoryNodesOnePerShard(dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error) { + return bp.getNodesOnePerShard(bp.fullHistoryNodesProvider.GetNodesByShardId, dataAvailability) } func (bp *BaseProcessor) getNodesOnePerShard( - observersInShardGetter func(shardID uint32) ([]*proxyData.NodeData, error), + observersInShardGetter func(shardID uint32, dataAvailability proxyData.ObserverDataAvailabilityType) ([]*proxyData.NodeData, error), + dataAvailability proxyData.ObserverDataAvailabilityType, ) ([]*proxyData.NodeData, error) { numShards := bp.shardCoordinator.NumberOfShards() sliceToReturn := make([]*proxyData.NodeData, 0) for shardID := uint32(0); shardID < numShards; shardID++ { - observersInShard, err := observersInShardGetter(shardID) + observersInShard, err := observersInShardGetter(shardID, dataAvailability) if err != nil || len(observersInShard) < 1 { continue } @@ -164,7 +164,7 @@ func (bp *BaseProcessor) getNodesOnePerShard( sliceToReturn = append(sliceToReturn, observersInShard[0]) } - observersInShardMeta, err := observersInShardGetter(core.MetachainShardId) + observersInShardMeta, err := observersInShardGetter(core.MetachainShardId, dataAvailability) if err == nil && len(observersInShardMeta) > 0 { sliceToReturn = append(sliceToReturn, observersInShardMeta[0]) } diff --git a/process/baseProcessor_test.go b/process/baseProcessor_test.go index cb487454..1ca4bc9f 100644 --- a/process/baseProcessor_test.go +++ b/process/baseProcessor_test.go @@ -129,14 +129,14 @@ func TestBaseProcessor_GetObserversEmptyListShouldWork(t *testing.T) { 5, &mock.ShardCoordinatorMock{}, &mock.ObserversProviderStub{ - GetNodesByShardIdCalled: func(_ uint32) ([]*data.NodeData, error) { + GetNodesByShardIdCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return observersSlice, nil }, }, &mock.ObserversProviderStub{}, &mock.PubKeyConverterMock{}, ) - observers, err := bp.GetObservers(0) + observers, err := bp.GetObservers(0, data.AvailabilityAll) assert.Nil(t, err) assert.Equal(t, observersSlice, observers) @@ -163,7 +163,7 @@ func TestBaseProcessor_ComputeShardId(t *testing.T) { 5, msc, &mock.ObserversProviderStub{ - GetNodesByShardIdCalled: func(_ uint32) ([]*data.NodeData, error) { + GetNodesByShardIdCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return observersList, nil }, }, @@ -319,7 +319,7 @@ func TestBaseProcessor_GetAllObserversWithOkValuesShouldPass(t *testing.T) { 5, &mock.ShardCoordinatorMock{}, &mock.ObserversProviderStub{ - GetAllNodesCalled: func() ([]*data.NodeData, error) { + GetAllNodesCalled: func(_ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return observersList, nil }, }, @@ -329,7 +329,7 @@ func TestBaseProcessor_GetAllObserversWithOkValuesShouldPass(t *testing.T) { assert.Nil(t, err) - observers, _ := bp.GetAllObservers() + observers, _ := bp.GetAllObservers(data.AvailabilityAll) assert.Nil(t, err) assert.Equal(t, server.URL, observers[0].Address) } @@ -360,7 +360,7 @@ func TestBaseProcessor_GetObserversOnePerShardShouldWork(t *testing.T) { 5, &mock.ShardCoordinatorMock{NumShards: 2}, &mock.ObserversProviderStub{ - GetNodesByShardIdCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetNodesByShardIdCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch shardId { case 0: return observersListShard0, nil @@ -377,7 +377,7 @@ func TestBaseProcessor_GetObserversOnePerShardShouldWork(t *testing.T) { &mock.PubKeyConverterMock{}, ) - observers, err := bp.GetObserversOnePerShard() + observers, err := bp.GetObserversOnePerShard(data.AvailabilityAll) assert.NoError(t, err) for i := 0; i < len(observers); i++ { @@ -408,7 +408,7 @@ func TestBaseProcessor_GetObserversOnePerShardOneShardHasNoObserverShouldWork(t 5, &mock.ShardCoordinatorMock{NumShards: 2}, &mock.ObserversProviderStub{ - GetNodesByShardIdCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetNodesByShardIdCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch shardId { case 0: return observersListShard0, nil @@ -425,7 +425,7 @@ func TestBaseProcessor_GetObserversOnePerShardOneShardHasNoObserverShouldWork(t &mock.PubKeyConverterMock{}, ) - observers, err := bp.GetObserversOnePerShard() + observers, err := bp.GetObserversOnePerShard(data.AvailabilityAll) assert.NoError(t, err) for i := 0; i < len(observers); i++ { @@ -456,7 +456,7 @@ func TestBaseProcessor_GetObserversOnePerShardMetachainHasNoObserverShouldWork(t 5, &mock.ShardCoordinatorMock{NumShards: 2}, &mock.ObserversProviderStub{ - GetNodesByShardIdCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetNodesByShardIdCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch shardId { case 0: return observersListShard0, nil @@ -473,7 +473,7 @@ func TestBaseProcessor_GetObserversOnePerShardMetachainHasNoObserverShouldWork(t &mock.PubKeyConverterMock{}, ) - observers, err := bp.GetObserversOnePerShard() + observers, err := bp.GetObserversOnePerShard(data.AvailabilityAll) assert.NoError(t, err) for i := 0; i < len(observers); i++ { @@ -509,7 +509,7 @@ func TestBaseProcessor_GetFullHistoryNodesOnePerShardShouldWork(t *testing.T) { &mock.ShardCoordinatorMock{NumShards: 2}, &mock.ObserversProviderStub{}, &mock.ObserversProviderStub{ - GetNodesByShardIdCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetNodesByShardIdCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch shardId { case 0: return observersListShard0, nil @@ -525,7 +525,7 @@ func TestBaseProcessor_GetFullHistoryNodesOnePerShardShouldWork(t *testing.T) { &mock.PubKeyConverterMock{}, ) - observers, err := bp.GetFullHistoryNodesOnePerShard() + observers, err := bp.GetFullHistoryNodesOnePerShard(data.AvailabilityAll) assert.NoError(t, err) for i := 0; i < len(observers); i++ { diff --git a/process/blockProcessor.go b/process/blockProcessor.go index a983262d..8a9adb8e 100644 --- a/process/blockProcessor.go +++ b/process/blockProcessor.go @@ -114,12 +114,12 @@ func (bp *BlockProcessor) GetBlockByNonce(shardID uint32, nonce uint64, options } func (bp *BlockProcessor) getObserversOrFullHistoryNodes(shardID uint32) ([]*data.NodeData, error) { - fullHistoryNodes, err := bp.proc.GetFullHistoryNodes(shardID) + fullHistoryNodes, err := bp.proc.GetFullHistoryNodes(shardID, data.AvailabilityAll) if err == nil { return fullHistoryNodes, nil } - return bp.proc.GetObservers(shardID) + return bp.proc.GetObservers(shardID, data.AvailabilityAll) } // GetHyperBlockByHash returns the hyperblock by hash @@ -410,7 +410,7 @@ func (bp *BlockProcessor) GetInternalStartOfEpochValidatorsInfo(epoch uint32) (* // GetAlteredAccountsByNonce will return altered accounts by block nonce func (bp *BlockProcessor) GetAlteredAccountsByNonce(shardID uint32, nonce uint64, options common.GetAlteredAccountsForBlockOptions) (*data.AlteredAccountsApiResponse, error) { - observers, err := bp.proc.GetObservers(shardID) + observers, err := bp.proc.GetObservers(shardID, data.AvailabilityAll) if err != nil { return nil, err } @@ -435,7 +435,7 @@ func (bp *BlockProcessor) GetAlteredAccountsByNonce(shardID uint32, nonce uint64 // GetAlteredAccountsByHash will return altered accounts by block hash func (bp *BlockProcessor) GetAlteredAccountsByHash(shardID uint32, hash string, options common.GetAlteredAccountsForBlockOptions) (*data.AlteredAccountsApiResponse, error) { - observers, err := bp.proc.GetObservers(shardID) + observers, err := bp.proc.GetObservers(shardID, data.AvailabilityAll) if err != nil { return nil, err } diff --git a/process/blockProcessor_test.go b/process/blockProcessor_test.go index 729e6e9c..c38c0a0d 100644 --- a/process/blockProcessor_test.go +++ b/process/blockProcessor_test.go @@ -60,11 +60,11 @@ func TestBlockProcessor_GetBlockByHashShouldGetFullHistoryNodes(t *testing.T) { getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -86,11 +86,11 @@ func TestBlockProcessor_GetBlockByHashShouldGetObservers(t *testing.T) { getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, errors.New("local err") }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -110,10 +110,10 @@ func TestBlockProcessor_GetBlockByHashNoFullNodesOrObserversShouldErr(t *testing localErr := errors.New("local err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, } @@ -131,7 +131,7 @@ func TestBlockProcessor_GetBlockByHashCallGetFailsShouldErr(t *testing.T) { localErr := errors.New("err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -152,7 +152,7 @@ func TestBlockProcessor_GetBlockByHashShouldWork(t *testing.T) { nonce := uint64(37) proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -179,7 +179,7 @@ func TestBlockProcessor_GetBlockByHashShouldWorkAndIncludeAlsoTxs(t *testing.T) isAddressCorrect := false nonce := uint64(37) proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -209,11 +209,11 @@ func TestBlockProcessor_GetBlockByNonceShouldGetFullHistoryNodes(t *testing.T) { getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -235,11 +235,11 @@ func TestBlockProcessor_GetBlockByNonceShouldGetObservers(t *testing.T) { getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, errors.New("local err") }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -259,10 +259,10 @@ func TestBlockProcessor_GetBlockByNonceNoFullNodesOrObserversShouldErr(t *testin localErr := errors.New("local err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, } @@ -280,7 +280,7 @@ func TestBlockProcessor_GetBlockByNonceCallGetFailsShouldErr(t *testing.T) { localErr := errors.New("err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -301,7 +301,7 @@ func TestBlockProcessor_GetBlockByNonceShouldWork(t *testing.T) { nonce := uint64(37) proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -328,7 +328,7 @@ func TestBlockProcessor_GetBlockByNonceShouldWorkAndIncludeAlsoTxs(t *testing.T) isAddressCorrect := false nonce := uint64(37) proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -356,8 +356,8 @@ func TestBlockProcessor_GetHyperBlock(t *testing.T) { numGetBlockCalled := 0 proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { - return []*data.NodeData{{ShardId: shardId, Address: fmt.Sprintf("http://observer-%d", shardId)}}, nil + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { + return []*data.NodeData{{ShardId: shardId, Address: fmt.Sprintf("observer-%d", shardId)}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { numGetBlockCalled++ @@ -405,7 +405,7 @@ func TestBlockProcessor_GetInternalBlockByNonceInvalidOutputFormat_ShouldFail(t t.Parallel() proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, nil }, } @@ -425,11 +425,11 @@ func TestBlockProcessor_GetInternalBlockByNonceShouldGetFullHistoryNodes(t *test getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -451,11 +451,11 @@ func TestBlockProcessor_GetInternalBlockByNonceShouldGetObservers(t *testing.T) getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, errors.New("local err") }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -475,10 +475,10 @@ func TestBlockProcessor_GetInternalBlockByNonceNoFullNodesOrObserversShouldErr(t localErr := errors.New("local err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, } @@ -496,7 +496,7 @@ func TestBlockProcessor_GetInternalBlockByNonceCallGetFailsShouldErr(t *testing. localErr := errors.New("err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -523,7 +523,7 @@ func TestBlockProcessor_GetInternalBlockByNonceShouldWork(t *testing.T) { nonce := uint64(37) expectedData := data.InternalBlockApiResponsePayload{Block: ts} proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -553,7 +553,7 @@ func TestBlockProcessor_GetInternalBlockByHashInvalidOutputFormat_ShouldFail(t * t.Parallel() proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, nil }, } @@ -573,11 +573,11 @@ func TestBlockProcessor_GetInternalBlockByHashShouldGetFullHistoryNodes(t *testi getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -599,11 +599,11 @@ func TestBlockProcessor_GetInternalBlockByHashShouldGetObservers(t *testing.T) { getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, errors.New("local err") }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -623,10 +623,10 @@ func TestBlockProcessor_GetInternalBlockByHashNoFullNodesOrObserversShouldErr(t localErr := errors.New("local err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, } @@ -644,7 +644,7 @@ func TestBlockProcessor_GetInternalBlockByHashCallGetFailsShouldErr(t *testing.T localErr := errors.New("err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -670,7 +670,7 @@ func TestBlockProcessor_GetInternalBlockByHashShouldWork(t *testing.T) { expectedData := data.InternalBlockApiResponsePayload{Block: ts} proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -700,7 +700,7 @@ func TestBlockProcessor_GetInternalMiniBlockByHashInvalidOutputFormat_ShouldFail t.Parallel() proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, nil }, } @@ -720,11 +720,11 @@ func TestBlockProcessor_GetInternalMiniBlockByHashShouldGetFullHistoryNodes(t *t getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -746,11 +746,11 @@ func TestBlockProcessor_GetInternalMiniBlockByHashShouldGetObservers(t *testing. getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, errors.New("local err") }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -770,10 +770,10 @@ func TestBlockProcessor_GetInternalMiniBlockByHashNoFullNodesOrObserversShouldEr localErr := errors.New("local err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, } @@ -791,7 +791,7 @@ func TestBlockProcessor_GetInternalMiniBlockByHashCallGetFailsShouldErr(t *testi localErr := errors.New("err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -817,7 +817,7 @@ func TestBlockProcessor_GetInternalMiniBlockByHashShouldWork(t *testing.T) { expectedData := data.InternalMiniBlockApiResponsePayload{MiniBlock: ts} proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -847,7 +847,7 @@ func TestBlockProcessor_GetInternalStartOfEpochMetaBlockInvalidOutputFormat_Shou t.Parallel() proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, nil }, } @@ -867,11 +867,11 @@ func TestBlockProcessor_GetInternalStartOfEpochMetaBlockShouldGetFullHistoryNode getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -893,11 +893,11 @@ func TestBlockProcessor_GetInternalStartOfEpochMetaBlockShouldGetObservers(t *te getObserversCalled := false proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getFullHistoryNodesCalled = true return nil, errors.New("local err") }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { getObserversCalled = true return nil, nil }, @@ -917,10 +917,10 @@ func TestBlockProcessor_GetInternalStartOfEpochMetaBlockNoFullNodesOrObserversSh localErr := errors.New("local err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, } @@ -938,7 +938,7 @@ func TestBlockProcessor_GetInternalStartOfEpochMetaBlockCallGetFailsShouldErr(t localErr := errors.New("err") proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { assert.Equal(t, shardId, core.MetachainShardId) return nil, nil }, @@ -965,7 +965,7 @@ func TestBlockProcessor_GetInternalStartOfEpochMetaBlockShouldWork(t *testing.T) expectedData := data.InternalBlockApiResponsePayload{Block: ts} proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -1000,7 +1000,7 @@ func TestBlockProcessor_GetAlteredAccountsByNonce(t *testing.T) { expectedErr := errors.New("local error") proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, expectedErr }, } @@ -1020,7 +1020,7 @@ func TestBlockProcessor_GetAlteredAccountsByNonce(t *testing.T) { node2 := &data.NodeData{ShardId: requestedShardID, Address: "addr2"} proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, requestedShardID, shardId) return []*data.NodeData{node1, node2}, nil }, @@ -1045,7 +1045,7 @@ func TestBlockProcessor_GetAlteredAccountsByNonce(t *testing.T) { t.Parallel() proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, requestedShardID, shardId) return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, @@ -1087,7 +1087,7 @@ func TestBlockProcessor_GetAlteredAccountsByHash(t *testing.T) { expectedErr := errors.New("local error") proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, expectedErr }, } @@ -1107,7 +1107,7 @@ func TestBlockProcessor_GetAlteredAccountsByHash(t *testing.T) { node2 := &data.NodeData{ShardId: requestedShardID, Address: "addr2"} proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, requestedShardID, shardId) return []*data.NodeData{node1, node2}, nil }, @@ -1132,7 +1132,7 @@ func TestBlockProcessor_GetAlteredAccountsByHash(t *testing.T) { t.Parallel() proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, requestedShardID, shardId) return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, @@ -1173,7 +1173,7 @@ func TestBlockProcessor_GetHyperBlockByNonceWithAlteredAccounts(t *testing.T) { callGetEndpointCt := 0 getObserversCt := 0 proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch getObserversCt { case 0: require.Equal(t, core.MetachainShardId, shardId) @@ -1290,7 +1290,7 @@ func TestBlockProcessor_GetHyperBlockByHashWithAlteredAccounts(t *testing.T) { callGetEndpointCt := 0 getObserversCt := 0 proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch getObserversCt { case 0: require.Equal(t, core.MetachainShardId, shardId) @@ -1409,7 +1409,7 @@ func TestBlockProcessor_GetInternalStartOfEpochValidatorsInfo(t *testing.T) { ValidatorsInfo: ts, } proc := &mock.ProcessorStub{ - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{ShardId: shardId, Address: "addr"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { diff --git a/process/blocksProcessor.go b/process/blocksProcessor.go index 48902acf..41fbbdba 100644 --- a/process/blocksProcessor.go +++ b/process/blocksProcessor.go @@ -44,7 +44,7 @@ func (bp *BlocksProcessor) GetBlocksByRound(round uint64, options common.BlockQu path := common.BuildUrlWithBlockQueryOptions(fmt.Sprintf("%s/%d", blockByRoundPath, round), options) for _, shardID := range shardIDs { - observers, err := bp.proc.GetObservers(shardID) + observers, err := bp.proc.GetObservers(shardID, data.AvailabilityAll) if err != nil { return nil, err } diff --git a/process/blocksProcessor_test.go b/process/blocksProcessor_test.go index fb1fa4eb..d9651093 100644 --- a/process/blocksProcessor_test.go +++ b/process/blocksProcessor_test.go @@ -26,7 +26,7 @@ func TestBlocksProcessor_GetBlocksByRound_InvalidObservers_ExpectError(t *testin err := errors.New("err observers") proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, err }, GetShardIDsCalled: func() []uint32 { @@ -47,7 +47,7 @@ func TestBlocksProcessor_GetBlocksByRound_InvalidCallGetRestEndPoint_ExpectZeroF err := errors.New("err call get") proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch shardId { case 0: return []*data.NodeData{ @@ -100,7 +100,7 @@ func TestBlocksProcessor_GetBlocksByRound_TwoBlocks_ThreeObservers_OneObserverGe } proc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { switch shardId { case 0: return []*data.NodeData{ diff --git a/process/economicMetrics.go b/process/economicMetrics.go index 6f4dd3bc..186bf3a5 100644 --- a/process/economicMetrics.go +++ b/process/economicMetrics.go @@ -19,7 +19,7 @@ func (nsp *NodeStatusProcessor) GetEconomicsDataMetrics() (*data.GenericAPIRespo } func (nsp *NodeStatusProcessor) getEconomicsDataMetricsFromApi() (*data.GenericAPIResponse, error) { - metaObservers, err := nsp.proc.GetObservers(core.MetachainShardId) + metaObservers, err := nsp.proc.GetObservers(core.MetachainShardId, data.AvailabilityRecent) if err != nil { return nil, err } diff --git a/process/economicMetrics_test.go b/process/economicMetrics_test.go index 649a117c..347a8c62 100644 --- a/process/economicMetrics_test.go +++ b/process/economicMetrics_test.go @@ -38,7 +38,7 @@ func TestNodeStatusProcessor_CacheShouldUpdate(t *testing.T) { numOfTimesHttpWasCalled := int32(0) cacher := &mock.GenericApiResponseCacherMock{} hp, err := process.NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{Address: "obs1"}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -78,7 +78,7 @@ func TestNodeStatusProcessor_GetEconomicsDataMetricsShouldWork(t *testing.T) { } nodeStatusProc, _ := process.NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: addressMeta, ShardId: core.MetachainShardId}, }, nil diff --git a/process/esdtSupplyProcessor.go b/process/esdtSupplyProcessor.go index fba52831..3b5fa806 100644 --- a/process/esdtSupplyProcessor.go +++ b/process/esdtSupplyProcessor.go @@ -138,7 +138,7 @@ func (esp *esdtSupplyProcessor) getInitialSupplyFromMeta(token string) (*big.Int } func (esp *esdtSupplyProcessor) getShardSupply(token string, shardID uint32) (*data.ESDTSupply, error) { - shardObservers, errObs := esp.baseProc.GetObservers(shardID) + shardObservers, errObs := esp.baseProc.GetObservers(shardID, data.AvailabilityAll) if errObs != nil { return nil, errObs } diff --git a/process/esdtSupplyProcessor_test.go b/process/esdtSupplyProcessor_test.go index 704f5007..3c5af65f 100644 --- a/process/esdtSupplyProcessor_test.go +++ b/process/esdtSupplyProcessor_test.go @@ -29,7 +29,7 @@ func TestEsdtSupplyProcessor_GetESDTSupplyFungible(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{0, 1, core.MetachainShardId} }, - GetObserversCalled: func(shardID uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { ShardId: shardID, @@ -80,7 +80,7 @@ func TestEsdtSupplyProcessor_GetESDTSupplyNonFungible(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{0, 1, core.MetachainShardId} }, - GetObserversCalled: func(shardID uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { ShardId: shardID, diff --git a/process/factory/interface.go b/process/factory/interface.go index 64887e10..c8fb267c 100644 --- a/process/factory/interface.go +++ b/process/factory/interface.go @@ -13,13 +13,13 @@ type Processor interface { ComputeShardId(addressBuff []byte) (uint32, error) CallGetRestEndPoint(address string, path string, value interface{}) (int, error) CallPostRestEndPoint(address string, path string, data interface{}, response interface{}) (int, error) - GetObserversOnePerShard() ([]*data.NodeData, error) + GetObserversOnePerShard(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) GetShardIDs() []uint32 - GetFullHistoryNodesOnePerShard() ([]*data.NodeData, error) - GetObservers(shardID uint32) ([]*data.NodeData, error) - GetAllObservers() ([]*data.NodeData, error) - GetFullHistoryNodes(shardID uint32) ([]*data.NodeData, error) - GetAllFullHistoryNodes() ([]*data.NodeData, error) + GetFullHistoryNodesOnePerShard(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetObservers(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllObservers(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetFullHistoryNodes(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllFullHistoryNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) GetShardCoordinator() common.Coordinator GetPubKeyConverter() core.PubkeyConverter GetObserverProvider() observer.NodesProviderHandler diff --git a/process/interface.go b/process/interface.go index 3230a4b1..40603ea4 100644 --- a/process/interface.go +++ b/process/interface.go @@ -12,12 +12,12 @@ import ( // Processor defines what a processor should be able to do type Processor interface { - GetObservers(shardID uint32) ([]*data.NodeData, error) - GetAllObservers() ([]*data.NodeData, error) - GetObserversOnePerShard() ([]*data.NodeData, error) - GetFullHistoryNodesOnePerShard() ([]*data.NodeData, error) - GetFullHistoryNodes(shardID uint32) ([]*data.NodeData, error) - GetAllFullHistoryNodes() ([]*data.NodeData, error) + GetObservers(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllObservers(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetObserversOnePerShard(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetFullHistoryNodesOnePerShard(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetFullHistoryNodes(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllFullHistoryNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) GetShardIDs() []uint32 ComputeShardId(addressBuff []byte) (uint32, error) CallGetRestEndPoint(address string, path string, value interface{}) (int, error) diff --git a/process/mock/observersProviderStub.go b/process/mock/observersProviderStub.go index ddf1b341..acaa612d 100644 --- a/process/mock/observersProviderStub.go +++ b/process/mock/observersProviderStub.go @@ -6,17 +6,17 @@ import ( // ObserversProviderStub - type ObserversProviderStub struct { - GetNodesByShardIdCalled func(shardId uint32) ([]*data.NodeData, error) - GetAllNodesCalled func() ([]*data.NodeData, error) + GetNodesByShardIdCalled func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllNodesCalled func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) ReloadNodesCalled func(nodesType data.NodeType) data.NodesReloadResponse UpdateNodesBasedOnSyncStateCalled func(nodesWithSyncStatus []*data.NodeData) GetAllNodesWithSyncStateCalled func() []*data.NodeData } // GetNodesByShardId - -func (ops *ObserversProviderStub) GetNodesByShardId(shardId uint32) ([]*data.NodeData, error) { +func (ops *ObserversProviderStub) GetNodesByShardId(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ops.GetNodesByShardIdCalled != nil { - return ops.GetNodesByShardIdCalled(shardId) + return ops.GetNodesByShardIdCalled(shardId, dataAvailability) } return []*data.NodeData{ @@ -28,9 +28,9 @@ func (ops *ObserversProviderStub) GetNodesByShardId(shardId uint32) ([]*data.Nod } // GetAllNodes - -func (ops *ObserversProviderStub) GetAllNodes() ([]*data.NodeData, error) { +func (ops *ObserversProviderStub) GetAllNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ops.GetAllNodesCalled != nil { - return ops.GetAllNodesCalled() + return ops.GetAllNodesCalled(dataAvailability) } return []*data.NodeData{ @@ -41,7 +41,7 @@ func (ops *ObserversProviderStub) GetAllNodes() ([]*data.NodeData, error) { }, nil } -// RemoveOutOfSyncNodesIfNeeded - +// UpdateNodesBasedOnSyncState - func (ops *ObserversProviderStub) UpdateNodesBasedOnSyncState(nodesWithSyncStatus []*data.NodeData) { if ops.UpdateNodesBasedOnSyncStateCalled != nil { ops.UpdateNodesBasedOnSyncStateCalled(nodesWithSyncStatus) diff --git a/process/mock/processorStub.go b/process/mock/processorStub.go index 27588202..f07e810e 100644 --- a/process/mock/processorStub.go +++ b/process/mock/processorStub.go @@ -13,12 +13,12 @@ var errNotImplemented = errors.New("not implemented") type ProcessorStub struct { ApplyConfigCalled func(cfg *config.Config) error - GetObserversCalled func(shardId uint32) ([]*data.NodeData, error) - GetAllObserversCalled func() ([]*data.NodeData, error) - GetObserversOnePerShardCalled func() ([]*data.NodeData, error) - GetFullHistoryNodesOnePerShardCalled func() ([]*data.NodeData, error) - GetFullHistoryNodesCalled func(shardId uint32) ([]*data.NodeData, error) - GetAllFullHistoryNodesCalled func() ([]*data.NodeData, error) + GetObserversCalled func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllObserversCalled func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetObserversOnePerShardCalled func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetFullHistoryNodesOnePerShardCalled func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetFullHistoryNodesCalled func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) + GetAllFullHistoryNodesCalled func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) GetShardIDsCalled func() []uint32 ComputeShardIdCalled func(addressBuff []byte) (uint32, error) CallGetRestEndPointCalled func(address string, path string, value interface{}) (int, error) @@ -75,9 +75,9 @@ func (ps *ProcessorStub) ApplyConfig(cfg *config.Config) error { } // GetObservers will call the GetObserversCalled handler if not nil -func (ps *ProcessorStub) GetObservers(shardID uint32) ([]*data.NodeData, error) { +func (ps *ProcessorStub) GetObservers(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ps.GetObserversCalled != nil { - return ps.GetObserversCalled(shardID) + return ps.GetObserversCalled(shardID, dataAvailability) } return nil, errNotImplemented @@ -120,45 +120,45 @@ func (ps *ProcessorStub) GetShardIDs() []uint32 { } // GetAllObservers will call the GetAllNodesCalled if not nil -func (ps *ProcessorStub) GetAllObservers() ([]*data.NodeData, error) { +func (ps *ProcessorStub) GetAllObservers(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ps.GetAllObserversCalled != nil { - return ps.GetAllObserversCalled() + return ps.GetAllObserversCalled(dataAvailability) } return nil, nil } // GetObserversOnePerShard will call the GetObserversOnePerShardCalled if not nil -func (ps *ProcessorStub) GetObserversOnePerShard() ([]*data.NodeData, error) { +func (ps *ProcessorStub) GetObserversOnePerShard(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ps.GetObserversOnePerShardCalled != nil { - return ps.GetObserversOnePerShardCalled() + return ps.GetObserversOnePerShardCalled(dataAvailability) } return nil, nil } // GetFullHistoryNodesOnePerShard will call the GetFullHistoryNodesOnePerShardCalled if not nil -func (ps *ProcessorStub) GetFullHistoryNodesOnePerShard() ([]*data.NodeData, error) { +func (ps *ProcessorStub) GetFullHistoryNodesOnePerShard(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ps.GetFullHistoryNodesOnePerShardCalled != nil { - return ps.GetFullHistoryNodesOnePerShardCalled() + return ps.GetFullHistoryNodesOnePerShardCalled(dataAvailability) } return nil, nil } // GetFullHistoryNodes will call the GetFullHistoryNodes handler if not nil -func (ps *ProcessorStub) GetFullHistoryNodes(shardID uint32) ([]*data.NodeData, error) { +func (ps *ProcessorStub) GetFullHistoryNodes(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ps.GetFullHistoryNodesCalled != nil { - return ps.GetFullHistoryNodesCalled(shardID) + return ps.GetFullHistoryNodesCalled(shardID, dataAvailability) } return nil, errNotImplemented } // GetAllFullHistoryNodes will call the GetAllFullHistoryNodes handler if not nil -func (ps *ProcessorStub) GetAllFullHistoryNodes() ([]*data.NodeData, error) { +func (ps *ProcessorStub) GetAllFullHistoryNodes(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if ps.GetAllFullHistoryNodesCalled != nil { - return ps.GetAllFullHistoryNodesCalled() + return ps.GetAllFullHistoryNodesCalled(dataAvailability) } return nil, errNotImplemented diff --git a/process/nodeGroupProcessor.go b/process/nodeGroupProcessor.go index 6f4088c6..2c869ceb 100644 --- a/process/nodeGroupProcessor.go +++ b/process/nodeGroupProcessor.go @@ -53,7 +53,7 @@ func NewNodeGroupProcessor( // IsOldStorageForToken returns true if the token is stored in the old fashion func (hbp *NodeGroupProcessor) IsOldStorageForToken(tokenID string, nonce uint64) (bool, error) { - observers, err := hbp.proc.GetAllObservers() + observers, err := hbp.proc.GetAllObservers(data.AvailabilityRecent) if err != nil { return false, err } @@ -120,7 +120,7 @@ func (hbp *NodeGroupProcessor) getHeartbeatsFromApi() (*data.HeartbeatResponse, responseMap := make(map[string]data.PubKeyHeartbeat) for _, shard := range shardIDs { - observers, err := hbp.proc.GetObservers(shard) + observers, err := hbp.proc.GetObservers(shard, data.AvailabilityRecent) if err != nil { log.Error("could not get observers", "shard", shard, "error", err.Error()) continue diff --git a/process/nodeGroupProcessor_test.go b/process/nodeGroupProcessor_test.go index 918a8564..336653e8 100644 --- a/process/nodeGroupProcessor_test.go +++ b/process/nodeGroupProcessor_test.go @@ -121,7 +121,7 @@ func TestNodeGroupProcessor_GetHeartbeatDataOkValuesShouldPass(t *testing.T) { GetShardIDsCalled: func() []uint32 { return providedShardIDs }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { assert.Contains(t, providedShardIDs, shardId) var obs []*data.NodeData @@ -212,7 +212,7 @@ func TestNodeGroupProcessor_GetHeartbeatDataShouldReturnDataFromApiBecauseCacheD GetShardIDsCalled: func() []uint32 { return []uint32{providedShardID} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { assert.Equal(t, providedShardID, shardId) var obs []*data.NodeData obs = append(obs, &data.NodeData{ @@ -328,7 +328,7 @@ func TestNodeGroupProcessor_GetHeartbeatDataShouldReturnDataFromApiBecauseCacheD GetShardIDsCalled: func() []uint32 { return []uint32{providedShardID0, providedShardID1} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { var obs []*data.NodeData switch counter { case 0: @@ -407,7 +407,7 @@ func TestNodeGroupProcessor_CacheShouldUpdate(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{providedShardID} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { assert.Equal(t, providedShardID, shardId) var obs []*data.NodeData obs = append(obs, &data.NodeData{ @@ -471,7 +471,7 @@ func TestNodeGroupProcessor_NoDataForAShardShouldNotUpdateCache(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{providedShardID0, providedShardID1} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { var obs []*data.NodeData if shardId == providedShardID0 { obs = append(obs, &data.NodeData{ @@ -525,7 +525,7 @@ func TestNodeGroupProcessor_IsOldStorageForToken(t *testing.T) { proc, _ := process.NewNodeGroupProcessor( &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "addr0", ShardId: 0}, {Address: "addr1", ShardId: 1}, @@ -548,7 +548,7 @@ func TestNodeGroupProcessor_IsOldStorageForToken(t *testing.T) { proc, _ := process.NewNodeGroupProcessor( &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "addr0", ShardId: 0}, {Address: "addr1", ShardId: 1}, @@ -574,7 +574,7 @@ func TestNodeGroupProcessor_IsOldStorageForToken(t *testing.T) { proc, _ := process.NewNodeGroupProcessor( &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "addr0", ShardId: 0}, {Address: "addr1", ShardId: 1}, @@ -600,7 +600,7 @@ func TestNodeGroupProcessor_IsOldStorageForToken(t *testing.T) { proc, _ := process.NewNodeGroupProcessor( &mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "addr0", ShardId: 0}, {Address: "addr1", ShardId: 1}, diff --git a/process/nodeStatusProcessor.go b/process/nodeStatusProcessor.go index 72f588ef..d02f44e9 100644 --- a/process/nodeStatusProcessor.go +++ b/process/nodeStatusProcessor.go @@ -90,7 +90,7 @@ func NewNodeStatusProcessor( // GetNetworkStatusMetrics will simply forward the network status metrics from an observer in the given shard func (nsp *NodeStatusProcessor) GetNetworkStatusMetrics(shardID uint32) (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetObservers(shardID) + observers, err := nsp.proc.GetObservers(shardID, data.AvailabilityRecent) if err != nil { return nil, err } @@ -114,7 +114,7 @@ func (nsp *NodeStatusProcessor) GetNetworkStatusMetrics(shardID uint32) (*data.G // GetNetworkConfigMetrics will simply forward the network config metrics from an observer in the given shard func (nsp *NodeStatusProcessor) GetNetworkConfigMetrics() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetAllObservers() + observers, err := nsp.proc.GetAllObservers(data.AvailabilityRecent) if err != nil { return nil, err } @@ -138,7 +138,7 @@ func (nsp *NodeStatusProcessor) GetNetworkConfigMetrics() (*data.GenericAPIRespo // GetEnableEpochsMetrics will simply forward the activation epochs config metrics from an observer func (nsp *NodeStatusProcessor) GetEnableEpochsMetrics() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetAllObservers() + observers, err := nsp.proc.GetAllObservers(data.AvailabilityRecent) if err != nil { return nil, err } @@ -165,7 +165,7 @@ func (nsp *NodeStatusProcessor) GetAllIssuedESDTs(tokenType string) (*data.Gener return nil, ErrInvalidTokenType } - observers, err := nsp.proc.GetObservers(core.MetachainShardId) + observers, err := nsp.proc.GetObservers(core.MetachainShardId, data.AvailabilityRecent) if err != nil { return nil, err } @@ -193,7 +193,7 @@ func (nsp *NodeStatusProcessor) GetAllIssuedESDTs(tokenType string) (*data.Gener // GetDelegatedInfo returns the delegated info from nodes func (nsp *NodeStatusProcessor) GetDelegatedInfo() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetObservers(core.MetachainShardId) + observers, err := nsp.proc.GetObservers(core.MetachainShardId, data.AvailabilityRecent) if err != nil { return nil, err } @@ -217,7 +217,7 @@ func (nsp *NodeStatusProcessor) GetDelegatedInfo() (*data.GenericAPIResponse, er // GetDirectStakedInfo returns the delegated info from nodes func (nsp *NodeStatusProcessor) GetDirectStakedInfo() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetObservers(core.MetachainShardId) + observers, err := nsp.proc.GetObservers(core.MetachainShardId, data.AvailabilityRecent) if err != nil { return nil, err } @@ -241,7 +241,7 @@ func (nsp *NodeStatusProcessor) GetDirectStakedInfo() (*data.GenericAPIResponse, // GetRatingsConfig will simply forward the ratings configuration from an observer func (nsp *NodeStatusProcessor) GetRatingsConfig() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetAllObservers() + observers, err := nsp.proc.GetAllObservers(data.AvailabilityRecent) if err != nil { return nil, err } @@ -264,7 +264,7 @@ func (nsp *NodeStatusProcessor) GetRatingsConfig() (*data.GenericAPIResponse, er } func (nsp *NodeStatusProcessor) getNodeStatusMetrics(shardID uint32) (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetObservers(shardID) + observers, err := nsp.proc.GetObservers(shardID, data.AvailabilityRecent) if err != nil { return nil, err } @@ -344,7 +344,7 @@ func getMinNonce(noncesSlice []uint64) uint64 { } func (nsp *NodeStatusProcessor) getShardsIDs() (map[uint32]struct{}, error) { - observers, err := nsp.proc.GetAllObservers() + observers, err := nsp.proc.GetAllObservers(data.AvailabilityAll) if err != nil { return nil, err } @@ -446,7 +446,7 @@ func getUint(value interface{}) uint64 { // GetGenesisNodesPubKeys will return genesis nodes public keys func (nsp *NodeStatusProcessor) GetGenesisNodesPubKeys() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetAllObservers() + observers, err := nsp.proc.GetAllObservers(data.AvailabilityAll) if err != nil { return nil, err } @@ -470,7 +470,7 @@ func (nsp *NodeStatusProcessor) GetGenesisNodesPubKeys() (*data.GenericAPIRespon // GetGasConfigs will return gas configs func (nsp *NodeStatusProcessor) GetGasConfigs() (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetAllObservers() + observers, err := nsp.proc.GetAllObservers(data.AvailabilityRecent) if err != nil { return nil, err } @@ -494,7 +494,7 @@ func (nsp *NodeStatusProcessor) GetGasConfigs() (*data.GenericAPIResponse, error // GetEpochStartData will return the epoch-start data for the given epoch and shard func (nsp *NodeStatusProcessor) GetEpochStartData(epoch uint32, shardID uint32) (*data.GenericAPIResponse, error) { - observers, err := nsp.proc.GetObservers(shardID) + observers, err := nsp.proc.GetObservers(shardID, data.AvailabilityAll) if err != nil { return nil, err } diff --git a/process/nodeStatusProcessor_test.go b/process/nodeStatusProcessor_test.go index e1ac1394..fd37f2c6 100644 --- a/process/nodeStatusProcessor_test.go +++ b/process/nodeStatusProcessor_test.go @@ -44,7 +44,7 @@ func TestNodeStatusProcessor_GetConfigMetricsGetRestEndPointError(t *testing.T) localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -66,7 +66,7 @@ func TestNodeStatusProcessor_GetConfigMetrics(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -103,7 +103,7 @@ func TestNodeStatusProcessor_GetNetworkMetricsGetObserversFailedShouldErr(t *tes localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return nil, localErr }, }, @@ -121,7 +121,7 @@ func TestNodeStatusProcessor_GetNetworkMetricsGetRestEndPointError(t *testing.T) localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -143,7 +143,7 @@ func TestNodeStatusProcessor_GetNetworkMetrics(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -178,13 +178,13 @@ func TestNodeStatusProcessor_GetLatestBlockNonce(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() (observers []*data.NodeData, err error) { + GetAllObserversCalled: func(_ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, {Address: "address2", ShardId: core.MetachainShardId}, }, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: "address1", ShardId: 0}, @@ -233,7 +233,7 @@ func TestNodeStatusProcessor_GetAllIssuedEDTsGetObserversFailedShouldErr(t *test localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return nil, localErr }, }, @@ -251,7 +251,7 @@ func TestNodeStatusProcessor_GetAllIssuedESDTsGetRestEndPointError(t *testing.T) localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -274,7 +274,7 @@ func TestNodeStatusProcessor_GetAllIssuedESDTs(t *testing.T) { tokens := []string{"ESDT-5t6y7u", "NFT-9i8u7y-03"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -313,7 +313,7 @@ func TestNodeStatusProcessor_ApiPathIsCorrect(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -336,7 +336,7 @@ func TestNodeStatusProcessor_GetDelegatedInfoGetObserversFailedShouldErr(t *test localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return nil, localErr }, }, @@ -354,7 +354,7 @@ func TestNodeStatusProcessor_GetDelegatedInfoGetRestEndPointError(t *testing.T) localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -377,7 +377,7 @@ func TestNodeStatusProcessor_GetDelegatedInfo(t *testing.T) { expectedResp := &data.GenericAPIResponse{Data: "delegated info"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -402,7 +402,7 @@ func TestNodeStatusProcessor_GetDirectStakedInfoGetObserversFailedShouldErr(t *t localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return nil, localErr }, }, @@ -420,7 +420,7 @@ func TestNodeStatusProcessor_GetDirectStakedInfoGetRestEndPointError(t *testing. localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -443,7 +443,7 @@ func TestNodeStatusProcessor_GetDirectStakedInfo(t *testing.T) { expectedResp := &data.GenericAPIResponse{Data: "direct staked info"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -468,7 +468,7 @@ func TestNodeStatusProcessor_GetEnableEpochsMetricsGetEndpointErr(t *testing.T) localErr := errors.New("local error") nodesStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "addr1", ShardId: 0}, }, nil @@ -492,7 +492,7 @@ func TestNodeStatusProcessor_GetEnableEpochsMetricsShouldWork(t *testing.T) { key := "smart_contract_deploy" expectedValue := float64(4) nodesStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "addr1", ShardId: 0}, }, nil @@ -528,7 +528,7 @@ func TestNodeStatusProcessor_GetEnableEpochsMetricsGetObserversShouldErr(t *test localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, }, @@ -546,7 +546,7 @@ func TestNodeStatusProcessor_GetRatingsConfigGetAllObserversShouldFail(t *testin localErr := errors.New("local error") nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, localErr }, }, @@ -564,7 +564,7 @@ func TestNodeStatusProcessor_GetRatingsConfig(t *testing.T) { expectedResp := &data.GenericAPIResponse{Data: "ratings config"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() (observers []*data.NodeData, err error) { + GetAllObserversCalled: func(_ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -589,7 +589,7 @@ func TestNodeStatusProcessor_GetGenesisNodesPubKeys(t *testing.T) { expectedResp := &data.GenericAPIResponse{Data: "genesis nodes pub keys"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() (observers []*data.NodeData, err error) { + GetAllObserversCalled: func(_ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -616,7 +616,7 @@ func TestNodeStatusProcessor_GetGasConfigs(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() (observers []*data.NodeData, err error) { + GetAllObserversCalled: func(_ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -639,7 +639,7 @@ func TestNodeStatusProcessor_GetGasConfigs(t *testing.T) { expectedResp := &data.GenericAPIResponse{Data: "gas configs"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() (observers []*data.NodeData, err error) { + GetAllObserversCalled: func(_ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -667,7 +667,7 @@ func TestNodeStatusProcessor_GetTriesStatistics(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -688,7 +688,7 @@ func TestNodeStatusProcessor_GetTriesStatistics(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -717,7 +717,7 @@ func TestNodeStatusProcessor_GetTriesStatistics(t *testing.T) { providedNumNodes := uint64(1234) nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -752,7 +752,7 @@ func TestNodeStatusProcessor_GetEpochStartData(t *testing.T) { t.Parallel() nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil @@ -775,7 +775,7 @@ func TestNodeStatusProcessor_GetEpochStartData(t *testing.T) { expectedResp := &data.GenericAPIResponse{Data: "epoch start data"} nodeStatusProc, _ := NewNodeStatusProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, }, nil diff --git a/process/proofProcessor.go b/process/proofProcessor.go index b6a0877c..a9e2fc7a 100644 --- a/process/proofProcessor.go +++ b/process/proofProcessor.go @@ -217,7 +217,7 @@ func (pp *ProofProcessor) getObserversForAddress(address string) ([]*data.NodeDa return nil, err } - observers, err := pp.proc.GetObservers(shardID) + observers, err := pp.proc.GetObservers(shardID, data.AvailabilityAll) if err != nil { return nil, err } diff --git a/process/proofProcessor_test.go b/process/proofProcessor_test.go index e86c303b..4e1c067f 100644 --- a/process/proofProcessor_test.go +++ b/process/proofProcessor_test.go @@ -61,7 +61,7 @@ func TestProofProcessor_GetProofSendingFailsOnFirstObserverShouldStillSend(t *te ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: addressFail, ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -113,7 +113,7 @@ func TestProofProcessor_VerifyProofSendingFailsOnFirstObserverShouldStillSend(t ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: addressFail, ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -163,7 +163,7 @@ func TestProofProcessor_GetProofDataTrieSendingFailsOnFirstObserverShouldStillSe ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: addressFail, ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -215,7 +215,7 @@ func TestProofProcessor_GetProofCurrentRootHashSendingFailsOnFirstObserverShould ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: addressFail, ShardId: 0}, {Address: "address2", ShardId: 0}, diff --git a/process/scQueryProcessor.go b/process/scQueryProcessor.go index af5206f4..a03dcf27 100644 --- a/process/scQueryProcessor.go +++ b/process/scQueryProcessor.go @@ -47,7 +47,8 @@ func (scQueryProcessor *SCQueryProcessor) ExecuteQuery(query *data.SCQuery) (*vm return nil, err } - observers, err := scQueryProcessor.proc.GetObservers(shardID) + // TODO: if vm queries will allow historical block coordinates, adjust the data availability here + observers, err := scQueryProcessor.proc.GetObservers(shardID, data.AvailabilityRecent) if err != nil { return nil, err } diff --git a/process/scQueryProcessor_test.go b/process/scQueryProcessor_test.go index a4e35932..e3239736 100644 --- a/process/scQueryProcessor_test.go +++ b/process/scQueryProcessor_test.go @@ -62,7 +62,7 @@ func TestSCQueryProcessor_ExecuteQueryGetObserversFailsShouldErr(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return nil, errExpected }, }, testPubKeyConverter) @@ -80,7 +80,7 @@ func TestSCQueryProcessor_ExecuteQuerySendingFailsOnAllObserversShouldErr(t *tes ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -103,7 +103,7 @@ func TestSCQueryProcessor_ExecuteQuery(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "adress1", ShardId: 0}, }, nil @@ -135,7 +135,7 @@ func TestSCQueryProcessor_ExecuteQueryFailsOnRandomErrorShouldErr(t *testing.T) ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -159,7 +159,7 @@ func TestSCQueryProcessor_ExecuteQueryFailsOnBadRequestWithExplicitErrorShouldEr ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, {Address: "address2", ShardId: 0}, diff --git a/process/transactionProcessor.go b/process/transactionProcessor.go index 317e7949..64328750 100644 --- a/process/transactionProcessor.go +++ b/process/transactionProcessor.go @@ -129,7 +129,7 @@ func (tp *TransactionProcessor) SendTransaction(tx *data.Transaction) (int, stri return http.StatusInternalServerError, "", err } - observers, err := tp.proc.GetObservers(shardID) + observers, err := tp.proc.GetObservers(shardID, data.AvailabilityRecent) if err != nil { return http.StatusInternalServerError, "", err } @@ -177,7 +177,7 @@ func (tp *TransactionProcessor) SimulateTransaction(tx *data.Transaction, checkS return nil, err } - observers, err := tp.proc.GetObservers(senderShardID) + observers, err := tp.proc.GetObservers(senderShardID, data.AvailabilityRecent) if err != nil { return nil, err } @@ -205,7 +205,7 @@ func (tp *TransactionProcessor) SimulateTransaction(tx *data.Transaction, checkS }, nil } - observersForReceiverShard, err := tp.proc.GetObservers(receiverShardID) + observersForReceiverShard, err := tp.proc.GetObservers(receiverShardID, data.AvailabilityRecent) if err != nil { return nil, err } @@ -292,7 +292,7 @@ func (tp *TransactionProcessor) SendMultipleTransactions(txs []*data.Transaction txsHashes := make(map[int]string) txsByShardID := tp.groupTxsByShard(txsToSend) for shardID, groupOfTxs := range txsByShardID { - observersInShard, err := tp.proc.GetObservers(shardID) + observersInShard, err := tp.proc.GetObservers(shardID, data.AvailabilityRecent) if err != nil { return data.MultipleTransactionsResponseData{}, ErrMissingObserver } @@ -721,7 +721,7 @@ func (tp *TransactionProcessor) getTxFromObserver( func (tp *TransactionProcessor) getTxFromDestShard(txHash string, dstShardID uint32, withEvents bool) (*transaction.ApiTransactionResult, bool) { // cross shard transaction - destinationShardObservers, err := tp.proc.GetObservers(dstShardID) + destinationShardObservers, err := tp.proc.GetObservers(dstShardID, data.AvailabilityAll) if err != nil { return nil, false } @@ -889,13 +889,13 @@ func (tp *TransactionProcessor) ComputeTransactionHash(tx *data.Transaction) (st func (tp *TransactionProcessor) getNodesInShard(shardID uint32, reqType requestType) ([]*data.NodeData, error) { if reqType == requestTypeFullHistoryNodes { - fullHistoryNodes, err := tp.proc.GetFullHistoryNodes(shardID) + fullHistoryNodes, err := tp.proc.GetFullHistoryNodes(shardID, data.AvailabilityAll) if err == nil && len(fullHistoryNodes) > 0 { return fullHistoryNodes, nil } } - observers, err := tp.proc.GetObservers(shardID) + observers, err := tp.proc.GetObservers(shardID, data.AvailabilityAll) return observers, err } diff --git a/process/transactionProcessor_test.go b/process/transactionProcessor_test.go index 5d39bd3e..cb852999 100644 --- a/process/transactionProcessor_test.go +++ b/process/transactionProcessor_test.go @@ -62,7 +62,7 @@ func createTestProcessorFromScenarioData(testData *scenarioData) *process.Transa ComputeShardIdCalled: func(addressBuff []byte) (uint32, error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "test", @@ -228,7 +228,7 @@ func TestTransactionProcessor_SendTransactionGetObserversFailsShouldErr(t *testi ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return nil, errExpected }, }, @@ -260,7 +260,7 @@ func TestTransactionProcessor_SendTransactionSendingFailsOnAllObserversShouldErr ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "address1", ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -299,7 +299,7 @@ func TestTransactionProcessor_SendTransactionSendingFailsOnFirstObserverShouldSt ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: addressFail, ShardId: 0}, {Address: "address2", ShardId: 0}, @@ -344,7 +344,7 @@ func TestTransactionProcessor_SendMultipleTransactionsShouldWork(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "observer1", ShardId: 0}, }, nil @@ -405,7 +405,7 @@ func TestTransactionProcessor_SendMultipleTransactionsShouldWorkAndSendTxsByShar } return 0, nil }, - GetObserversCalled: func(shardID uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { if shardID == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -467,7 +467,7 @@ func TestTransactionProcessor_SimulateTransactionShouldWork(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: "observer1", ShardId: 0}, }, nil @@ -513,7 +513,7 @@ func TestTransactionProcessor_SimulateTransactionCrossShardOkOnSenderFailOnRecei } return 1, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { if shardId == 0 { return []*data.NodeData{{Address: obsSh0, ShardId: 0}}, nil } @@ -578,7 +578,7 @@ func TestTransactionProcessor_GetTransactionStatusIntraShardTransaction(t *testi GetShardIDsCalled: func() []uint32 { return []uint32{0, 1} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -643,7 +643,7 @@ func TestTransactionProcessor_GetTransactionStatusCrossShardTransaction(t *testi GetShardIDsCalled: func() []uint32 { return []uint32{0} }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: addrObs1, ShardId: 1}, }, nil @@ -699,7 +699,7 @@ func TestTransactionProcessor_GetTransactionStatusCrossShardTransactionDestinati GetShardIDsCalled: func() []uint32 { return []uint32{0, 1} }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -766,12 +766,12 @@ func TestTransactionProcessor_GetTransactionStatusWithSenderAddressCrossShard(t } return 0, nil }, - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, }, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: addrObs1, ShardId: 1}, {Address: addrObs2, ShardId: 1}, @@ -849,7 +849,7 @@ func TestTransactionProcessor_GetTransactionStatusWithSenderAddressIntraShard(t ComputeShardIdCalled: func(addressBuff []byte) (uint32, error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, err error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, err error) { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, {Address: addrObs1, ShardId: 0}, @@ -1066,7 +1066,7 @@ func TestTransactionProcessor_GetTransactionShouldWork(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{0, 1} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1121,7 +1121,7 @@ func TestTransactionProcessor_GetTransactionShouldCallOtherObserverInShardIfHttp GetShardIDsCalled: func() []uint32 { return []uint32{0} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1166,12 +1166,12 @@ func TestTransactionProcessor_GetTransactionShouldNotCallOtherObserverInShardIfN ComputeShardIdCalled: func(_ []byte) (uint32, error) { return 0, nil }, - GetObserversOnePerShardCalled: func() ([]*data.NodeData, error) { + GetObserversOnePerShardCalled: func(_ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, }, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1239,7 +1239,7 @@ func TestTransactionProcessor_GetTransactionWithEventsFirstFromDstShardAndAfterS GetShardIDsCalled: func() []uint32 { return []uint32{1, 0} }, - GetFullHistoryNodesCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetFullHistoryNodesCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1335,7 +1335,7 @@ func TestTransactionProcessor_GetTransactionPool(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{0, 1} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1423,7 +1423,7 @@ func TestTransactionProcessor_GetTransactionPool(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{0, 1, 2} }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1502,7 +1502,7 @@ func TestTransactionProcessor_GetTransactionPool(t *testing.T) { addrObs0 := "observer0" tp, _ := process.NewTransactionProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, uint32(0), shardId) if shardId == 0 { return []*data.NodeData{ @@ -1583,7 +1583,7 @@ func TestTransactionProcessor_GetTransactionPool(t *testing.T) { } tp, _ := process.NewTransactionProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { if shardId == 0 { return []*data.NodeData{ {Address: addrObs0, ShardId: 0}, @@ -1643,7 +1643,7 @@ func TestTransactionProcessor_GetTransactionPool(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (uint32, error) { return providedShardId, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, providedShardId, shardId) return []*data.NodeData{ {Address: addrObs0, ShardId: providedShardId}, @@ -1723,7 +1723,7 @@ func TestTransactionProcessor_GetTransactionPool(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (uint32, error) { return providedShardId, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, providedShardId, shardId) return []*data.NodeData{ {Address: addrObs0, ShardId: providedShardId}, @@ -1924,7 +1924,7 @@ func TestTransactionProcessor_GetProcessedTransactionStatus(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (uint32, error) { return providedShardId, nil }, - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { require.Equal(t, providedShardId, shardId) return []*data.NodeData{ { diff --git a/process/txcost/transactionCostProcessor.go b/process/txcost/transactionCostProcessor.go index a872a33d..9ff18d0c 100644 --- a/process/txcost/transactionCostProcessor.go +++ b/process/txcost/transactionCostProcessor.go @@ -78,7 +78,7 @@ func (tcp *transactionCostProcessor) ResolveCostRequest(tx *data.Transaction) (* func (tcp *transactionCostProcessor) doCostRequests(senderShardID, receiverShardID uint32, tx *data.Transaction) (*data.TxCostResponseData, error) { shouldExecuteOnSource := senderShardID != receiverShardID && len(tcp.responses) == 0 if shouldExecuteOnSource { - observers, errGet := tcp.proc.GetObservers(senderShardID) + observers, errGet := tcp.proc.GetObservers(senderShardID, data.AvailabilityRecent) if errGet != nil { return nil, errGet } @@ -93,7 +93,7 @@ func (tcp *transactionCostProcessor) doCostRequests(senderShardID, receiverShard } } - observers, err := tcp.proc.GetObservers(receiverShardID) + observers, err := tcp.proc.GetObservers(receiverShardID, data.AvailabilityRecent) if err != nil { return nil, err } @@ -191,7 +191,7 @@ func (tcp *transactionCostProcessor) processScResult( txFromScr := convertSCRInTransaction(scr, originalTx) tcp.txsFromSCR = append(tcp.txsFromSCR, txFromScr) - observers, err := tcp.proc.GetObservers(scrReceiverShardID) + observers, err := tcp.proc.GetObservers(scrReceiverShardID, data.AvailabilityRecent) if err != nil { return nil, err } diff --git a/process/txcost/transactionCostProcessor_test.go b/process/txcost/transactionCostProcessor_test.go index 7e077b3c..c5f05ba4 100644 --- a/process/txcost/transactionCostProcessor_test.go +++ b/process/txcost/transactionCostProcessor_test.go @@ -33,7 +33,7 @@ func TestTransactionCostProcessor_RezolveCostRequestWith3LevelsOfAsyncCalls(t *t count := 0 coreProc := &mock.ProcessorStub{ - GetObserversCalled: func(shardId uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{}}, nil }, ComputeShardIdCalled: func(addressBuff []byte) (uint32, error) { diff --git a/process/validatorStatisticsProcessor.go b/process/validatorStatisticsProcessor.go index b9fe2d8c..b52f3dc0 100644 --- a/process/validatorStatisticsProcessor.go +++ b/process/validatorStatisticsProcessor.go @@ -57,7 +57,7 @@ func (vsp *ValidatorStatisticsProcessor) GetValidatorStatistics() (*data.Validat } func (vsp *ValidatorStatisticsProcessor) getValidatorStatisticsFromApi() (*data.ValidatorStatisticsResponse, error) { - observers, errFetchObs := vsp.proc.GetObservers(core.MetachainShardId) + observers, errFetchObs := vsp.proc.GetObservers(core.MetachainShardId, data.AvailabilityRecent) if errFetchObs != nil { return nil, errFetchObs } diff --git a/process/validatorStatisticsProcessor_test.go b/process/validatorStatisticsProcessor_test.go index f0ed7334..88549ddf 100644 --- a/process/validatorStatisticsProcessor_test.go +++ b/process/validatorStatisticsProcessor_test.go @@ -64,7 +64,7 @@ func TestValidatorStatisticsProcessor_GetValidatorStatisticsDataOkValuesShouldPa t.Parallel() hp, err := process.NewValidatorStatisticsProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { var obs []*data.NodeData obs = append(obs, &data.NodeData{ ShardId: core.MetachainShardId, @@ -91,7 +91,7 @@ func TestValidatorStatisticsProcessor_GetValidatorStatisticsNoMetaObserverShould t.Parallel() hp, err := process.NewValidatorStatisticsProcessor(&mock.ProcessorStub{ - GetAllObserversCalled: func() ([]*data.NodeData, error) { + GetAllObserversCalled: func(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { var obs []*data.NodeData obs = append(obs, &data.NodeData{ ShardId: 1, @@ -122,7 +122,7 @@ func TestValidatorStatisticsProcessor_GetValidatorStatisticsShouldReturnDataFrom cacher := &mock.ValStatsCacherMock{Data: nil} hp, err := process.NewValidatorStatisticsProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{Address: "obs1", ShardId: core.MetachainShardId}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { @@ -162,7 +162,7 @@ func TestValidatorStatisticsProcessor_CacheShouldUpdate(t *testing.T) { numOfTimesHttpWasCalled := int32(0) cacher := &mock.ValStatsCacherMock{} hp, err := process.NewValidatorStatisticsProcessor(&mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{{Address: "obs1", ShardId: core.MetachainShardId}}, nil }, CallGetRestEndPointCalled: func(address string, path string, value interface{}) (int, error) { From ec2c8a6a0f020667d9fc4a772e851a41c358d87d Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Wed, 5 Jul 2023 16:10:38 +0300 Subject: [PATCH 02/13] fixes + tests --- observer/baseNodeProvider.go | 442 ++++++---------------------- observer/baseNodeProvider_test.go | 405 ++++++++----------------- observer/holder/nodesHolder.go | 426 +++++++++++++++++++++++++++ observer/holder/nodesHolder_test.go | 249 ++++++++++++++++ observer/interface.go | 11 + 5 files changed, 903 insertions(+), 630 deletions(-) create mode 100644 observer/holder/nodesHolder.go create mode 100644 observer/holder/nodesHolder_test.go diff --git a/observer/baseNodeProvider.go b/observer/baseNodeProvider.go index 121bcebd..5fbe5b22 100644 --- a/observer/baseNodeProvider.go +++ b/observer/baseNodeProvider.go @@ -3,23 +3,20 @@ package observer import ( "fmt" "sort" - "strings" "sync" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-proxy-go/config" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/holder" ) type baseNodeProvider struct { - mutNodes sync.RWMutex - shardIds []uint32 - configurationFilePath string - syncedNodes []*data.NodeData - outOfSyncNodes []*data.NodeData - syncedFallbackNodes []*data.NodeData - outOfSyncFallbackNodes []*data.NodeData - lastSyncedNodes map[uint32]*data.NodeData + mutNodes sync.RWMutex + shardIds []uint32 + configurationFilePath string + regularNodes NodesHolder + snapshotlessNodes NodesHolder } func (bnp *baseNodeProvider) initNodes(nodes []*data.NodeData) error { @@ -29,9 +26,6 @@ func (bnp *baseNodeProvider) initNodes(nodes []*data.NodeData) error { newNodes := make(map[uint32][]*data.NodeData) for _, observer := range nodes { - if observer.IsFallback && observer.IsSnapshotless { - return ErrObserverCannotBeBothFallbackAndSnapshotless - } shardId := observer.ShardId newNodes[shardId] = append(newNodes[shardId], observer) } @@ -42,12 +36,18 @@ func (bnp *baseNodeProvider) initNodes(nodes []*data.NodeData) error { } bnp.mutNodes.Lock() + defer bnp.mutNodes.Unlock() + bnp.shardIds = getSortedShardIDsSlice(newNodes) - bnp.syncedNodes, bnp.syncedFallbackNodes = initAllNodesSlice(newNodes) - bnp.outOfSyncNodes = make([]*data.NodeData, 0) - bnp.outOfSyncFallbackNodes = make([]*data.NodeData, 0) - bnp.lastSyncedNodes = make(map[uint32]*data.NodeData) - bnp.mutNodes.Unlock() + syncedNodes, syncedFallbackNodes, syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes := initAllNodesSlice(newNodes) + bnp.regularNodes, err = holder.NewNodesHolder(syncedNodes, syncedFallbackNodes, bnp.shardIds, data.AvailabilityAll) + if err != nil { + return err + } + bnp.snapshotlessNodes, err = holder.NewNodesHolder(syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes, bnp.shardIds, data.AvailabilityRecent) + if err != nil { + return err + } return nil } @@ -75,19 +75,15 @@ func (bnp *baseNodeProvider) GetAllNodesWithSyncState() []*data.NodeData { defer bnp.mutNodes.RUnlock() nodesSlice := make([]*data.NodeData, 0) - for _, node := range bnp.syncedNodes { - nodesSlice = append(nodesSlice, node) - } - for _, node := range bnp.outOfSyncNodes { - nodesSlice = append(nodesSlice, node) - } - for _, node := range bnp.syncedFallbackNodes { - nodesSlice = append(nodesSlice, node) - } - for _, node := range bnp.outOfSyncFallbackNodes { - nodesSlice = append(nodesSlice, node) - } - + nodesSlice = append(nodesSlice, bnp.regularNodes.GetSyncedNodes()...) + nodesSlice = append(nodesSlice, bnp.regularNodes.GetOutOfSyncNodes()...) + nodesSlice = append(nodesSlice, bnp.regularNodes.GetSyncedFallbackNodes()...) + nodesSlice = append(nodesSlice, bnp.regularNodes.GetOutOfSyncFallbackNodes()...) + + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetSyncedNodes()...) + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetOutOfSyncNodes()...) + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetSyncedFallbackNodes()...) + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetOutOfSyncFallbackNodes()...) return nodesSlice } @@ -100,300 +96,23 @@ func (bnp *baseNodeProvider) UpdateNodesBasedOnSyncState(nodesWithSyncStatus []* bnp.mutNodes.Lock() defer bnp.mutNodes.Unlock() - syncedNodes, syncedFallbackNodes, outOfSyncNodes, err := computeSyncedAndOutOfSyncNodes(nodesWithSyncStatus, bnp.shardIds) - if err != nil { - log.Error("cannot update nodes based on sync state", "error", err) - return - } - - sameNumOfSynced := len(bnp.syncedNodes) == len(syncedNodes) - sameNumOfSyncedFallback := len(bnp.syncedFallbackNodes) == len(syncedFallbackNodes) - if sameNumOfSynced && sameNumOfSyncedFallback && len(outOfSyncNodes) == 0 { - bnp.printSyncedNodesInShardsUnprotected() - // early exit as all the nodes are in sync - return - } - - syncedNodesMap := nodesSliceToShardedMap(syncedNodes) - syncedFallbackNodesMap := nodesSliceToShardedMap(syncedFallbackNodes) - - bnp.removeOutOfSyncNodesUnprotected(outOfSyncNodes, syncedNodesMap, syncedFallbackNodesMap) - bnp.addSyncedNodesUnprotected(syncedNodes, syncedFallbackNodes) - bnp.printSyncedNodesInShardsUnprotected() -} - -func (bnp *baseNodeProvider) printSyncedNodesInShardsUnprotected() { - inSyncAddresses := make(map[uint32][]string, 0) - for _, syncedNode := range bnp.syncedNodes { - inSyncAddresses[syncedNode.ShardId] = append(inSyncAddresses[syncedNode.ShardId], syncedNode.Address) - } - - inSyncFallbackAddresses := make(map[uint32][]string, 0) - for _, syncedFallbackNode := range bnp.syncedFallbackNodes { - inSyncFallbackAddresses[syncedFallbackNode.ShardId] = append(inSyncFallbackAddresses[syncedFallbackNode.ShardId], syncedFallbackNode.Address) - } - - for _, shardID := range bnp.shardIds { - totalNumOfActiveNodes := len(inSyncAddresses[shardID]) + len(inSyncFallbackAddresses[shardID]) - // if none of them is active, use the backup if exists - hasBackup := bnp.lastSyncedNodes[shardID] != nil - if totalNumOfActiveNodes == 0 && hasBackup { - totalNumOfActiveNodes++ - inSyncAddresses[shardID] = append(inSyncAddresses[shardID], bnp.lastSyncedNodes[shardID].Address) - } - log.Info(fmt.Sprintf("shard %d active nodes", shardID), - "observers count", totalNumOfActiveNodes, - "addresses", strings.Join(inSyncAddresses[shardID], ", "), - "fallback addresses", strings.Join(inSyncFallbackAddresses[shardID], ", ")) - } + regularNodes, snapshotlessNodes := splitNodesByDataAvailability(nodesWithSyncStatus) + bnp.regularNodes.UpdateNodes(regularNodes) + bnp.snapshotlessNodes.UpdateNodes(snapshotlessNodes) } -func computeSyncedAndOutOfSyncNodes(nodes []*data.NodeData, shardIDs []uint32) ([]*data.NodeData, []*data.NodeData, []*data.NodeData, error) { - tempSyncedNodesMap := make(map[uint32][]*data.NodeData) - tempSyncedFallbackNodesMap := make(map[uint32][]*data.NodeData) - tempNotSyncedNodesMap := make(map[uint32][]*data.NodeData) - +func splitNodesByDataAvailability(nodes []*data.NodeData) ([]*data.NodeData, []*data.NodeData) { + regularNodes := make([]*data.NodeData, 0) + snapshotlessNodes := make([]*data.NodeData, 0) for _, node := range nodes { - if node.IsSynced { - if node.IsFallback { - tempSyncedFallbackNodesMap[node.ShardId] = append(tempSyncedFallbackNodesMap[node.ShardId], node) - } else { - tempSyncedNodesMap[node.ShardId] = append(tempSyncedNodesMap[node.ShardId], node) - } - continue - } - - tempNotSyncedNodesMap[node.ShardId] = append(tempNotSyncedNodesMap[node.ShardId], node) - } - - syncedNodes := make([]*data.NodeData, 0) - syncedFallbackNodes := make([]*data.NodeData, 0) - notSyncedNodes := make([]*data.NodeData, 0) - for _, shardID := range shardIDs { - syncedNodes = append(syncedNodes, tempSyncedNodesMap[shardID]...) - syncedFallbackNodes = append(syncedFallbackNodes, tempSyncedFallbackNodesMap[shardID]...) - notSyncedNodes = append(notSyncedNodes, tempNotSyncedNodesMap[shardID]...) - - totalLen := len(tempSyncedNodesMap[shardID]) + len(tempSyncedFallbackNodesMap[shardID]) + len(tempNotSyncedNodesMap[shardID]) - if totalLen == 0 { - return nil, nil, nil, fmt.Errorf("%w for shard %d - no synced or not synced node", ErrWrongObserversConfiguration, shardID) - } - } - - return syncedNodes, syncedFallbackNodes, notSyncedNodes, nil -} - -func (bnp *baseNodeProvider) addSyncedNodesUnprotected(receivedSyncedNodes []*data.NodeData, receivedSyncedFallbackNodes []*data.NodeData) { - syncedNodesPerShard := make(map[uint32][]string) - for _, node := range receivedSyncedNodes { - bnp.removeFromOutOfSyncIfNeededUnprotected(node) - syncedNodesPerShard[node.ShardId] = append(syncedNodesPerShard[node.ShardId], node.Address) - if bnp.isReceivedSyncedNodeExistent(node) { - continue - } - - bnp.syncedNodes = append(bnp.syncedNodes, node) - } - - for _, node := range receivedSyncedFallbackNodes { - bnp.removeFromOutOfSyncIfNeededUnprotected(node) - if bnp.isReceivedSyncedNodeExistentAsFallback(node) { - continue - } - - bnp.syncedFallbackNodes = append(bnp.syncedFallbackNodes, node) - } - - // if there is at least one synced node regular received, clean the backup list - for _, shardId := range bnp.shardIds { - if len(syncedNodesPerShard[shardId]) != 0 { - delete(bnp.lastSyncedNodes, shardId) - } - } -} - -func (bnp *baseNodeProvider) removeFromOutOfSyncIfNeededUnprotected(node *data.NodeData) { - if node.IsFallback { - bnp.removeFallbackFromOutOfSyncListUnprotected(node) - return - } - - bnp.removeRegularFromOutOfSyncListUnprotected(node) -} - -func (bnp *baseNodeProvider) isReceivedSyncedNodeExistent(receivedNode *data.NodeData) bool { - for _, node := range bnp.syncedNodes { - if node.Address == receivedNode.Address && node.ShardId == receivedNode.ShardId { - return true - } - } - - return false -} - -func (bnp *baseNodeProvider) isReceivedSyncedNodeExistentAsFallback(receivedNode *data.NodeData) bool { - for _, node := range bnp.syncedFallbackNodes { - if node.Address == receivedNode.Address && node.ShardId == receivedNode.ShardId { - return true - } - } - - return false -} - -func (bnp *baseNodeProvider) addToOutOfSyncUnprotected(node *data.NodeData) { - if node.IsFallback { - bnp.addFallbackToOutOfSyncUnprotected(node) - return - } - - bnp.addRegularToOutOfSyncUnprotected(node) -} - -func (bnp *baseNodeProvider) addRegularToOutOfSyncUnprotected(node *data.NodeData) { - for _, oosNode := range bnp.outOfSyncNodes { - if oosNode.Address == node.Address && oosNode.ShardId == node.ShardId { - return - } - } - - bnp.outOfSyncNodes = append(bnp.outOfSyncNodes, node) -} - -func (bnp *baseNodeProvider) addFallbackToOutOfSyncUnprotected(node *data.NodeData) { - for _, oosNode := range bnp.outOfSyncFallbackNodes { - if oosNode.Address == node.Address && oosNode.ShardId == node.ShardId { - return + if node.IsSnapshotless { + snapshotlessNodes = append(snapshotlessNodes, node) + } else { + regularNodes = append(regularNodes, node) } } - bnp.outOfSyncFallbackNodes = append(bnp.outOfSyncFallbackNodes, node) -} - -func (bnp *baseNodeProvider) removeOutOfSyncNodesUnprotected( - outOfSyncNodes []*data.NodeData, - syncedNodesMap map[uint32][]*data.NodeData, - syncedFallbackNodesMap map[uint32][]*data.NodeData, -) { - if len(outOfSyncNodes) == 0 { - bnp.outOfSyncNodes = make([]*data.NodeData, 0) - bnp.outOfSyncFallbackNodes = make([]*data.NodeData, 0) - return - } - - for _, outOfSyncNode := range outOfSyncNodes { - hasOneSyncedNode := len(syncedNodesMap[outOfSyncNode.ShardId]) >= 1 - hasEnoughSyncedFallbackNodes := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) > 1 - canDeleteFallbackNode := hasOneSyncedNode || hasEnoughSyncedFallbackNodes - if outOfSyncNode.IsFallback && canDeleteFallbackNode { - bnp.removeNodeUnprotected(outOfSyncNode) - continue - } - - // if trying to delete last fallback, use last known synced node - // if backup node does not exist, keep fallback - hasBackup := bnp.lastSyncedNodes[outOfSyncNode.ShardId] != nil - if outOfSyncNode.IsFallback && hasBackup { - bnp.removeNodeUnprotected(outOfSyncNode) - continue - } - - hasEnoughSyncedNodes := len(syncedNodesMap[outOfSyncNode.ShardId]) >= 1 - if hasEnoughSyncedNodes { - bnp.removeNodeUnprotected(outOfSyncNode) - continue - } - - // trying to remove last synced node - // if fallbacks are available, save this one as backup and use fallbacks - // else, keep using this one - // save this last regular observer as backup in case fallbacks go offline - // also, if this is the old fallback observer which didn't get synced, keep it in list - wasSyncedAtPreviousStep := bnp.isReceivedSyncedNodeExistent(outOfSyncNode) - isBackupObserver := bnp.lastSyncedNodes[outOfSyncNode.ShardId] == outOfSyncNode - isRegularSyncedBefore := !outOfSyncNode.IsFallback && wasSyncedAtPreviousStep - if isRegularSyncedBefore || isBackupObserver { - log.Info("backup observer updated", - "address", outOfSyncNode.Address, - "is fallback", outOfSyncNode.IsFallback, - "shard", outOfSyncNode.ShardId) - bnp.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode - } - hasOneSyncedFallbackNode := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) >= 1 - if hasOneSyncedFallbackNode { - bnp.removeNodeUnprotected(outOfSyncNode) - continue - } - - // safe to delete regular observer, as it is already in lastSyncedNodes map - if !outOfSyncNode.IsFallback { - bnp.removeNodeUnprotected(outOfSyncNode) - continue - } - - // this is a fallback node, with no synced nodes. - // save it as backup and delete it from its list - bnp.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode - bnp.removeNodeUnprotected(outOfSyncNode) - } -} - -func (bnp *baseNodeProvider) removeNodeUnprotected(node *data.NodeData) { - bnp.removeNodeFromSyncedNodesUnprotected(node) - bnp.addToOutOfSyncUnprotected(node) -} - -func (bnp *baseNodeProvider) removeNodeFromSyncedNodesUnprotected(nodeToRemove *data.NodeData) { - if nodeToRemove.IsFallback { - bnp.removeFallbackFromSyncedListUnprotected(nodeToRemove) - return - } - - bnp.removeRegularFromSyncedListUnprotected(nodeToRemove) -} - -func (bnp *baseNodeProvider) removeRegularFromSyncedListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, bnp.syncedNodes) - if nodeIndex == -1 { - return - } - - copy(bnp.syncedNodes[nodeIndex:], bnp.syncedNodes[nodeIndex+1:]) - bnp.syncedNodes[len(bnp.syncedNodes)-1] = nil - bnp.syncedNodes = bnp.syncedNodes[:len(bnp.syncedNodes)-1] -} - -func (bnp *baseNodeProvider) removeFallbackFromSyncedListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, bnp.syncedFallbackNodes) - if nodeIndex == -1 { - return - } - - copy(bnp.syncedFallbackNodes[nodeIndex:], bnp.syncedFallbackNodes[nodeIndex+1:]) - bnp.syncedFallbackNodes[len(bnp.syncedFallbackNodes)-1] = nil - bnp.syncedFallbackNodes = bnp.syncedFallbackNodes[:len(bnp.syncedFallbackNodes)-1] -} - -func (bnp *baseNodeProvider) removeRegularFromOutOfSyncListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, bnp.outOfSyncNodes) - if nodeIndex == -1 { - return - } - - copy(bnp.outOfSyncNodes[nodeIndex:], bnp.outOfSyncNodes[nodeIndex+1:]) - bnp.outOfSyncNodes[len(bnp.outOfSyncNodes)-1] = nil - bnp.outOfSyncNodes = bnp.outOfSyncNodes[:len(bnp.outOfSyncNodes)-1] -} - -func (bnp *baseNodeProvider) removeFallbackFromOutOfSyncListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, bnp.outOfSyncFallbackNodes) - if nodeIndex == -1 { - return - } - - copy(bnp.outOfSyncFallbackNodes[nodeIndex:], bnp.outOfSyncFallbackNodes[nodeIndex+1:]) - bnp.outOfSyncFallbackNodes[len(bnp.outOfSyncFallbackNodes)-1] = nil - bnp.outOfSyncFallbackNodes = bnp.outOfSyncFallbackNodes[:len(bnp.outOfSyncFallbackNodes)-1] + return regularNodes, snapshotlessNodes } // ReloadNodes will reload the observers or the full history observers @@ -428,10 +147,27 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo } bnp.mutNodes.Lock() + defer bnp.mutNodes.Unlock() bnp.shardIds = getSortedShardIDsSlice(newNodes) - bnp.syncedNodes, bnp.syncedFallbackNodes = initAllNodesSlice(newNodes) - bnp.lastSyncedNodes = make(map[uint32]*data.NodeData) - bnp.mutNodes.Unlock() + syncedNodes, syncedFallbackNodes, syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes := initAllNodesSlice(newNodes) + bnp.regularNodes, err = holder.NewNodesHolder(syncedNodes, syncedFallbackNodes, bnp.shardIds, data.AvailabilityAll) + if err != nil { + log.Error("cannot reload regular nodes: NewNodesHolder", "error", err) + return data.NodesReloadResponse{ + OkRequest: true, + Description: "not reloaded", + Error: "cannot create the regular nodes holder: " + err.Error(), + } + } + bnp.snapshotlessNodes, err = holder.NewNodesHolder(syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes, bnp.shardIds, data.AvailabilityRecent) + if err != nil { + log.Error("cannot reload snapshotless nodes: NewNodesHolder", "error", err) + return data.NodesReloadResponse{ + OkRequest: true, + Description: "not reloaded", + Error: "cannot create the snapshotless nodes holder: " + err.Error(), + } + } return data.NodesReloadResponse{ OkRequest: true, @@ -441,23 +177,31 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo } func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { + var syncedNodesSource []*data.NodeData + if dataAvailability == data.AvailabilityRecent && len(bnp.snapshotlessNodes.GetSyncedNodes()) > 0 { + syncedNodesSource = bnp.snapshotlessNodes.GetSyncedNodes() + } else { + syncedNodesSource = bnp.regularNodes.GetSyncedNodes() + } syncedNodes := make([]*data.NodeData, 0) - for _, node := range bnp.syncedNodes { + for _, node := range syncedNodesSource { if node.ShardId != shardId { continue } - // TODO: analyze if only snapshotless observers should be returned for recent availability endpoints - // currently, we only restrict snapshotless observers not to be used for historical requests - if dataAvailability == data.AvailabilityAll && node.IsSnapshotless { - continue - } + syncedNodes = append(syncedNodes, node) } if len(syncedNodes) != 0 { return syncedNodes, nil } - for _, node := range bnp.syncedFallbackNodes { + var fallbackNodesSource []*data.NodeData + if dataAvailability == data.AvailabilityRecent { + fallbackNodesSource = bnp.snapshotlessNodes.GetSyncedNodes() + } else { + fallbackNodesSource = bnp.regularNodes.GetSyncedNodes() + } + for _, node := range fallbackNodesSource { if node.ShardId == shardId { syncedNodes = append(syncedNodes, node) } @@ -466,7 +210,13 @@ func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32, d return syncedNodes, nil } - backupNode, hasBackup := bnp.lastSyncedNodes[shardId] + var lastSyncedNodesMap map[uint32]*data.NodeData + if dataAvailability == data.AvailabilityAll { + lastSyncedNodesMap = bnp.regularNodes.GetLastSyncedNodes() + } else { + lastSyncedNodesMap = bnp.snapshotlessNodes.GetLastSyncedNodes() + } + backupNode, hasBackup := lastSyncedNodesMap[shardId] if hasBackup { return []*data.NodeData{backupNode}, nil } @@ -524,9 +274,11 @@ func prepareReloadResponseMessage(newNodes map[uint32][]*data.NodeData) string { return retString } -func initAllNodesSlice(nodesOnShards map[uint32][]*data.NodeData) ([]*data.NodeData, []*data.NodeData) { - sliceToReturn := make([]*data.NodeData, 0) +func initAllNodesSlice(nodesOnShards map[uint32][]*data.NodeData) ([]*data.NodeData, []*data.NodeData, []*data.NodeData, []*data.NodeData) { + eligibleNodes := make([]*data.NodeData, 0) fallbackNodes := make([]*data.NodeData, 0) + eligibleSnapshotlessNodes := make([]*data.NodeData, 0) + fallbackSnapshotlessNodes := make([]*data.NodeData, 0) shardIDs := getSortedShardIDsSlice(nodesOnShards) finishedShards := make(map[uint32]struct{}) @@ -538,10 +290,18 @@ func initAllNodesSlice(nodesOnShards map[uint32][]*data.NodeData) ([]*data.NodeD } node := nodesOnShards[shardID][i] - if node.IsFallback { - fallbackNodes = append(fallbackNodes, node) + if node.IsSnapshotless { + if node.IsFallback { + fallbackSnapshotlessNodes = append(fallbackSnapshotlessNodes, node) + } else { + eligibleSnapshotlessNodes = append(eligibleSnapshotlessNodes, node) + } } else { - sliceToReturn = append(sliceToReturn, node) + if node.IsFallback { + fallbackNodes = append(fallbackNodes, node) + } else { + eligibleNodes = append(eligibleNodes, node) + } } } @@ -550,7 +310,7 @@ func initAllNodesSlice(nodesOnShards map[uint32][]*data.NodeData) ([]*data.NodeD } } - return sliceToReturn, fallbackNodes + return eligibleNodes, fallbackNodes, eligibleSnapshotlessNodes, fallbackSnapshotlessNodes } func getSortedShardIDsSlice(nodesOnShards map[uint32][]*data.NodeData) []uint32 { @@ -564,15 +324,3 @@ func getSortedShardIDsSlice(nodesOnShards map[uint32][]*data.NodeData) []uint32 return shardIDs } - -func getIndexFromList(providedNode *data.NodeData, list []*data.NodeData) int { - nodeIndex := -1 - for idx, node := range list { - if node.Address == providedNode.Address && node.ShardId == providedNode.ShardId { - nodeIndex = idx - break - } - } - - return nodeIndex -} diff --git a/observer/baseNodeProvider_test.go b/observer/baseNodeProvider_test.go index fe743709..ad898604 100644 --- a/observer/baseNodeProvider_test.go +++ b/observer/baseNodeProvider_test.go @@ -1,13 +1,13 @@ package observer import ( - "errors" "fmt" "sort" "testing" "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/holder" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -172,7 +172,7 @@ func TestInitAllNodesSlice_BalancesNumObserversDistribution(t *testing.T) { "shard meta - id 3", } - resultSynced, resultFallback := initAllNodesSlice(nodesMap) + resultSynced, resultFallback, _, _ := initAllNodesSlice(nodesMap) for i, r := range resultSynced { assert.Equal(t, expectedSyncedOrder[i], r.Address) } @@ -233,7 +233,7 @@ func TestInitAllNodesSlice_UnbalancedNumObserversDistribution(t *testing.T) { "shard meta - id 4", } - resultSynced, resultFallback := initAllNodesSlice(nodesMap) + resultSynced, resultFallback, _, _ := initAllNodesSlice(nodesMap) for i, r := range resultSynced { assert.Equal(t, expectedSyncedOrder[i], r.Address) } @@ -270,7 +270,7 @@ func TestInitAllNodesSlice_EmptyObserversSliceForAShardShouldStillWork(t *testin "shard meta - id 1", } - result, _ := initAllNodesSlice(nodesMap) + result, _, _, _ := initAllNodesSlice(nodesMap) for i, r := range result { assert.Equal(t, expectedOrder[i], r.Address) } @@ -289,7 +289,7 @@ func TestInitAllNodesSlice_SingleShardShouldWork(t *testing.T) { "shard 0 - id 0", } - result, _ := initAllNodesSlice(nodesMap) + result, _, _, _ := initAllNodesSlice(nodesMap) for i, r := range result { assert.Equal(t, expectedOrder[i], r.Address) } @@ -314,50 +314,60 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncState(t *testing.T) { require.Equal(t, []data.NodeData{ {Address: "addr3", ShardId: 0, IsSynced: true}, {Address: "addr7", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr4", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr6", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) } func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldNotRemoveNodeIfNotEnoughLeft(t *testing.T) { t.Parallel() allNodes := prepareNodes(4) + snapshotlessNodes := prepareSnapshotlessNodes(4) nodesMap := nodesSliceToShardedMap(allNodes) bnp := &baseNodeProvider{ configurationFilePath: configurationPath, shardIds: getSortedShardIDsSlice(nodesMap), - syncedNodes: allNodes, - lastSyncedNodes: map[uint32]*data.NodeData{}, + regularNodes: createNodesHolder(allNodes, []uint32{0, 1}), + snapshotlessNodes: createNodesHolder(snapshotlessNodes, []uint32{0, 1}), } - nodesCopy := copyNodes(allNodes) - setSyncedStateToNodes(nodesCopy, false, 0, 2) + nodesCopy := append(copyNodes(allNodes), copyNodes(snapshotlessNodes)...) + setSyncedStateToNodes(nodesCopy, false, 0, 2, 4, 6) bnp.UpdateNodesBasedOnSyncState(nodesCopy) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: true}, {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false}, {Address: "addr2", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) + + require.Equal(t, []data.NodeData{ + {Address: "addr1", ShardId: 0, IsSynced: true, IsSnapshotless: true}, + {Address: "addr3", ShardId: 1, IsSynced: true, IsSnapshotless: true}, + }, convertAndSortSlice(bnp.snapshotlessNodes.GetSyncedNodes())) + require.Equal(t, []data.NodeData{ + {Address: "addr0", ShardId: 0, IsSynced: false, IsSnapshotless: true}, + {Address: "addr2", ShardId: 1, IsSynced: false, IsSnapshotless: true}, + }, convertAndSortSlice(bnp.snapshotlessNodes.GetOutOfSyncNodes())) syncedNodes, err := bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) @@ -370,16 +380,15 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldNotRemoveNodeIfNotEno setSyncedStateToNodes(nodesCopy, false, 1, 3) bnp.UpdateNodesBasedOnSyncState(nodesCopy) - - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.syncedNodes)) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false}, {Address: "addr1", ShardId: 0, IsSynced: false}, {Address: "addr2", ShardId: 1, IsSynced: false}, {Address: "addr3", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) - require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - require.Equal(t, data.NodeData{Address: "addr3", ShardId: 1, IsSynced: false}, *bnp.lastSyncedNodes[1]) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) + require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) + require.Equal(t, data.NodeData{Address: "addr3", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) syncedNodes, err = bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) @@ -398,8 +407,8 @@ func TestBaseNodeProvider_getSyncedNodesUnprotectedShouldWork(t *testing.T) { bnp := &baseNodeProvider{ configurationFilePath: configurationPath, shardIds: getSortedShardIDsSlice(nodesMap), - syncedNodes: allNodes, - lastSyncedNodes: map[uint32]*data.NodeData{}, + regularNodes: createNodesHolder(allNodes, []uint32{0, 1}), + snapshotlessNodes: createEmptyNodesHolder(), } nodesCopy := copyNodes(allNodes) @@ -411,10 +420,10 @@ func TestBaseNodeProvider_getSyncedNodesUnprotectedShouldWork(t *testing.T) { {Address: "addr1", ShardId: 0, IsSynced: true}, {Address: "addr2", ShardId: 1, IsSynced: true}, {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) syncedNodes, err := bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) @@ -432,12 +441,12 @@ func TestBaseNodeProvider_getSyncedNodesUnprotectedShouldWork(t *testing.T) { require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 1, IsSynced: true}, {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false}, {Address: "addr1", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) - require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) + require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) syncedNodes, err = bnp.getSyncedNodesUnprotected(data.AvailabilityAll) require.Nil(t, err) @@ -475,21 +484,21 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr2", ShardId: 0, IsSynced: true}, {Address: "addr4", ShardId: 0, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr3", ShardId: 0, IsSynced: false}, {Address: "addr7", ShardId: 1, IsSynced: false}, {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, 0, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, @@ -518,13 +527,13 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter setSyncedStateToNodes(nodesCopy, false, 2, 3, 4, 7, 8, 9) for i := 0; i < 3; i++ { bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.syncedNodes)) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr3", ShardId: 0, IsSynced: false}, @@ -532,9 +541,9 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: false}, {Address: "addr8", ShardId: 1, IsSynced: false}, {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.lastSyncedNodes[1]) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) + require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) + require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, @@ -555,8 +564,8 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter setSyncedStateToNodes(nodesCopy, false, 0, 1, 5, 6) for i := 0; i < 3; i++ { bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.syncedNodes)) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.syncedFallbackNodes)) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr3", ShardId: 0, IsSynced: false}, @@ -564,15 +573,15 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: false}, {Address: "addr8", ShardId: 1, IsSynced: false}, {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.lastSyncedNodes[1]) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) + require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, @@ -596,21 +605,21 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: true}, {Address: "addr7", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr3", ShardId: 0, IsSynced: false}, {Address: "addr4", ShardId: 0, IsSynced: false}, {Address: "addr8", ShardId: 1, IsSynced: false}, {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, 0, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, @@ -642,17 +651,17 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, 0, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, @@ -671,11 +680,11 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter nodesCopy = copyNodes(nodesCopy) setSyncedStateToNodes(nodesCopy, false, 2, 3, 4, 7, 8, 9) bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.syncedNodes)) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr3", ShardId: 0, IsSynced: false}, @@ -683,13 +692,13 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: false}, {Address: "addr8", ShardId: 1, IsSynced: false}, {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.lastSyncedNodes[1]) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) + require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, @@ -717,22 +726,22 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr3", ShardId: 0, IsSynced: false}, {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - require.Equal(t, 1, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) + require.Equal(t, 1, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, @@ -763,18 +772,18 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, 0, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, @@ -799,21 +808,21 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr3", ShardId: 0, IsSynced: false}, {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, data.NodeData{Address: "addr3", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) // last synced - require.Equal(t, 1, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, data.NodeData{Address: "addr3", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) // last synced + require.Equal(t, 1, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, @@ -837,22 +846,22 @@ func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIter {Address: "addr7", ShardId: 1, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) require.Equal(t, []data.NodeData{ {Address: "addr2", ShardId: 0, IsSynced: false}, {Address: "addr3", ShardId: 0, IsSynced: false}, {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.outOfSyncFallbackNodes)) - require.Equal(t, data.NodeData{Address: "addr3", ShardId: 0, IsSynced: false}, *bnp.lastSyncedNodes[0]) - require.Equal(t, 1, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) + require.Equal(t, data.NodeData{Address: "addr3", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) + require.Equal(t, 1, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, @@ -877,15 +886,15 @@ func syncAllNodesAndCheck(t *testing.T, nodes []*data.NodeData, bnp *baseNodePro {Address: "addr7", ShardId: 1, IsSynced: true}, {Address: "addr8", ShardId: 1, IsSynced: true}, {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.outOfSyncNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) + require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.syncedFallbackNodes)) - require.Equal(t, 0, len(bnp.lastSyncedNodes)) + }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) + require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) require.Equal(t, []data.NodeData{ {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, @@ -918,14 +927,34 @@ func prepareNodes(count int) []*data.NodeData { return nodes } +func prepareSnapshotlessNodes(count int) []*data.NodeData { + nodes := prepareNodes(count) + for _, node := range nodes { + node.IsSnapshotless = true + } + + return nodes +} + +func createNodesHolder(nodes []*data.NodeData, shardIDs []uint32) NodesHolder { + holderInstance, _ := holder.NewNodesHolder(nodes, []*data.NodeData{}, shardIDs, "") + return holderInstance +} + +func createEmptyNodesHolder() NodesHolder { + holderInstance, _ := holder.NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, []uint32{0}, "") + return holderInstance +} + func copyNodes(nodes []*data.NodeData) []*data.NodeData { nodesCopy := make([]*data.NodeData, len(nodes)) for i, node := range nodes { nodesCopy[i] = &data.NodeData{ - ShardId: node.ShardId, - Address: node.Address, - IsSynced: node.IsSynced, - IsFallback: node.IsFallback, + ShardId: node.ShardId, + Address: node.Address, + IsSynced: node.IsSynced, + IsFallback: node.IsFallback, + IsSnapshotless: node.IsSnapshotless, } } @@ -972,207 +1001,17 @@ func TestBaseNodeProvider_GetNodesShouldWorkAccordingToTheAvailability(t *testin IsSnapshotless: false, }, } + syncedNodes, _, syncedSnapshotless, _ := initAllNodesSlice(map[uint32][]*data.NodeData{1: nodes}) bnp := &baseNodeProvider{ - syncedNodes: nodes, + regularNodes: createNodesHolder(syncedNodes, []uint32{0}), + snapshotlessNodes: createNodesHolder(syncedSnapshotless, []uint32{0}), } returnedNodes, err := bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) require.NoError(t, err) require.Equal(t, "addr0", returnedNodes[0].Address) - require.Equal(t, "addr1", returnedNodes[1].Address) returnedNodes, err = bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityAll) require.NoError(t, err) require.Equal(t, "addr1", returnedNodes[0].Address) } - -func TestComputeSyncAndOutOfSyncNodes(t *testing.T) { - t.Parallel() - - t.Run("all nodes synced", testComputeSyncedAndOutOfSyncNodesAllNodesSynced) - t.Run("enough synced nodes", testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers) - t.Run("all nodes are out of sync", testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced) - t.Run("invalid config - no node", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll) - t.Run("invalid config - no node in a shard", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard) - t.Run("edge case - address should not exist in both sync and not-synced lists", testEdgeCaseAddressShouldNotExistInBothLists) -} - -func testComputeSyncedAndOutOfSyncNodesAllNodesSynced(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "2", ShardId: 1, IsSynced: true}, - {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, - } - - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) - require.Equal(t, []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "2", ShardId: 1, IsSynced: true}, - }, synced) - require.Equal(t, []*data.NodeData{ - {Address: "1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, - }, syncedFb) - require.Empty(t, notSynced) -} - -func testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "1", ShardId: 0, IsSynced: false}, - {Address: "2", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "3", ShardId: 1, IsSynced: true}, - {Address: "4", ShardId: 1, IsSynced: false}, - {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, - } - - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) - require.Equal(t, []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "3", ShardId: 1, IsSynced: true}, - }, synced) - require.Equal(t, []*data.NodeData{ - {Address: "2", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, - }, syncedFb) - require.Equal(t, []*data.NodeData{ - {Address: "1", ShardId: 0, IsSynced: false}, - {Address: "4", ShardId: 1, IsSynced: false}, - }, notSynced) -} - -func testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: false}, - {Address: "1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "2", ShardId: 1, IsSynced: false}, - {Address: "3", ShardId: 1, IsSynced: false, IsFallback: true}, - } - - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) - require.Equal(t, []*data.NodeData{}, synced) - require.Equal(t, []*data.NodeData{}, syncedFb) - require.Equal(t, input, notSynced) -} - -func testEdgeCaseAddressShouldNotExistInBothLists(t *testing.T) { - t.Parallel() - - allNodes := prepareNodes(10) - - nodesMap := nodesSliceToShardedMap(allNodes) - bnp := &baseNodeProvider{ - configurationFilePath: configurationPath, - shardIds: getSortedShardIDsSlice(nodesMap), - syncedNodes: allNodes, - } - - setSyncedStateToNodes(allNodes, false, 1, 3, 5, 7, 9) - - bnp.UpdateNodesBasedOnSyncState(allNodes) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr6", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: false}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.outOfSyncNodes)) - require.False(t, slicesHaveCommonObjects(bnp.syncedNodes, bnp.outOfSyncNodes)) - - allNodes = prepareNodes(10) - - bnp.UpdateNodesBasedOnSyncState(allNodes) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true}, - {Address: "addr1", ShardId: 0, IsSynced: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr5", ShardId: 1, IsSynced: true}, - {Address: "addr6", ShardId: 1, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.syncedNodes)) - require.False(t, slicesHaveCommonObjects(bnp.syncedNodes, bnp.outOfSyncNodes)) -} - -func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - var input []*data.NodeData - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs) - require.Error(t, err) - require.Nil(t, synced) - require.Nil(t, syncedFb) - require.Nil(t, notSynced) - - // no node in one shard - shardIDs = []uint32{0, 1} - input = []*data.NodeData{ - { - Address: "0", ShardId: 0, IsSynced: true, - }, - } - synced, syncedFb, notSynced, err = computeSyncedAndOutOfSyncNodes(input, shardIDs) - require.True(t, errors.Is(err, ErrWrongObserversConfiguration)) - require.Nil(t, synced) - require.Nil(t, syncedFb) - require.Nil(t, notSynced) -} - -func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard(t *testing.T) { - t.Parallel() - - // no node in one shard - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - { - Address: "0", ShardId: 0, IsSynced: true, - }, - } - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs) - require.True(t, errors.Is(err, ErrWrongObserversConfiguration)) - require.Nil(t, synced) - require.Nil(t, syncedFb) - require.Nil(t, notSynced) -} - -func slicesHaveCommonObjects(firstSlice []*data.NodeData, secondSlice []*data.NodeData) bool { - nodeDataToStr := func(nd *data.NodeData) string { - return fmt.Sprintf("%s%d", nd.Address, nd.ShardId) - } - firstSliceItems := make(map[string]struct{}) - for _, el := range firstSlice { - firstSliceItems[nodeDataToStr(el)] = struct{}{} - } - - for _, el := range secondSlice { - nodeDataStr := nodeDataToStr(el) - _, found := firstSliceItems[nodeDataStr] - if found { - return true - } - } - - return false -} diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go new file mode 100644 index 00000000..b28f2625 --- /dev/null +++ b/observer/holder/nodesHolder.go @@ -0,0 +1,426 @@ +package holder + +import ( + "errors" + "fmt" + "strings" + "sync" + + logger "github.com/multiversx/mx-chain-logger-go" + "github.com/multiversx/mx-chain-proxy-go/data" +) + +var ( + errEmptyShardIDsList = errors.New("empty shard IDs list") + errWrongConfiguration = errors.New("wrong observers configuration") + log = logger.GetOrCreate("observer/holder") +) + +type nodesHolder struct { + mut sync.RWMutex + syncedNodes []*data.NodeData + outOfSyncNodes []*data.NodeData + syncedFallbackNodes []*data.NodeData + outOfSyncFallbackNodes []*data.NodeData + lastSyncedNodes map[uint32]*data.NodeData + shardIDs []uint32 + availability data.ObserverDataAvailabilityType +} + +// NewNodesHolder will return a new instance of a nodesHolder +func NewNodesHolder(syncedNodes []*data.NodeData, fallbackNodes []*data.NodeData, shardIDs []uint32, availability data.ObserverDataAvailabilityType) (*nodesHolder, error) { + if len(shardIDs) == 0 { + return nil, errEmptyShardIDsList + } + + return &nodesHolder{ + syncedNodes: syncedNodes, + outOfSyncNodes: make([]*data.NodeData, 0), + syncedFallbackNodes: fallbackNodes, + outOfSyncFallbackNodes: make([]*data.NodeData, 0), + lastSyncedNodes: make(map[uint32]*data.NodeData), + shardIDs: shardIDs, + availability: availability, + }, nil +} + +// UpdateNodes will update the internal maps based on the provided nodes +func (nh *nodesHolder) UpdateNodes(nodesWithSyncStatus []*data.NodeData) { + if len(nodesWithSyncStatus) == 0 { + return + } + syncedNodes, syncedFallbackNodes, outOfSyncNodes, err := computeSyncedAndOutOfSyncNodes(nodesWithSyncStatus, nh.shardIDs) + if err != nil { + log.Error("cannot update nodes based on sync state", "error", err) + return + } + + sameNumOfSynced := len(nh.syncedNodes) == len(syncedNodes) + sameNumOfSyncedFallback := len(nh.syncedFallbackNodes) == len(syncedFallbackNodes) + if sameNumOfSynced && sameNumOfSyncedFallback && len(outOfSyncNodes) == 0 { + nh.printSyncedNodesInShardsUnprotected() + // early exit as all the nodes are in sync + return + } + + syncedNodesMap := nodesSliceToShardedMap(syncedNodes) + syncedFallbackNodesMap := nodesSliceToShardedMap(syncedFallbackNodes) + + nh.removeOutOfSyncNodesUnprotected(outOfSyncNodes, syncedNodesMap, syncedFallbackNodesMap) + nh.addSyncedNodesUnprotected(syncedNodes, syncedFallbackNodes) + nh.printSyncedNodesInShardsUnprotected() +} + +// GetSyncedNodes returns all the synced nodes +func (nh *nodesHolder) GetSyncedNodes() []*data.NodeData { + nh.mut.RLock() + defer nh.mut.RUnlock() + + return copyNodes(nh.syncedNodes) +} + +// GetSyncedFallbackNodes returns all the synced fallback nodes +func (nh *nodesHolder) GetSyncedFallbackNodes() []*data.NodeData { + nh.mut.RLock() + defer nh.mut.RUnlock() + + return copyNodes(nh.syncedFallbackNodes) +} + +// GetOutOfSyncNodes returns all the out of sync nodes +func (nh *nodesHolder) GetOutOfSyncNodes() []*data.NodeData { + nh.mut.RLock() + defer nh.mut.RUnlock() + + return copyNodes(nh.outOfSyncNodes) +} + +// GetOutOfSyncFallbackNodes returns all the out of sync fallback nodes +func (nh *nodesHolder) GetOutOfSyncFallbackNodes() []*data.NodeData { + nh.mut.RLock() + defer nh.mut.RUnlock() + + return copyNodes(nh.outOfSyncFallbackNodes) +} + +// GetLastSyncedNodes returns the internal map of the last synced nodes +func (nh *nodesHolder) GetLastSyncedNodes() map[uint32]*data.NodeData { + mapCopy := make(map[uint32]*data.NodeData, 0) + nh.mut.RLock() + for key, value := range nh.lastSyncedNodes { + mapCopy[key] = value + } + nh.mut.RUnlock() + + return mapCopy +} + +// IsInterfaceNil returns true if there is no value under the interface +func (nh *nodesHolder) IsInterfaceNil() bool { + return nh == nil +} + +func copyNodes(nodes []*data.NodeData) []*data.NodeData { + sliceCopy := make([]*data.NodeData, 0, len(nodes)) + for _, node := range nodes { + sliceCopy = append(sliceCopy, node) + } + + return sliceCopy +} + +func (nh *nodesHolder) printSyncedNodesInShardsUnprotected() { + inSyncAddresses := make(map[uint32][]string, 0) + for _, syncedNode := range nh.syncedNodes { + inSyncAddresses[syncedNode.ShardId] = append(inSyncAddresses[syncedNode.ShardId], syncedNode.Address) + } + + inSyncFallbackAddresses := make(map[uint32][]string, 0) + for _, syncedFallbackNode := range nh.syncedFallbackNodes { + inSyncFallbackAddresses[syncedFallbackNode.ShardId] = append(inSyncFallbackAddresses[syncedFallbackNode.ShardId], syncedFallbackNode.Address) + } + + for _, shardID := range nh.shardIDs { + totalNumOfActiveNodes := len(inSyncAddresses[shardID]) + len(inSyncFallbackAddresses[shardID]) + // if none of them is active, use the backup if exists + hasBackup := nh.lastSyncedNodes[shardID] != nil + if totalNumOfActiveNodes == 0 && hasBackup { + totalNumOfActiveNodes++ + inSyncAddresses[shardID] = append(inSyncAddresses[shardID], nh.lastSyncedNodes[shardID].Address) + } + log.Info(fmt.Sprintf("shard %d active nodes", shardID), + "observers count", totalNumOfActiveNodes, + "addresses", strings.Join(inSyncAddresses[shardID], ", "), + "fallback addresses", strings.Join(inSyncFallbackAddresses[shardID], ", ")) + } +} + +func computeSyncedAndOutOfSyncNodes(nodes []*data.NodeData, shardIDs []uint32) ([]*data.NodeData, []*data.NodeData, []*data.NodeData, error) { + tempSyncedNodesMap := make(map[uint32][]*data.NodeData) + tempSyncedFallbackNodesMap := make(map[uint32][]*data.NodeData) + tempNotSyncedNodesMap := make(map[uint32][]*data.NodeData) + + for _, node := range nodes { + if node.IsSynced { + if node.IsFallback { + tempSyncedFallbackNodesMap[node.ShardId] = append(tempSyncedFallbackNodesMap[node.ShardId], node) + } else { + tempSyncedNodesMap[node.ShardId] = append(tempSyncedNodesMap[node.ShardId], node) + } + continue + } + + tempNotSyncedNodesMap[node.ShardId] = append(tempNotSyncedNodesMap[node.ShardId], node) + } + + syncedNodes := make([]*data.NodeData, 0) + syncedFallbackNodes := make([]*data.NodeData, 0) + notSyncedNodes := make([]*data.NodeData, 0) + for _, shardID := range shardIDs { + syncedNodes = append(syncedNodes, tempSyncedNodesMap[shardID]...) + syncedFallbackNodes = append(syncedFallbackNodes, tempSyncedFallbackNodesMap[shardID]...) + notSyncedNodes = append(notSyncedNodes, tempNotSyncedNodesMap[shardID]...) + + totalLen := len(tempSyncedNodesMap[shardID]) + len(tempSyncedFallbackNodesMap[shardID]) + len(tempNotSyncedNodesMap[shardID]) + if totalLen == 0 { + return nil, nil, nil, fmt.Errorf("%w for shard %d - no synced or not synced node", errWrongConfiguration, shardID) + } + } + + return syncedNodes, syncedFallbackNodes, notSyncedNodes, nil +} + +func (nh *nodesHolder) addSyncedNodesUnprotected(receivedSyncedNodes []*data.NodeData, receivedSyncedFallbackNodes []*data.NodeData) { + syncedNodesPerShard := make(map[uint32][]string) + for _, node := range receivedSyncedNodes { + nh.removeFromOutOfSyncIfNeededUnprotected(node) + syncedNodesPerShard[node.ShardId] = append(syncedNodesPerShard[node.ShardId], node.Address) + if nh.isReceivedSyncedNodeExistent(node) { + continue + } + + nh.syncedNodes = append(nh.syncedNodes, node) + } + + for _, node := range receivedSyncedFallbackNodes { + nh.removeFromOutOfSyncIfNeededUnprotected(node) + if nh.isReceivedSyncedNodeExistentAsFallback(node) { + continue + } + + nh.syncedFallbackNodes = append(nh.syncedFallbackNodes, node) + } + + // if there is at least one synced node regular received, clean the backup list + for _, shardId := range nh.shardIDs { + if len(syncedNodesPerShard[shardId]) != 0 { + delete(nh.lastSyncedNodes, shardId) + } + } +} + +func (nh *nodesHolder) removeFromOutOfSyncIfNeededUnprotected(node *data.NodeData) { + if node.IsFallback { + nh.removeFallbackFromOutOfSyncListUnprotected(node) + return + } + + nh.removeRegularFromOutOfSyncListUnprotected(node) +} + +func (nh *nodesHolder) isReceivedSyncedNodeExistent(receivedNode *data.NodeData) bool { + for _, node := range nh.syncedNodes { + if node.Address == receivedNode.Address && node.ShardId == receivedNode.ShardId { + return true + } + } + + return false +} + +func (nh *nodesHolder) isReceivedSyncedNodeExistentAsFallback(receivedNode *data.NodeData) bool { + for _, node := range nh.syncedFallbackNodes { + if node.Address == receivedNode.Address && node.ShardId == receivedNode.ShardId { + return true + } + } + + return false +} + +func (nh *nodesHolder) addToOutOfSyncUnprotected(node *data.NodeData) { + if node.IsFallback { + nh.addFallbackToOutOfSyncUnprotected(node) + return + } + + nh.addRegularToOutOfSyncUnprotected(node) +} + +func (nh *nodesHolder) addRegularToOutOfSyncUnprotected(node *data.NodeData) { + for _, oosNode := range nh.outOfSyncNodes { + if oosNode.Address == node.Address && oosNode.ShardId == node.ShardId { + return + } + } + + nh.outOfSyncNodes = append(nh.outOfSyncNodes, node) +} + +func (nh *nodesHolder) addFallbackToOutOfSyncUnprotected(node *data.NodeData) { + for _, oosNode := range nh.outOfSyncFallbackNodes { + if oosNode.Address == node.Address && oosNode.ShardId == node.ShardId { + return + } + } + + nh.outOfSyncFallbackNodes = append(nh.outOfSyncFallbackNodes, node) +} + +func (nh *nodesHolder) removeOutOfSyncNodesUnprotected( + outOfSyncNodes []*data.NodeData, + syncedNodesMap map[uint32][]*data.NodeData, + syncedFallbackNodesMap map[uint32][]*data.NodeData, +) { + if len(outOfSyncNodes) == 0 { + nh.outOfSyncNodes = make([]*data.NodeData, 0) + nh.outOfSyncFallbackNodes = make([]*data.NodeData, 0) + return + } + + for _, outOfSyncNode := range outOfSyncNodes { + hasOneSyncedNode := len(syncedNodesMap[outOfSyncNode.ShardId]) >= 1 + hasEnoughSyncedFallbackNodes := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) > 1 + canDeleteFallbackNode := hasOneSyncedNode || hasEnoughSyncedFallbackNodes + if outOfSyncNode.IsFallback && canDeleteFallbackNode { + nh.removeNodeUnprotected(outOfSyncNode) + continue + } + + // if trying to delete last fallback, use last known synced node + // if backup node does not exist, keep fallback + hasBackup := nh.lastSyncedNodes[outOfSyncNode.ShardId] != nil + if outOfSyncNode.IsFallback && hasBackup { + nh.removeNodeUnprotected(outOfSyncNode) + continue + } + + hasEnoughSyncedNodes := len(syncedNodesMap[outOfSyncNode.ShardId]) >= 1 + if hasEnoughSyncedNodes { + nh.removeNodeUnprotected(outOfSyncNode) + continue + } + + // trying to remove last synced node + // if fallbacks are available, save this one as backup and use fallbacks + // else, keep using this one + // save this last regular observer as backup in case fallbacks go offline + // also, if this is the old fallback observer which didn't get synced, keep it in list + wasSyncedAtPreviousStep := nh.isReceivedSyncedNodeExistent(outOfSyncNode) + isBackupObserver := nh.lastSyncedNodes[outOfSyncNode.ShardId] == outOfSyncNode + isRegularSyncedBefore := !outOfSyncNode.IsFallback && wasSyncedAtPreviousStep + if isRegularSyncedBefore || isBackupObserver { + log.Info("backup observer updated", + "address", outOfSyncNode.Address, + "is fallback", outOfSyncNode.IsFallback, + "shard", outOfSyncNode.ShardId) + nh.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode + } + hasOneSyncedFallbackNode := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) >= 1 + if hasOneSyncedFallbackNode { + nh.removeNodeUnprotected(outOfSyncNode) + continue + } + + // safe to delete regular observer, as it is already in lastSyncedNodes map + if !outOfSyncNode.IsFallback { + nh.removeNodeUnprotected(outOfSyncNode) + continue + } + + // this is a fallback node, with no synced nodes. + // save it as backup and delete it from its list + nh.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode + nh.removeNodeUnprotected(outOfSyncNode) + } +} + +func (nh *nodesHolder) removeNodeUnprotected(node *data.NodeData) { + nh.removeNodeFromSyncedNodesUnprotected(node) + nh.addToOutOfSyncUnprotected(node) +} + +func (nh *nodesHolder) removeNodeFromSyncedNodesUnprotected(nodeToRemove *data.NodeData) { + if nodeToRemove.IsFallback { + nh.removeFallbackFromSyncedListUnprotected(nodeToRemove) + return + } + + nh.removeRegularFromSyncedListUnprotected(nodeToRemove) +} + +func (nh *nodesHolder) removeRegularFromSyncedListUnprotected(nodeToRemove *data.NodeData) { + nodeIndex := getIndexFromList(nodeToRemove, nh.syncedNodes) + if nodeIndex == -1 { + return + } + + copy(nh.syncedNodes[nodeIndex:], nh.syncedNodes[nodeIndex+1:]) + nh.syncedNodes[len(nh.syncedNodes)-1] = nil + nh.syncedNodes = nh.syncedNodes[:len(nh.syncedNodes)-1] +} + +func (nh *nodesHolder) removeFallbackFromSyncedListUnprotected(nodeToRemove *data.NodeData) { + nodeIndex := getIndexFromList(nodeToRemove, nh.syncedFallbackNodes) + if nodeIndex == -1 { + return + } + + copy(nh.syncedFallbackNodes[nodeIndex:], nh.syncedFallbackNodes[nodeIndex+1:]) + nh.syncedFallbackNodes[len(nh.syncedFallbackNodes)-1] = nil + nh.syncedFallbackNodes = nh.syncedFallbackNodes[:len(nh.syncedFallbackNodes)-1] +} + +func (nh *nodesHolder) removeRegularFromOutOfSyncListUnprotected(nodeToRemove *data.NodeData) { + nodeIndex := getIndexFromList(nodeToRemove, nh.outOfSyncNodes) + if nodeIndex == -1 { + return + } + + copy(nh.outOfSyncNodes[nodeIndex:], nh.outOfSyncNodes[nodeIndex+1:]) + nh.outOfSyncNodes[len(nh.outOfSyncNodes)-1] = nil + nh.outOfSyncNodes = nh.outOfSyncNodes[:len(nh.outOfSyncNodes)-1] +} + +func (nh *nodesHolder) removeFallbackFromOutOfSyncListUnprotected(nodeToRemove *data.NodeData) { + nodeIndex := getIndexFromList(nodeToRemove, nh.outOfSyncFallbackNodes) + if nodeIndex == -1 { + return + } + + copy(nh.outOfSyncFallbackNodes[nodeIndex:], nh.outOfSyncFallbackNodes[nodeIndex+1:]) + nh.outOfSyncFallbackNodes[len(nh.outOfSyncFallbackNodes)-1] = nil + nh.outOfSyncFallbackNodes = nh.outOfSyncFallbackNodes[:len(nh.outOfSyncFallbackNodes)-1] +} + +func getIndexFromList(providedNode *data.NodeData, list []*data.NodeData) int { + nodeIndex := -1 + for idx, node := range list { + if node.Address == providedNode.Address && node.ShardId == providedNode.ShardId { + nodeIndex = idx + break + } + } + + return nodeIndex +} + +func nodesSliceToShardedMap(nodes []*data.NodeData) map[uint32][]*data.NodeData { + newNodes := make(map[uint32][]*data.NodeData) + for _, node := range nodes { + shardId := node.ShardId + newNodes[shardId] = append(newNodes[shardId], node) + } + + return newNodes +} diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go new file mode 100644 index 00000000..741ae0c1 --- /dev/null +++ b/observer/holder/nodesHolder_test.go @@ -0,0 +1,249 @@ +package holder + +import ( + "errors" + "fmt" + "sort" + "testing" + + "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/stretchr/testify/require" +) + +func TestComputeSyncAndOutOfSyncNodes(t *testing.T) { + t.Parallel() + + t.Run("all nodes synced", testComputeSyncedAndOutOfSyncNodesAllNodesSynced) + t.Run("enough synced nodes", testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers) + t.Run("all nodes are out of sync", testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced) + t.Run("invalid config - no node", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll) + t.Run("invalid config - no node in a shard", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard) + t.Run("edge case - address should not exist in both sync and not-synced lists", testEdgeCaseAddressShouldNotExistInBothLists) +} + +func testComputeSyncedAndOutOfSyncNodesAllNodesSynced(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, 1} + input := []*data.NodeData{ + {Address: "0", ShardId: 0, IsSynced: true}, + {Address: "1", ShardId: 0, IsSynced: true, IsFallback: true}, + {Address: "2", ShardId: 1, IsSynced: true}, + {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, + } + + synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) + require.Equal(t, []*data.NodeData{ + {Address: "0", ShardId: 0, IsSynced: true}, + {Address: "2", ShardId: 1, IsSynced: true}, + }, synced) + require.Equal(t, []*data.NodeData{ + {Address: "1", ShardId: 0, IsSynced: true, IsFallback: true}, + {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, + }, syncedFb) + require.Empty(t, notSynced) +} + +func testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, 1} + input := []*data.NodeData{ + {Address: "0", ShardId: 0, IsSynced: true}, + {Address: "1", ShardId: 0, IsSynced: false}, + {Address: "2", ShardId: 0, IsSynced: true, IsFallback: true}, + {Address: "3", ShardId: 1, IsSynced: true}, + {Address: "4", ShardId: 1, IsSynced: false}, + {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, + } + + synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) + require.Equal(t, []*data.NodeData{ + {Address: "0", ShardId: 0, IsSynced: true}, + {Address: "3", ShardId: 1, IsSynced: true}, + }, synced) + require.Equal(t, []*data.NodeData{ + {Address: "2", ShardId: 0, IsSynced: true, IsFallback: true}, + {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, + }, syncedFb) + require.Equal(t, []*data.NodeData{ + {Address: "1", ShardId: 0, IsSynced: false}, + {Address: "4", ShardId: 1, IsSynced: false}, + }, notSynced) +} + +func testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, 1} + input := []*data.NodeData{ + {Address: "0", ShardId: 0, IsSynced: false}, + {Address: "1", ShardId: 0, IsSynced: false, IsFallback: true}, + {Address: "2", ShardId: 1, IsSynced: false}, + {Address: "3", ShardId: 1, IsSynced: false, IsFallback: true}, + } + + synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) + require.Equal(t, []*data.NodeData{}, synced) + require.Equal(t, []*data.NodeData{}, syncedFb) + require.Equal(t, input, notSynced) +} + +func testEdgeCaseAddressShouldNotExistInBothLists(t *testing.T) { + t.Parallel() + + allNodes := prepareNodes(10) + + nodesMap := nodesSliceToShardedMap(allNodes) + nh := &nodesHolder{ + shardIDs: getSortedShardIDsSlice(nodesMap), + syncedNodes: allNodes, + } + + setSyncedStateToNodes(allNodes, false, 1, 3, 5, 7, 9) + + nh.UpdateNodes(allNodes) + require.Equal(t, []data.NodeData{ + {Address: "addr0", ShardId: 0, IsSynced: true}, + {Address: "addr2", ShardId: 0, IsSynced: true}, + {Address: "addr4", ShardId: 0, IsSynced: true}, + {Address: "addr6", ShardId: 1, IsSynced: true}, + {Address: "addr8", ShardId: 1, IsSynced: true}, + }, convertAndSortSlice(nh.syncedNodes)) + require.Equal(t, []data.NodeData{ + {Address: "addr1", ShardId: 0, IsSynced: false}, + {Address: "addr3", ShardId: 0, IsSynced: false}, + {Address: "addr5", ShardId: 1, IsSynced: false}, + {Address: "addr7", ShardId: 1, IsSynced: false}, + {Address: "addr9", ShardId: 1, IsSynced: false}, + }, convertAndSortSlice(nh.outOfSyncNodes)) + require.False(t, slicesHaveCommonObjects(nh.syncedNodes, nh.outOfSyncNodes)) + + allNodes = prepareNodes(10) + + nh.UpdateNodes(allNodes) + + require.Equal(t, []data.NodeData{ + {Address: "addr0", ShardId: 0, IsSynced: true}, + {Address: "addr1", ShardId: 0, IsSynced: true}, + {Address: "addr2", ShardId: 0, IsSynced: true}, + {Address: "addr3", ShardId: 0, IsSynced: true}, + {Address: "addr4", ShardId: 0, IsSynced: true}, + {Address: "addr5", ShardId: 1, IsSynced: true}, + {Address: "addr6", ShardId: 1, IsSynced: true}, + {Address: "addr7", ShardId: 1, IsSynced: true}, + {Address: "addr8", ShardId: 1, IsSynced: true}, + {Address: "addr9", ShardId: 1, IsSynced: true}, + }, convertAndSortSlice(nh.syncedNodes)) + require.False(t, slicesHaveCommonObjects(nh.syncedNodes, nh.outOfSyncNodes)) +} + +func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, 1} + var input []*data.NodeData + synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs) + require.Error(t, err) + require.Nil(t, synced) + require.Nil(t, syncedFb) + require.Nil(t, notSynced) + + // no node in one shard + shardIDs = []uint32{0, 1} + input = []*data.NodeData{ + { + Address: "0", ShardId: 0, IsSynced: true, + }, + } + synced, syncedFb, notSynced, err = computeSyncedAndOutOfSyncNodes(input, shardIDs) + require.True(t, errors.Is(err, errWrongConfiguration)) + require.Nil(t, synced) + require.Nil(t, syncedFb) + require.Nil(t, notSynced) +} + +func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard(t *testing.T) { + t.Parallel() + + // no node in one shard + shardIDs := []uint32{0, 1} + input := []*data.NodeData{ + { + Address: "0", ShardId: 0, IsSynced: true, + }, + } + synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs) + require.True(t, errors.Is(err, errWrongConfiguration)) + require.Nil(t, synced) + require.Nil(t, syncedFb) + require.Nil(t, notSynced) +} + +func slicesHaveCommonObjects(firstSlice []*data.NodeData, secondSlice []*data.NodeData) bool { + nodeDataToStr := func(nd *data.NodeData) string { + return fmt.Sprintf("%s%d", nd.Address, nd.ShardId) + } + firstSliceItems := make(map[string]struct{}) + for _, el := range firstSlice { + firstSliceItems[nodeDataToStr(el)] = struct{}{} + } + + for _, el := range secondSlice { + nodeDataStr := nodeDataToStr(el) + _, found := firstSliceItems[nodeDataStr] + if found { + return true + } + } + + return false +} + +func prepareNodes(count int) []*data.NodeData { + nodes := make([]*data.NodeData, 0, count) + for i := 0; i < count; i++ { + shardID := uint32(0) + if i >= count/2 { + shardID = 1 + } + nodes = append(nodes, &data.NodeData{ + ShardId: shardID, + Address: fmt.Sprintf("addr%d", i), + IsSynced: true, + }) + } + + return nodes +} + +func getSortedShardIDsSlice(nodesOnShards map[uint32][]*data.NodeData) []uint32 { + shardIDs := make([]uint32, 0) + for shardID := range nodesOnShards { + shardIDs = append(shardIDs, shardID) + } + sort.SliceStable(shardIDs, func(i, j int) bool { + return shardIDs[i] < shardIDs[j] + }) + + return shardIDs +} + +func setSyncedStateToNodes(nodes []*data.NodeData, state bool, indices ...int) { + for _, idx := range indices { + nodes[idx].IsSynced = state + } +} + +func convertAndSortSlice(nodes []*data.NodeData) []data.NodeData { + newSlice := make([]data.NodeData, 0, len(nodes)) + for _, node := range nodes { + newSlice = append(newSlice, *node) + } + + sort.Slice(newSlice, func(i, j int) bool { + return newSlice[i].Address < newSlice[j].Address + }) + + return newSlice +} diff --git a/observer/interface.go b/observer/interface.go index 0adae03a..0e545a59 100644 --- a/observer/interface.go +++ b/observer/interface.go @@ -11,3 +11,14 @@ type NodesProviderHandler interface { ReloadNodes(nodesType data.NodeType) data.NodesReloadResponse IsInterfaceNil() bool } + +// NodesHolder defines the actions of a component that is able to hold nodes +type NodesHolder interface { + UpdateNodes(nodesWithSyncStatus []*data.NodeData) + GetSyncedNodes() []*data.NodeData + GetSyncedFallbackNodes() []*data.NodeData + GetOutOfSyncNodes() []*data.NodeData + GetOutOfSyncFallbackNodes() []*data.NodeData + GetLastSyncedNodes() map[uint32]*data.NodeData + IsInterfaceNil() bool +} From 49367d4efc73d61ad1d6a26742e2344acc58cd76 Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Wed, 5 Jul 2023 16:13:52 +0300 Subject: [PATCH 03/13] test fix --- observer/baseNodeProvider_test.go | 71 +++++++++++-------------------- observer/errors.go | 6 --- 2 files changed, 25 insertions(+), 52 deletions(-) diff --git a/observer/baseNodeProvider_test.go b/observer/baseNodeProvider_test.go index ad898604..a3926ffa 100644 --- a/observer/baseNodeProvider_test.go +++ b/observer/baseNodeProvider_test.go @@ -19,53 +19,32 @@ const configurationPath = "testdata/config.toml" func TestBaseNodeProvider_InvalidNodesConfiguration(t *testing.T) { t.Parallel() - t.Run("node both snapshotless and fallback", func(t *testing.T) { - t.Parallel() - - nodes := []*data.NodeData{ - { - Address: "addr", - ShardId: 0, - IsFallback: true, - IsSnapshotless: true, - }, - } - - bnp := baseNodeProvider{} - err := bnp.initNodes(nodes) - require.Equal(t, ErrObserverCannotBeBothFallbackAndSnapshotless, err) - }) - - t.Run("nodes in shard should not be snapshotless only", func(t *testing.T) { - t.Parallel() - - nodes := []*data.NodeData{ - { - Address: "addr0", - ShardId: 0, - IsSnapshotless: false, - }, - { - Address: "addr1", - ShardId: 0, - IsSnapshotless: true, - }, - { - Address: "addr2", - ShardId: 1, - IsSnapshotless: true, - }, - { - Address: "addr3", - ShardId: 1, - IsSnapshotless: true, - }, - } + nodes := []*data.NodeData{ + { + Address: "addr0", + ShardId: 0, + IsSnapshotless: false, + }, + { + Address: "addr1", + ShardId: 0, + IsSnapshotless: true, + }, + { + Address: "addr2", + ShardId: 1, + IsSnapshotless: true, + }, + { + Address: "addr3", + ShardId: 1, + IsSnapshotless: true, + }, + } - bnp := baseNodeProvider{} - err := bnp.initNodes(nodes) - require.Contains(t, err.Error(), "observers for shard 1 must include at least one historical (non-snapshotless) observer") - }) + bnp := baseNodeProvider{} + err := bnp.initNodes(nodes) + require.Contains(t, err.Error(), "observers for shard 1 must include at least one historical (non-snapshotless) observer") } func TestBaseNodeProvider_ReloadNodesDifferentNumberOfNewShard(t *testing.T) { diff --git a/observer/errors.go b/observer/errors.go index 5b7f8556..c4529012 100644 --- a/observer/errors.go +++ b/observer/errors.go @@ -7,9 +7,3 @@ var ErrEmptyObserversList = errors.New("empty observers list") // ErrShardNotAvailable signals that the specified shard ID cannot be found in internal maps var ErrShardNotAvailable = errors.New("the specified shard ID does not exist in proxy's configuration") - -// ErrWrongObserversConfiguration signals an invalid observers configuration -var ErrWrongObserversConfiguration = errors.New("wrong observers configuration") - -// ErrObserverCannotBeBothFallbackAndSnapshotless signals an invalid configuration for one of the configuration nodes -var ErrObserverCannotBeBothFallbackAndSnapshotless = errors.New("observer cannot be both fallback and snapshotless") From 3c114c9f2fe677e94806913118357945c435114b Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Wed, 5 Jul 2023 16:32:36 +0300 Subject: [PATCH 04/13] fixes after merge --- process/accountProcessor.go | 2 +- process/accountProcessor_test.go | 6 +++--- process/esdtSupplyProcessor_test.go | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/process/accountProcessor.go b/process/accountProcessor.go index 08a05ddf..a916af85 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -435,7 +435,7 @@ func (ap *AccountProcessor) GetBaseProcessor() Processor { // IsDataTrieMigrated returns true if the data trie for the given address is migrated func (ap *AccountProcessor) IsDataTrieMigrated(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - observers, err := ap.getObserversForAddress(address) + observers, err := ap.getObserversForAddress(address, data.AvailabilityRecent) if err != nil { return nil, err } diff --git a/process/accountProcessor_test.go b/process/accountProcessor_test.go index b19cf80a..0c971404 100644 --- a/process/accountProcessor_test.go +++ b/process/accountProcessor_test.go @@ -493,7 +493,7 @@ func TestAccountProcessor_IsDataTrieMigrated(t *testing.T) { t.Run("should return error when cannot get observers", func(t *testing.T) { ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return nil, errors.New("cannot get observers") }, }, @@ -509,7 +509,7 @@ func TestAccountProcessor_IsDataTrieMigrated(t *testing.T) { t.Run("should return error when cannot get data trie migrated", func(t *testing.T) { ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "observer0", @@ -537,7 +537,7 @@ func TestAccountProcessor_IsDataTrieMigrated(t *testing.T) { t.Run("should work", func(t *testing.T) { ap, _ := process.NewAccountProcessor( &mock.ProcessorStub{ - GetObserversCalled: func(_ uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(_ uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { Address: "observer0", diff --git a/process/esdtSupplyProcessor_test.go b/process/esdtSupplyProcessor_test.go index 79098247..f85aacf9 100644 --- a/process/esdtSupplyProcessor_test.go +++ b/process/esdtSupplyProcessor_test.go @@ -128,7 +128,7 @@ func TestEsdtSupplyProcessor_GetESDTSupplyShouldReturnErrorIfInconsistentRespons GetShardIDsCalled: func() []uint32 { return []uint32{0, 1, core.MetachainShardId} }, - GetObserversCalled: func(shardID uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardID uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { ShardId: shardID, @@ -185,7 +185,7 @@ func TestEsdtSupplyProcessor_GetESDTSupplyShouldReturnRecomputed(t *testing.T) { GetShardIDsCalled: func() []uint32 { return []uint32{0, 1, core.MetachainShardId} }, - GetObserversCalled: func(shardID uint32) ([]*data.NodeData, error) { + GetObserversCalled: func(shardID uint32, _ data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { return []*data.NodeData{ { ShardId: shardID, From e1b2a6f67c79d34f92ae3fcbc89de50ec12154c4 Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Tue, 5 Sep 2023 10:58:47 +0300 Subject: [PATCH 05/13] fix test --- process/scQueryProcessor_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/process/scQueryProcessor_test.go b/process/scQueryProcessor_test.go index f226a7ce..13bfe52f 100644 --- a/process/scQueryProcessor_test.go +++ b/process/scQueryProcessor_test.go @@ -152,7 +152,7 @@ func TestSCQueryProcessor_ExecuteQueryWithCoordinates(t *testing.T) { ComputeShardIdCalled: func(addressBuff []byte) (u uint32, e error) { return 0, nil }, - GetObserversCalled: func(shardId uint32) (observers []*data.NodeData, e error) { + GetObserversCalled: func(shardId uint32, _ data.ObserverDataAvailabilityType) (observers []*data.NodeData, e error) { return []*data.NodeData{ {Address: providedAddr, ShardId: 0}, }, nil From 29ad5671abc11d043553dea471ec964aa194b64a Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Thu, 14 Sep 2023 17:03:03 +0300 Subject: [PATCH 06/13] fixes after system tests --- observer/baseNodeProvider.go | 43 +++++++++++++++++--------- observer/holder/nodesHolder.go | 26 +++++++++++----- observer/holder/nodesHolder_test.go | 47 +++++++++++++++++++++++++---- process/baseProcessor.go | 15 +++++---- 4 files changed, 95 insertions(+), 36 deletions(-) diff --git a/observer/baseNodeProvider.go b/observer/baseNodeProvider.go index 5fbe5b22..a4d7c18c 100644 --- a/observer/baseNodeProvider.go +++ b/observer/baseNodeProvider.go @@ -178,34 +178,38 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { var syncedNodesSource []*data.NodeData - if dataAvailability == data.AvailabilityRecent && len(bnp.snapshotlessNodes.GetSyncedNodes()) > 0 { + if dataAvailability == data.AvailabilityRecent { syncedNodesSource = bnp.snapshotlessNodes.GetSyncedNodes() } else { syncedNodesSource = bnp.regularNodes.GetSyncedNodes() } - syncedNodes := make([]*data.NodeData, 0) - for _, node := range syncedNodesSource { - if node.ShardId != shardId { - continue - } - - syncedNodes = append(syncedNodes, node) - } + syncedNodes := filterNodesInShard(syncedNodesSource, shardId) if len(syncedNodes) != 0 { return syncedNodes, nil } + if dataAvailability == data.AvailabilityRecent { + regularNodes := filterNodesInShard(bnp.regularNodes.GetSyncedNodes(), shardId) + if len(regularNodes) > 0 { + return regularNodes, nil + } + } + var fallbackNodesSource []*data.NodeData if dataAvailability == data.AvailabilityRecent { - fallbackNodesSource = bnp.snapshotlessNodes.GetSyncedNodes() + fallbackNodesSource = bnp.snapshotlessNodes.GetSyncedFallbackNodes() } else { - fallbackNodesSource = bnp.regularNodes.GetSyncedNodes() + fallbackNodesSource = bnp.regularNodes.GetSyncedFallbackNodes() } - for _, node := range fallbackNodesSource { - if node.ShardId == shardId { - syncedNodes = append(syncedNodes, node) + + if dataAvailability == data.AvailabilityRecent { + regularNodes := filterNodesInShard(bnp.regularNodes.GetSyncedNodes(), shardId) + if len(regularNodes) > 0 { + return regularNodes, nil } } + + syncedNodes = filterNodesInShard(fallbackNodesSource, shardId) if len(syncedNodes) != 0 { return syncedNodes, nil } @@ -224,6 +228,17 @@ func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32, d return nil, ErrShardNotAvailable } +func filterNodesInShard(nodes []*data.NodeData, shardID uint32) []*data.NodeData { + filteredSlice := make([]*data.NodeData, 0) + for _, node := range nodes { + if node.ShardId == shardID { + filteredSlice = append(filteredSlice, node) + } + } + + return filteredSlice +} + func (bnp *baseNodeProvider) getSyncedNodesUnprotected(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { syncedNodes := make([]*data.NodeData, 0) for _, shardId := range bnp.shardIds { diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go index b28f2625..a765feb5 100644 --- a/observer/holder/nodesHolder.go +++ b/observer/holder/nodesHolder.go @@ -49,7 +49,7 @@ func (nh *nodesHolder) UpdateNodes(nodesWithSyncStatus []*data.NodeData) { if len(nodesWithSyncStatus) == 0 { return } - syncedNodes, syncedFallbackNodes, outOfSyncNodes, err := computeSyncedAndOutOfSyncNodes(nodesWithSyncStatus, nh.shardIDs) + syncedNodes, syncedFallbackNodes, outOfSyncNodes, err := computeSyncedAndOutOfSyncNodes(nodesWithSyncStatus, nh.shardIDs, nh.availability) if err != nil { log.Error("cannot update nodes based on sync state", "error", err) return @@ -148,14 +148,18 @@ func (nh *nodesHolder) printSyncedNodesInShardsUnprotected() { totalNumOfActiveNodes++ inSyncAddresses[shardID] = append(inSyncAddresses[shardID], nh.lastSyncedNodes[shardID].Address) } - log.Info(fmt.Sprintf("shard %d active nodes", shardID), + nodesType := "regular active nodes" + if nh.availability == data.AvailabilityRecent { + nodesType = "snapshotless active nodes" + } + log.Info(fmt.Sprintf("shard %d %s", shardID, nodesType), "observers count", totalNumOfActiveNodes, "addresses", strings.Join(inSyncAddresses[shardID], ", "), "fallback addresses", strings.Join(inSyncFallbackAddresses[shardID], ", ")) } } -func computeSyncedAndOutOfSyncNodes(nodes []*data.NodeData, shardIDs []uint32) ([]*data.NodeData, []*data.NodeData, []*data.NodeData, error) { +func computeSyncedAndOutOfSyncNodes(nodes []*data.NodeData, shardIDs []uint32, availability data.ObserverDataAvailabilityType) ([]*data.NodeData, []*data.NodeData, []*data.NodeData, error) { tempSyncedNodesMap := make(map[uint32][]*data.NodeData) tempSyncedFallbackNodesMap := make(map[uint32][]*data.NodeData) tempNotSyncedNodesMap := make(map[uint32][]*data.NodeData) @@ -183,7 +187,9 @@ func computeSyncedAndOutOfSyncNodes(nodes []*data.NodeData, shardIDs []uint32) ( totalLen := len(tempSyncedNodesMap[shardID]) + len(tempSyncedFallbackNodesMap[shardID]) + len(tempNotSyncedNodesMap[shardID]) if totalLen == 0 { - return nil, nil, nil, fmt.Errorf("%w for shard %d - no synced or not synced node", errWrongConfiguration, shardID) + if availability != data.AvailabilityRecent { + return nil, nil, nil, fmt.Errorf("%w for shard %d - no synced or not synced node", errWrongConfiguration, shardID) + } } } @@ -282,6 +288,10 @@ func (nh *nodesHolder) removeOutOfSyncNodesUnprotected( syncedNodesMap map[uint32][]*data.NodeData, syncedFallbackNodesMap map[uint32][]*data.NodeData, ) { + minSyncedNodes := 1 + if nh.availability == data.AvailabilityRecent { + minSyncedNodes = 0 // allow the snapshotless list to be empty so regular observers can be used + } if len(outOfSyncNodes) == 0 { nh.outOfSyncNodes = make([]*data.NodeData, 0) nh.outOfSyncFallbackNodes = make([]*data.NodeData, 0) @@ -289,8 +299,8 @@ func (nh *nodesHolder) removeOutOfSyncNodesUnprotected( } for _, outOfSyncNode := range outOfSyncNodes { - hasOneSyncedNode := len(syncedNodesMap[outOfSyncNode.ShardId]) >= 1 - hasEnoughSyncedFallbackNodes := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) > 1 + hasOneSyncedNode := len(syncedNodesMap[outOfSyncNode.ShardId]) >= minSyncedNodes + hasEnoughSyncedFallbackNodes := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) > minSyncedNodes canDeleteFallbackNode := hasOneSyncedNode || hasEnoughSyncedFallbackNodes if outOfSyncNode.IsFallback && canDeleteFallbackNode { nh.removeNodeUnprotected(outOfSyncNode) @@ -305,7 +315,7 @@ func (nh *nodesHolder) removeOutOfSyncNodesUnprotected( continue } - hasEnoughSyncedNodes := len(syncedNodesMap[outOfSyncNode.ShardId]) >= 1 + hasEnoughSyncedNodes := len(syncedNodesMap[outOfSyncNode.ShardId]) >= minSyncedNodes if hasEnoughSyncedNodes { nh.removeNodeUnprotected(outOfSyncNode) continue @@ -326,7 +336,7 @@ func (nh *nodesHolder) removeOutOfSyncNodesUnprotected( "shard", outOfSyncNode.ShardId) nh.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode } - hasOneSyncedFallbackNode := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) >= 1 + hasOneSyncedFallbackNode := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) >= minSyncedNodes if hasOneSyncedFallbackNode { nh.removeNodeUnprotected(outOfSyncNode) continue diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go index 741ae0c1..0c392aa0 100644 --- a/observer/holder/nodesHolder_test.go +++ b/observer/holder/nodesHolder_test.go @@ -6,10 +6,28 @@ import ( "sort" "testing" + "github.com/multiversx/mx-chain-core-go/core" "github.com/multiversx/mx-chain-proxy-go/data" "github.com/stretchr/testify/require" ) +func TestNodesHolder_GetLastSyncedNodes(t *testing.T) { + t.Parallel() + + syncedNodes := []*data.NodeData{{Address: "addr0", ShardId: core.MetachainShardId}, {Address: "addr1", ShardId: 0}} + fallbackNodes := []*data.NodeData{{Address: "fallback-addr0", ShardId: core.MetachainShardId}, {Address: "fallback-addr1", ShardId: 0}} + shardIds := []uint32{0, core.MetachainShardId} + + nodesHolder, err := NewNodesHolder(syncedNodes, fallbackNodes, shardIds, data.AvailabilityAll) + require.NoError(t, err) + + require.Equal(t, syncedNodes, nodesHolder.GetSyncedNodes()) + require.Equal(t, fallbackNodes, nodesHolder.GetSyncedFallbackNodes()) + require.Empty(t, nodesHolder.GetOutOfSyncFallbackNodes()) + require.Empty(t, nodesHolder.GetOutOfSyncNodes()) + require.Empty(t, nodesHolder.GetLastSyncedNodes()) +} + func TestComputeSyncAndOutOfSyncNodes(t *testing.T) { t.Parallel() @@ -18,6 +36,7 @@ func TestComputeSyncAndOutOfSyncNodes(t *testing.T) { t.Run("all nodes are out of sync", testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced) t.Run("invalid config - no node", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll) t.Run("invalid config - no node in a shard", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard) + t.Run("snapshotless nodes should work with no node in a shard", testSnapshotlessNodesShouldWorkIfNoNodeInShardExists) t.Run("edge case - address should not exist in both sync and not-synced lists", testEdgeCaseAddressShouldNotExistInBothLists) } @@ -32,7 +51,7 @@ func testComputeSyncedAndOutOfSyncNodesAllNodesSynced(t *testing.T) { {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, } - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) + synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) require.Equal(t, []*data.NodeData{ {Address: "0", ShardId: 0, IsSynced: true}, {Address: "2", ShardId: 1, IsSynced: true}, @@ -57,7 +76,7 @@ func testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers(t *testing.T) { {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, } - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) + synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) require.Equal(t, []*data.NodeData{ {Address: "0", ShardId: 0, IsSynced: true}, {Address: "3", ShardId: 1, IsSynced: true}, @@ -83,7 +102,7 @@ func testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced(t *testing.T) { {Address: "3", ShardId: 1, IsSynced: false, IsFallback: true}, } - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs) + synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) require.Equal(t, []*data.NodeData{}, synced) require.Equal(t, []*data.NodeData{}, syncedFb) require.Equal(t, input, notSynced) @@ -143,7 +162,7 @@ func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll(t *testin shardIDs := []uint32{0, 1} var input []*data.NodeData - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs) + synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) require.Error(t, err) require.Nil(t, synced) require.Nil(t, syncedFb) @@ -156,7 +175,7 @@ func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll(t *testin Address: "0", ShardId: 0, IsSynced: true, }, } - synced, syncedFb, notSynced, err = computeSyncedAndOutOfSyncNodes(input, shardIDs) + synced, syncedFb, notSynced, err = computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) require.True(t, errors.Is(err, errWrongConfiguration)) require.Nil(t, synced) require.Nil(t, syncedFb) @@ -173,13 +192,29 @@ func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard(t *tes Address: "0", ShardId: 0, IsSynced: true, }, } - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs) + synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) require.True(t, errors.Is(err, errWrongConfiguration)) require.Nil(t, synced) require.Nil(t, syncedFb) require.Nil(t, notSynced) } +func testSnapshotlessNodesShouldWorkIfNoNodeInShardExists(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, core.MetachainShardId} + input := []*data.NodeData{ + { + Address: "m", ShardId: core.MetachainShardId, IsSynced: true, IsSnapshotless: true, + }, + } + synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityRecent) + require.NoError(t, err) + require.Empty(t, notSynced) + require.Empty(t, syncedFb) + require.Equal(t, input, synced) +} + func slicesHaveCommonObjects(firstSlice []*data.NodeData, secondSlice []*data.NodeData) bool { nodeDataToStr := func(nd *data.NodeData) string { return fmt.Sprintf("%s%d", nd.Address, nd.ShardId) diff --git a/process/baseProcessor.go b/process/baseProcessor.go index 80fde998..332b3393 100644 --- a/process/baseProcessor.go +++ b/process/baseProcessor.go @@ -6,7 +6,7 @@ import ( "encoding/json" "errors" "fmt" - "io/ioutil" + "io" "net" "net/http" "strconv" @@ -202,12 +202,11 @@ func (bp *BaseProcessor) CallGetRestEndPoint( resp, err := bp.httpClient.Do(req) if err != nil { + bp.triggerNodesSyncCheck(address) if isTimeoutError(err) { - bp.triggerNodesSyncCheck(address) return http.StatusRequestTimeout, err } - bp.triggerNodesSyncCheck(address) return http.StatusNotFound, err } @@ -218,7 +217,7 @@ func (bp *BaseProcessor) CallGetRestEndPoint( } }() - responseBodyBytes, err := ioutil.ReadAll(resp.Body) + responseBodyBytes, err := io.ReadAll(resp.Body) if err != nil { return http.StatusInternalServerError, err } @@ -262,12 +261,11 @@ func (bp *BaseProcessor) CallPostRestEndPoint( resp, err := bp.httpClient.Do(req) if err != nil { + bp.triggerNodesSyncCheck(address) if isTimeoutError(err) { - bp.triggerNodesSyncCheck(address) return http.StatusRequestTimeout, err } - bp.triggerNodesSyncCheck(address) return http.StatusNotFound, err } @@ -278,7 +276,7 @@ func (bp *BaseProcessor) CallPostRestEndPoint( } }() - responseBodyBytes, err := ioutil.ReadAll(resp.Body) + responseBodyBytes, err := io.ReadAll(resp.Body) if err != nil { return http.StatusInternalServerError, err } @@ -421,6 +419,7 @@ func (bp *BaseProcessor) isNodeSynced(node *proxyData.NodeData) (bool, error) { "probable highest nonce", probableHighestNonce, "is synced", isNodeSynced, "is ready for VM Queries", isReadyForVMQueries, + "is snapshotless", node.IsSnapshotless, "is fallback", node.IsFallback) if !isReadyForVMQueries { @@ -454,7 +453,7 @@ func (bp *BaseProcessor) getNodeStatusResponseFromAPI(url string) (*proxyData.No return nil, resp.StatusCode, nil } - responseBodyBytes, err := ioutil.ReadAll(resp.Body) + responseBodyBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, http.StatusInternalServerError, err } From a713a29476db93111dd1ed878c6f9b24b56b062e Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Thu, 21 Sep 2023 16:49:35 +0300 Subject: [PATCH 07/13] refactoring --- observer/baseNodeProvider.go | 120 +++-- observer/baseNodeProvider_test.go | 766 +++------------------------- observer/holder/nodesHolder.go | 467 +++++------------ observer/holder/nodesHolder_test.go | 384 ++++++-------- observer/interface.go | 10 +- 5 files changed, 410 insertions(+), 1337 deletions(-) diff --git a/observer/baseNodeProvider.go b/observer/baseNodeProvider.go index a4d7c18c..8395750f 100644 --- a/observer/baseNodeProvider.go +++ b/observer/baseNodeProvider.go @@ -40,11 +40,11 @@ func (bnp *baseNodeProvider) initNodes(nodes []*data.NodeData) error { bnp.shardIds = getSortedShardIDsSlice(newNodes) syncedNodes, syncedFallbackNodes, syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes := initAllNodesSlice(newNodes) - bnp.regularNodes, err = holder.NewNodesHolder(syncedNodes, syncedFallbackNodes, bnp.shardIds, data.AvailabilityAll) + bnp.regularNodes, err = holder.NewNodesHolder(syncedNodes, syncedFallbackNodes, data.AvailabilityAll) if err != nil { return err } - bnp.snapshotlessNodes, err = holder.NewNodesHolder(syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes, bnp.shardIds, data.AvailabilityRecent) + bnp.snapshotlessNodes, err = holder.NewNodesHolder(syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes, data.AvailabilityRecent) if err != nil { return err } @@ -75,23 +75,22 @@ func (bnp *baseNodeProvider) GetAllNodesWithSyncState() []*data.NodeData { defer bnp.mutNodes.RUnlock() nodesSlice := make([]*data.NodeData, 0) - nodesSlice = append(nodesSlice, bnp.regularNodes.GetSyncedNodes()...) - nodesSlice = append(nodesSlice, bnp.regularNodes.GetOutOfSyncNodes()...) - nodesSlice = append(nodesSlice, bnp.regularNodes.GetSyncedFallbackNodes()...) - nodesSlice = append(nodesSlice, bnp.regularNodes.GetOutOfSyncFallbackNodes()...) - - nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetSyncedNodes()...) - nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetOutOfSyncNodes()...) - nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetSyncedFallbackNodes()...) - nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetOutOfSyncFallbackNodes()...) + for _, shardID := range bnp.shardIds { + nodesSlice = append(nodesSlice, bnp.regularNodes.GetSyncedNodes(shardID)...) + nodesSlice = append(nodesSlice, bnp.regularNodes.GetOutOfSyncNodes(shardID)...) + nodesSlice = append(nodesSlice, bnp.regularNodes.GetSyncedFallbackNodes(shardID)...) + nodesSlice = append(nodesSlice, bnp.regularNodes.GetOutOfSyncFallbackNodes(shardID)...) + + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetSyncedNodes(shardID)...) + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetOutOfSyncNodes(shardID)...) + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetSyncedFallbackNodes(shardID)...) + nodesSlice = append(nodesSlice, bnp.snapshotlessNodes.GetOutOfSyncFallbackNodes(shardID)...) + } + return nodesSlice } -// UpdateNodesBasedOnSyncState will handle the nodes lists, by removing out of sync observers or by adding back observers -// that were previously removed because they were out of sync. -// If all observers are removed, the last one synced will be saved and the fallbacks will be used. -// If even the fallbacks are out of sync, the last regular observer synced will be used, even though it is out of sync. -// When one or more regular observers are back in sync, the fallbacks will not be used anymore. +// UpdateNodesBasedOnSyncState will simply call the corresponding function for both regular and snapshotless observers func (bnp *baseNodeProvider) UpdateNodesBasedOnSyncState(nodesWithSyncStatus []*data.NodeData) { bnp.mutNodes.Lock() defer bnp.mutNodes.Unlock() @@ -150,7 +149,7 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo defer bnp.mutNodes.Unlock() bnp.shardIds = getSortedShardIDsSlice(newNodes) syncedNodes, syncedFallbackNodes, syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes := initAllNodesSlice(newNodes) - bnp.regularNodes, err = holder.NewNodesHolder(syncedNodes, syncedFallbackNodes, bnp.shardIds, data.AvailabilityAll) + bnp.regularNodes, err = holder.NewNodesHolder(syncedNodes, syncedFallbackNodes, data.AvailabilityAll) if err != nil { log.Error("cannot reload regular nodes: NewNodesHolder", "error", err) return data.NodesReloadResponse{ @@ -159,7 +158,8 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo Error: "cannot create the regular nodes holder: " + err.Error(), } } - bnp.snapshotlessNodes, err = holder.NewNodesHolder(syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes, bnp.shardIds, data.AvailabilityRecent) + + bnp.snapshotlessNodes, err = holder.NewNodesHolder(syncedSnapshotlessNodes, syncedSnapshotlessFallbackNodes, data.AvailabilityRecent) if err != nil { log.Error("cannot reload snapshotless nodes: NewNodesHolder", "error", err) return data.NodesReloadResponse{ @@ -176,67 +176,61 @@ func (bnp *baseNodeProvider) ReloadNodes(nodesType data.NodeType) data.NodesRelo } } -func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardId uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { - var syncedNodesSource []*data.NodeData - if dataAvailability == data.AvailabilityRecent { - syncedNodesSource = bnp.snapshotlessNodes.GetSyncedNodes() - } else { - syncedNodesSource = bnp.regularNodes.GetSyncedNodes() - } - syncedNodes := filterNodesInShard(syncedNodesSource, shardId) +func (bnp *baseNodeProvider) getSyncedNodesForShardUnprotected(shardID uint32, dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { + var syncedNodes []*data.NodeData + + syncedNodes = bnp.getSyncedNodes(dataAvailability, shardID) if len(syncedNodes) != 0 { return syncedNodes, nil } - if dataAvailability == data.AvailabilityRecent { - regularNodes := filterNodesInShard(bnp.regularNodes.GetSyncedNodes(), shardId) - if len(regularNodes) > 0 { - return regularNodes, nil - } - } - - var fallbackNodesSource []*data.NodeData - if dataAvailability == data.AvailabilityRecent { - fallbackNodesSource = bnp.snapshotlessNodes.GetSyncedFallbackNodes() - } else { - fallbackNodesSource = bnp.regularNodes.GetSyncedFallbackNodes() + fallbackNodesSource := bnp.getFallbackNodes(dataAvailability, shardID) + if len(fallbackNodesSource) != 0 { + return fallbackNodesSource, nil } - if dataAvailability == data.AvailabilityRecent { - regularNodes := filterNodesInShard(bnp.regularNodes.GetSyncedNodes(), shardId) - if len(regularNodes) > 0 { - return regularNodes, nil - } + outOfSyncNodes := bnp.getOutOfSyncNodes(dataAvailability, shardID) + if len(outOfSyncNodes) > 0 { + return outOfSyncNodes, nil } - syncedNodes = filterNodesInShard(fallbackNodesSource, shardId) - if len(syncedNodes) != 0 { - return syncedNodes, nil - } - - var lastSyncedNodesMap map[uint32]*data.NodeData - if dataAvailability == data.AvailabilityAll { - lastSyncedNodesMap = bnp.regularNodes.GetLastSyncedNodes() - } else { - lastSyncedNodesMap = bnp.snapshotlessNodes.GetLastSyncedNodes() - } - backupNode, hasBackup := lastSyncedNodesMap[shardId] - if hasBackup { - return []*data.NodeData{backupNode}, nil + outOfSyncFallbackNodesSource := bnp.getOutOfSyncFallbackNodes(dataAvailability, shardID) + if len(outOfSyncFallbackNodesSource) != 0 { + return outOfSyncFallbackNodesSource, nil } return nil, ErrShardNotAvailable } -func filterNodesInShard(nodes []*data.NodeData, shardID uint32) []*data.NodeData { - filteredSlice := make([]*data.NodeData, 0) - for _, node := range nodes { - if node.ShardId == shardID { - filteredSlice = append(filteredSlice, node) +func (bnp *baseNodeProvider) getNodesByType( + availabilityType data.ObserverDataAvailabilityType, + shardID uint32, + getSnapshotlessNodesFunc func(uint32) []*data.NodeData, + getRegularNodesFunc func(uint32) []*data.NodeData) []*data.NodeData { + + if availabilityType == data.AvailabilityRecent { + nodes := getSnapshotlessNodesFunc(shardID) + if len(nodes) > 0 { + return nodes } } + return getRegularNodesFunc(shardID) +} + +func (bnp *baseNodeProvider) getSyncedNodes(availabilityType data.ObserverDataAvailabilityType, shardID uint32) []*data.NodeData { + return bnp.getNodesByType(availabilityType, shardID, bnp.snapshotlessNodes.GetSyncedNodes, bnp.regularNodes.GetSyncedNodes) +} + +func (bnp *baseNodeProvider) getFallbackNodes(availabilityType data.ObserverDataAvailabilityType, shardID uint32) []*data.NodeData { + return bnp.getNodesByType(availabilityType, shardID, bnp.snapshotlessNodes.GetSyncedFallbackNodes, bnp.regularNodes.GetSyncedFallbackNodes) +} + +func (bnp *baseNodeProvider) getOutOfSyncNodes(availabilityType data.ObserverDataAvailabilityType, shardID uint32) []*data.NodeData { + return bnp.getNodesByType(availabilityType, shardID, bnp.snapshotlessNodes.GetOutOfSyncNodes, bnp.regularNodes.GetOutOfSyncNodes) +} - return filteredSlice +func (bnp *baseNodeProvider) getOutOfSyncFallbackNodes(availabilityType data.ObserverDataAvailabilityType, shardID uint32) []*data.NodeData { + return bnp.getNodesByType(availabilityType, shardID, bnp.snapshotlessNodes.GetOutOfSyncFallbackNodes, bnp.regularNodes.GetOutOfSyncFallbackNodes) } func (bnp *baseNodeProvider) getSyncedNodesUnprotected(dataAvailability data.ObserverDataAvailabilityType) ([]*data.NodeData, error) { diff --git a/observer/baseNodeProvider_test.go b/observer/baseNodeProvider_test.go index a3926ffa..847e3ed8 100644 --- a/observer/baseNodeProvider_test.go +++ b/observer/baseNodeProvider_test.go @@ -1,8 +1,6 @@ package observer import ( - "fmt" - "sort" "testing" "github.com/multiversx/mx-chain-core-go/core" @@ -274,697 +272,11 @@ func TestInitAllNodesSlice_SingleShardShouldWork(t *testing.T) { } } -func TestBaseNodeProvider_UpdateNodesBasedOnSyncState(t *testing.T) { - t.Parallel() - - allNodes := prepareNodes(8) - setFallbackNodes(allNodes, 0, 1, 4, 5) - - bnp := &baseNodeProvider{ - configurationFilePath: configurationPath, - } - _ = bnp.initNodes(allNodes) - - nodesCopy := copyNodes(allNodes) - setSyncedStateToNodes(nodesCopy, false, 1, 2, 5, 6) - - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - - require.Equal(t, []data.NodeData{ - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr4", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr6", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) -} - -func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldNotRemoveNodeIfNotEnoughLeft(t *testing.T) { - t.Parallel() - - allNodes := prepareNodes(4) - snapshotlessNodes := prepareSnapshotlessNodes(4) - - nodesMap := nodesSliceToShardedMap(allNodes) - bnp := &baseNodeProvider{ - configurationFilePath: configurationPath, - shardIds: getSortedShardIDsSlice(nodesMap), - regularNodes: createNodesHolder(allNodes, []uint32{0, 1}), - snapshotlessNodes: createNodesHolder(snapshotlessNodes, []uint32{0, 1}), - } - - nodesCopy := append(copyNodes(allNodes), copyNodes(snapshotlessNodes)...) - setSyncedStateToNodes(nodesCopy, false, 0, 2, 4, 6) - - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false}, - {Address: "addr2", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: true, IsSnapshotless: true}, - {Address: "addr3", ShardId: 1, IsSynced: true, IsSnapshotless: true}, - }, convertAndSortSlice(bnp.snapshotlessNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsSnapshotless: true}, - {Address: "addr2", ShardId: 1, IsSynced: false, IsSnapshotless: true}, - }, convertAndSortSlice(bnp.snapshotlessNodes.GetOutOfSyncNodes())) - - syncedNodes, err := bnp.getSyncedNodesUnprotected(data.AvailabilityAll) - require.Nil(t, err) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(syncedNodes)) - - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 1, 3) - - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false}, - {Address: "addr1", ShardId: 0, IsSynced: false}, - {Address: "addr2", ShardId: 1, IsSynced: false}, - {Address: "addr3", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - require.Equal(t, data.NodeData{Address: "addr3", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) - - syncedNodes, err = bnp.getSyncedNodesUnprotected(data.AvailabilityAll) - require.Nil(t, err) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(syncedNodes)) -} - -func TestBaseNodeProvider_getSyncedNodesUnprotectedShouldWork(t *testing.T) { - t.Parallel() - - allNodes := prepareNodes(4) - - nodesMap := nodesSliceToShardedMap(allNodes) - bnp := &baseNodeProvider{ - configurationFilePath: configurationPath, - shardIds: getSortedShardIDsSlice(nodesMap), - regularNodes: createNodesHolder(allNodes, []uint32{0, 1}), - snapshotlessNodes: createEmptyNodesHolder(), - } - - nodesCopy := copyNodes(allNodes) - setSyncedStateToNodes(nodesCopy, false, 0) - - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: true}, - {Address: "addr2", ShardId: 1, IsSynced: true}, - {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - - syncedNodes, err := bnp.getSyncedNodesUnprotected(data.AvailabilityAll) - require.Nil(t, err) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: true}, - {Address: "addr2", ShardId: 1, IsSynced: true}, - {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(syncedNodes)) - - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 1) - - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 1, IsSynced: true}, - {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false}, - {Address: "addr1", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, data.NodeData{Address: "addr1", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - - syncedNodes, err = bnp.getSyncedNodesUnprotected(data.AvailabilityAll) - require.Nil(t, err) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: false}, - {Address: "addr2", ShardId: 1, IsSynced: true}, - {Address: "addr3", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(syncedNodes)) -} - -func TestBaseNodeProvider_UpdateNodesBasedOnSyncStateShouldWorkAfterMultipleIterations(t *testing.T) { - t.Parallel() - - allNodes := prepareNodes(10) - setFallbackNodes(allNodes, 0, 1, 5, 6) - - bnp := &baseNodeProvider{ - configurationFilePath: configurationPath, - } - _ = bnp.initNodes(allNodes) - - // sync all nodes, call update 3 times same state - nodesCopy := copyNodes(allNodes) - setFallbackNodes(nodesCopy, 0, 1, 5, 6) - for i := 0; i < 3; i++ { - syncAllNodesAndCheck(t, nodesCopy, bnp) - } - - // unsync some nodes, call Update 3 times same state - nodesCopy = copyNodes(allNodes) - setSyncedStateToNodes(nodesCopy, false, 1, 3, 5, 7, 9) - for i := 0; i < 3; i++ { - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - } - - // sync all nodes, call update 3 times same state - nodesCopy = prepareNodes(10) - setFallbackNodes(nodesCopy, 0, 1, 5, 6) - for i := 0; i < 3; i++ { - syncAllNodesAndCheck(t, nodesCopy, bnp) - } - - // unsync all regular observers, call update 3 times same state - nodesCopy = prepareNodes(10) - setFallbackNodes(nodesCopy, 0, 1, 5, 6) - setSyncedStateToNodes(nodesCopy, false, 2, 3, 4, 7, 8, 9) - for i := 0; i < 3; i++ { - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - } - - // unsync fallbacks as well -> should keep the last regular ones, call update 3 times same state - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 0, 1, 5, 6) - for i := 0; i < 3; i++ { - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - } - - // bring one node on each shard back, call update 3 times same state - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, true, 2, 7) - for i := 0; i < 3; i++ { - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: false, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - } - - // sync all nodes - nodesCopy = prepareNodes(10) - setFallbackNodes(nodesCopy, 0, 1, 5, 6) - syncAllNodesAndCheck(t, nodesCopy, bnp) - - // unsync fallbacks from shard 0 - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 0, 1) - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - - // unsync all regular observers - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 2, 3, 4, 7, 8, 9) - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - require.Equal(t, data.NodeData{Address: "addr9", ShardId: 1, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[1]) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr8", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - - // sync all nodes - nodesCopy = prepareNodes(10) - setFallbackNodes(nodesCopy, 0, 1, 5, 6) - syncAllNodesAndCheck(t, nodesCopy, bnp) - - // unsync all observers from shard 0 - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 0, 1, 2, 3, 4) - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, data.NodeData{Address: "addr4", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - require.Equal(t, 1, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - - // sync all nodes - nodesCopy = prepareNodes(10) - setFallbackNodes(nodesCopy, 0, 1, 5, 6) - syncAllNodesAndCheck(t, nodesCopy, bnp) - - // unsync last regular observer from shard 0, call update 3 times - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 4) - for i := 0; i < 3; i++ { - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - } - - // unsync all regular observers from shard 0, call update 3 times - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 2, 3) - for i := 0; i < 3; i++ { - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, data.NodeData{Address: "addr3", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) // last synced - require.Equal(t, 1, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) - } - - // unsync all fallback observers from shard 0 - nodesCopy = copyNodes(nodesCopy) - setSyncedStateToNodes(nodesCopy, false, 0, 1) - bnp.UpdateNodesBasedOnSyncState(nodesCopy) - require.Equal(t, []data.NodeData{ - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncFallbackNodes())) - require.Equal(t, data.NodeData{Address: "addr3", ShardId: 0, IsSynced: false}, *bnp.regularNodes.GetLastSyncedNodes()[0]) - require.Equal(t, 1, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr4", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) -} - -func syncAllNodesAndCheck(t *testing.T, nodes []*data.NodeData, bnp *baseNodeProvider) { - bnp.UpdateNodesBasedOnSyncState(nodes) - require.Equal(t, []data.NodeData{ - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedNodes())) - require.Equal(t, []data.NodeData{}, convertAndSortSlice(bnp.regularNodes.GetOutOfSyncNodes())) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - }, convertAndSortSlice(bnp.regularNodes.GetSyncedFallbackNodes())) - require.Equal(t, 0, len(bnp.regularNodes.GetLastSyncedNodes())) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr5", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr6", ShardId: 1, IsSynced: true, IsFallback: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(bnp.GetAllNodesWithSyncState())) -} - -func prepareNodes(count int) []*data.NodeData { - nodes := make([]*data.NodeData, 0, count) - for i := 0; i < count; i++ { - shardID := uint32(0) - if i >= count/2 { - shardID = 1 - } - nodes = append(nodes, &data.NodeData{ - ShardId: shardID, - Address: fmt.Sprintf("addr%d", i), - IsSynced: true, - }) - } - - return nodes -} - -func prepareSnapshotlessNodes(count int) []*data.NodeData { - nodes := prepareNodes(count) - for _, node := range nodes { - node.IsSnapshotless = true - } - - return nodes -} - -func createNodesHolder(nodes []*data.NodeData, shardIDs []uint32) NodesHolder { - holderInstance, _ := holder.NewNodesHolder(nodes, []*data.NodeData{}, shardIDs, "") +func createNodesHolder(nodes []*data.NodeData) NodesHolder { + holderInstance, _ := holder.NewNodesHolder(nodes, []*data.NodeData{}, "") return holderInstance } -func createEmptyNodesHolder() NodesHolder { - holderInstance, _ := holder.NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, []uint32{0}, "") - return holderInstance -} - -func copyNodes(nodes []*data.NodeData) []*data.NodeData { - nodesCopy := make([]*data.NodeData, len(nodes)) - for i, node := range nodes { - nodesCopy[i] = &data.NodeData{ - ShardId: node.ShardId, - Address: node.Address, - IsSynced: node.IsSynced, - IsFallback: node.IsFallback, - IsSnapshotless: node.IsSnapshotless, - } - } - - return nodesCopy -} - -func setSyncedStateToNodes(nodes []*data.NodeData, state bool, indices ...int) { - for _, idx := range indices { - nodes[idx].IsSynced = state - } -} - -func setFallbackNodes(nodes []*data.NodeData, indices ...int) { - for _, idx := range indices { - nodes[idx].IsFallback = true - } -} - -func convertAndSortSlice(nodes []*data.NodeData) []data.NodeData { - newSlice := make([]data.NodeData, 0, len(nodes)) - for _, node := range nodes { - newSlice = append(newSlice, *node) - } - - sort.Slice(newSlice, func(i, j int) bool { - return newSlice[i].Address < newSlice[j].Address - }) - - return newSlice -} - func TestBaseNodeProvider_GetNodesShouldWorkAccordingToTheAvailability(t *testing.T) { t.Parallel() @@ -982,8 +294,8 @@ func TestBaseNodeProvider_GetNodesShouldWorkAccordingToTheAvailability(t *testin } syncedNodes, _, syncedSnapshotless, _ := initAllNodesSlice(map[uint32][]*data.NodeData{1: nodes}) bnp := &baseNodeProvider{ - regularNodes: createNodesHolder(syncedNodes, []uint32{0}), - snapshotlessNodes: createNodesHolder(syncedSnapshotless, []uint32{0}), + regularNodes: createNodesHolder(syncedNodes), + snapshotlessNodes: createNodesHolder(syncedSnapshotless), } returnedNodes, err := bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) @@ -994,3 +306,73 @@ func TestBaseNodeProvider_GetNodesShouldWorkAccordingToTheAvailability(t *testin require.NoError(t, err) require.Equal(t, "addr1", returnedNodes[0].Address) } + +func TestBaseNodeProvider_getSyncedNodesForShardUnprotected(t *testing.T) { + getInitialNodes := func() []*data.NodeData { + return []*data.NodeData{ + { + Address: "addr0-snapshotless", + ShardId: 1, + IsSnapshotless: true, + IsSynced: true, + }, + { + Address: "addr1-regular", + ShardId: 1, + IsSnapshotless: false, + IsSynced: true, + }, + { + Address: "addr2-fallback", + ShardId: 1, + IsFallback: true, + IsSynced: true, + }, + } + } + initialNodes := getInitialNodes() + for _, node := range initialNodes { + node.IsSynced = true + } + syncedNodes, _, syncedSnapshotless, _ := initAllNodesSlice(map[uint32][]*data.NodeData{1: initialNodes}) + bnp := &baseNodeProvider{ + regularNodes: createNodesHolder(syncedNodes), + snapshotlessNodes: createNodesHolder(syncedSnapshotless), + shardIds: []uint32{1}, + } + + nodes, err := bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) + require.NoError(t, err) + require.Equal(t, "addr0-snapshotless", nodes[0].Address) + + // make the snapshotless node out of sync - it should go to the regular observer + updatedNodes := getInitialNodes() + updatedNodes[0].IsSynced = false + bnp.UpdateNodesBasedOnSyncState(updatedNodes) + + nodes, err = bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) + require.NoError(t, err) + require.Equal(t, "addr1-regular", nodes[0].Address) + + // make the regular node out of sync - it should go to the fallback observer + updatedNodes = getInitialNodes() + updatedNodes[0].IsSynced = false + updatedNodes[1].IsSynced = false + bnp.UpdateNodesBasedOnSyncState(updatedNodes) + + nodes, err = bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) + require.NoError(t, err) + require.Equal(t, "addr2-fallback", nodes[0].Address) + + // make the fallback node out of sync - it should use an out of sync snapshotless node + updatedNodes = getInitialNodes() + updatedNodes[0].IsSynced = false + updatedNodes[1].IsSynced = false + updatedNodes[2].IsSynced = false + bnp.UpdateNodesBasedOnSyncState(updatedNodes) + + nodes, err = bnp.getSyncedNodesForShardUnprotected(1, data.AvailabilityRecent) + require.NoError(t, err) + require.Equal(t, "addr0-snapshotless", nodes[0].Address) + require.False(t, nodes[0].IsSynced) +} diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go index a765feb5..cf4b5841 100644 --- a/observer/holder/nodesHolder.go +++ b/observer/holder/nodesHolder.go @@ -10,37 +10,36 @@ import ( "github.com/multiversx/mx-chain-proxy-go/data" ) +type cacheType string + +const ( + syncedNodesCache cacheType = "syncedNodes" + outOfSyncNodesCache cacheType = "outOfSyncNodes" + syncedFallbackCache cacheType = "syncedFallbackNodes" + outOfSyncFallbackNodes cacheType = "outOfSyncFallbackNodes" +) + var ( - errEmptyShardIDsList = errors.New("empty shard IDs list") - errWrongConfiguration = errors.New("wrong observers configuration") - log = logger.GetOrCreate("observer/holder") + log = logger.GetOrCreate("observer/holder") + errEmptyNodesList = errors.New("empty nodes list") ) type nodesHolder struct { - mut sync.RWMutex - syncedNodes []*data.NodeData - outOfSyncNodes []*data.NodeData - syncedFallbackNodes []*data.NodeData - outOfSyncFallbackNodes []*data.NodeData - lastSyncedNodes map[uint32]*data.NodeData - shardIDs []uint32 - availability data.ObserverDataAvailabilityType + mut sync.RWMutex + allNodes map[uint32][]*data.NodeData + cache map[string][]*data.NodeData + availability data.ObserverDataAvailabilityType } // NewNodesHolder will return a new instance of a nodesHolder -func NewNodesHolder(syncedNodes []*data.NodeData, fallbackNodes []*data.NodeData, shardIDs []uint32, availability data.ObserverDataAvailabilityType) (*nodesHolder, error) { - if len(shardIDs) == 0 { - return nil, errEmptyShardIDsList +func NewNodesHolder(syncedNodes []*data.NodeData, fallbackNodes []*data.NodeData, availability data.ObserverDataAvailabilityType) (*nodesHolder, error) { + if len(syncedNodes) == 0 && len(fallbackNodes) == 0 && availability != data.AvailabilityRecent { + return nil, errEmptyNodesList } - return &nodesHolder{ - syncedNodes: syncedNodes, - outOfSyncNodes: make([]*data.NodeData, 0), - syncedFallbackNodes: fallbackNodes, - outOfSyncFallbackNodes: make([]*data.NodeData, 0), - lastSyncedNodes: make(map[uint32]*data.NodeData), - shardIDs: shardIDs, - availability: availability, + allNodes: computeInitialNodeList(syncedNodes, fallbackNodes), + cache: make(map[string][]*data.NodeData), + availability: availability, }, nil } @@ -49,388 +48,170 @@ func (nh *nodesHolder) UpdateNodes(nodesWithSyncStatus []*data.NodeData) { if len(nodesWithSyncStatus) == 0 { return } - syncedNodes, syncedFallbackNodes, outOfSyncNodes, err := computeSyncedAndOutOfSyncNodes(nodesWithSyncStatus, nh.shardIDs, nh.availability) - if err != nil { - log.Error("cannot update nodes based on sync state", "error", err) - return - } - sameNumOfSynced := len(nh.syncedNodes) == len(syncedNodes) - sameNumOfSyncedFallback := len(nh.syncedFallbackNodes) == len(syncedFallbackNodes) - if sameNumOfSynced && sameNumOfSyncedFallback && len(outOfSyncNodes) == 0 { - nh.printSyncedNodesInShardsUnprotected() - // early exit as all the nodes are in sync - return - } + nh.mut.Lock() + defer nh.mut.Unlock() - syncedNodesMap := nodesSliceToShardedMap(syncedNodes) - syncedFallbackNodesMap := nodesSliceToShardedMap(syncedFallbackNodes) + nh.allNodes = make(map[uint32][]*data.NodeData) + nh.cache = make(map[string][]*data.NodeData) + for _, node := range nodesWithSyncStatus { + shouldSkipNode := nh.availability == data.AvailabilityRecent && !node.IsSnapshotless || + nh.availability == data.AvailabilityAll && node.IsSnapshotless + if shouldSkipNode { + continue + } + nh.allNodes[node.ShardId] = append(nh.allNodes[node.ShardId], node) + } - nh.removeOutOfSyncNodesUnprotected(outOfSyncNodes, syncedNodesMap, syncedFallbackNodesMap) - nh.addSyncedNodesUnprotected(syncedNodes, syncedFallbackNodes) - nh.printSyncedNodesInShardsUnprotected() + nh.printNodesInShardsUnprotected() } // GetSyncedNodes returns all the synced nodes -func (nh *nodesHolder) GetSyncedNodes() []*data.NodeData { - nh.mut.RLock() - defer nh.mut.RUnlock() - - return copyNodes(nh.syncedNodes) +func (nh *nodesHolder) GetSyncedNodes(shardID uint32) []*data.NodeData { + return nh.getObservers(syncedNodesCache, shardID) } // GetSyncedFallbackNodes returns all the synced fallback nodes -func (nh *nodesHolder) GetSyncedFallbackNodes() []*data.NodeData { - nh.mut.RLock() - defer nh.mut.RUnlock() - - return copyNodes(nh.syncedFallbackNodes) +func (nh *nodesHolder) GetSyncedFallbackNodes(shardID uint32) []*data.NodeData { + return nh.getObservers(syncedFallbackCache, shardID) } // GetOutOfSyncNodes returns all the out of sync nodes -func (nh *nodesHolder) GetOutOfSyncNodes() []*data.NodeData { - nh.mut.RLock() - defer nh.mut.RUnlock() - - return copyNodes(nh.outOfSyncNodes) +func (nh *nodesHolder) GetOutOfSyncNodes(shardID uint32) []*data.NodeData { + return nh.getObservers(outOfSyncNodesCache, shardID) } // GetOutOfSyncFallbackNodes returns all the out of sync fallback nodes -func (nh *nodesHolder) GetOutOfSyncFallbackNodes() []*data.NodeData { - nh.mut.RLock() - defer nh.mut.RUnlock() - - return copyNodes(nh.outOfSyncFallbackNodes) +func (nh *nodesHolder) GetOutOfSyncFallbackNodes(shardID uint32) []*data.NodeData { + return nh.getObservers(outOfSyncFallbackNodes, shardID) } -// GetLastSyncedNodes returns the internal map of the last synced nodes -func (nh *nodesHolder) GetLastSyncedNodes() map[uint32]*data.NodeData { - mapCopy := make(map[uint32]*data.NodeData, 0) +// Count computes and returns the total number of nodes +func (nh *nodesHolder) Count() int { + counter := 0 nh.mut.RLock() - for key, value := range nh.lastSyncedNodes { - mapCopy[key] = value - } - nh.mut.RUnlock() - - return mapCopy -} - -// IsInterfaceNil returns true if there is no value under the interface -func (nh *nodesHolder) IsInterfaceNil() bool { - return nh == nil -} - -func copyNodes(nodes []*data.NodeData) []*data.NodeData { - sliceCopy := make([]*data.NodeData, 0, len(nodes)) - for _, node := range nodes { - sliceCopy = append(sliceCopy, node) - } - - return sliceCopy -} - -func (nh *nodesHolder) printSyncedNodesInShardsUnprotected() { - inSyncAddresses := make(map[uint32][]string, 0) - for _, syncedNode := range nh.syncedNodes { - inSyncAddresses[syncedNode.ShardId] = append(inSyncAddresses[syncedNode.ShardId], syncedNode.Address) - } + defer nh.mut.RUnlock() - inSyncFallbackAddresses := make(map[uint32][]string, 0) - for _, syncedFallbackNode := range nh.syncedFallbackNodes { - inSyncFallbackAddresses[syncedFallbackNode.ShardId] = append(inSyncFallbackAddresses[syncedFallbackNode.ShardId], syncedFallbackNode.Address) + for _, nodes := range nh.allNodes { + counter += len(nodes) } - for _, shardID := range nh.shardIDs { - totalNumOfActiveNodes := len(inSyncAddresses[shardID]) + len(inSyncFallbackAddresses[shardID]) - // if none of them is active, use the backup if exists - hasBackup := nh.lastSyncedNodes[shardID] != nil - if totalNumOfActiveNodes == 0 && hasBackup { - totalNumOfActiveNodes++ - inSyncAddresses[shardID] = append(inSyncAddresses[shardID], nh.lastSyncedNodes[shardID].Address) - } - nodesType := "regular active nodes" - if nh.availability == data.AvailabilityRecent { - nodesType = "snapshotless active nodes" - } - log.Info(fmt.Sprintf("shard %d %s", shardID, nodesType), - "observers count", totalNumOfActiveNodes, - "addresses", strings.Join(inSyncAddresses[shardID], ", "), - "fallback addresses", strings.Join(inSyncFallbackAddresses[shardID], ", ")) - } + return counter } -func computeSyncedAndOutOfSyncNodes(nodes []*data.NodeData, shardIDs []uint32, availability data.ObserverDataAvailabilityType) ([]*data.NodeData, []*data.NodeData, []*data.NodeData, error) { - tempSyncedNodesMap := make(map[uint32][]*data.NodeData) - tempSyncedFallbackNodesMap := make(map[uint32][]*data.NodeData) - tempNotSyncedNodesMap := make(map[uint32][]*data.NodeData) - - for _, node := range nodes { - if node.IsSynced { - if node.IsFallback { - tempSyncedFallbackNodesMap[node.ShardId] = append(tempSyncedFallbackNodesMap[node.ShardId], node) - } else { - tempSyncedNodesMap[node.ShardId] = append(tempSyncedNodesMap[node.ShardId], node) - } - continue - } +func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.NodeData { + cacheKey := getCacheKey(cache, shardID) + nh.mut.RLock() + cachedValues, exists := nh.cache[getCacheKey(cache, shardID)] + nh.mut.RUnlock() - tempNotSyncedNodesMap[node.ShardId] = append(tempNotSyncedNodesMap[node.ShardId], node) + if exists { + return cachedValues } - syncedNodes := make([]*data.NodeData, 0) - syncedFallbackNodes := make([]*data.NodeData, 0) - notSyncedNodes := make([]*data.NodeData, 0) - for _, shardID := range shardIDs { - syncedNodes = append(syncedNodes, tempSyncedNodesMap[shardID]...) - syncedFallbackNodes = append(syncedFallbackNodes, tempSyncedFallbackNodesMap[shardID]...) - notSyncedNodes = append(notSyncedNodes, tempNotSyncedNodesMap[shardID]...) - - totalLen := len(tempSyncedNodesMap[shardID]) + len(tempSyncedFallbackNodesMap[shardID]) + len(tempNotSyncedNodesMap[shardID]) - if totalLen == 0 { - if availability != data.AvailabilityRecent { - return nil, nil, nil, fmt.Errorf("%w for shard %d - no synced or not synced node", errWrongConfiguration, shardID) - } + // nodes not cached, compute the list and update the cache + recomputedList := make([]*data.NodeData, 0) + nh.mut.Lock() + for _, node := range nh.allNodes[shardID] { + if areCompatibleParameters(cache, node) { + recomputedList = append(recomputedList, node) } } + nh.cache[cacheKey] = recomputedList + nh.mut.Unlock() - return syncedNodes, syncedFallbackNodes, notSyncedNodes, nil + return recomputedList } -func (nh *nodesHolder) addSyncedNodesUnprotected(receivedSyncedNodes []*data.NodeData, receivedSyncedFallbackNodes []*data.NodeData) { - syncedNodesPerShard := make(map[uint32][]string) - for _, node := range receivedSyncedNodes { - nh.removeFromOutOfSyncIfNeededUnprotected(node) - syncedNodesPerShard[node.ShardId] = append(syncedNodesPerShard[node.ShardId], node.Address) - if nh.isReceivedSyncedNodeExistent(node) { - continue - } - - nh.syncedNodes = append(nh.syncedNodes, node) +func areCompatibleParameters(cache cacheType, node *data.NodeData) bool { + isSynced, isFallback := node.IsSynced, node.IsFallback + if cache == syncedFallbackCache && isSynced && isFallback { + return true } - - for _, node := range receivedSyncedFallbackNodes { - nh.removeFromOutOfSyncIfNeededUnprotected(node) - if nh.isReceivedSyncedNodeExistentAsFallback(node) { - continue - } - - nh.syncedFallbackNodes = append(nh.syncedFallbackNodes, node) + if cache == outOfSyncFallbackNodes && !isSynced && isFallback { + return true } - - // if there is at least one synced node regular received, clean the backup list - for _, shardId := range nh.shardIDs { - if len(syncedNodesPerShard[shardId]) != 0 { - delete(nh.lastSyncedNodes, shardId) - } + if cache == syncedNodesCache && isSynced && !isFallback { + return true } -} - -func (nh *nodesHolder) removeFromOutOfSyncIfNeededUnprotected(node *data.NodeData) { - if node.IsFallback { - nh.removeFallbackFromOutOfSyncListUnprotected(node) - return - } - - nh.removeRegularFromOutOfSyncListUnprotected(node) -} - -func (nh *nodesHolder) isReceivedSyncedNodeExistent(receivedNode *data.NodeData) bool { - for _, node := range nh.syncedNodes { - if node.Address == receivedNode.Address && node.ShardId == receivedNode.ShardId { - return true - } + if cache == outOfSyncNodesCache && !isSynced && !isFallback { + return true } return false } -func (nh *nodesHolder) isReceivedSyncedNodeExistentAsFallback(receivedNode *data.NodeData) bool { - for _, node := range nh.syncedFallbackNodes { - if node.Address == receivedNode.Address && node.ShardId == receivedNode.ShardId { - return true - } - } - - return false +func getCacheKey(cache cacheType, shardID uint32) string { + return fmt.Sprintf("%s_%d", cache, shardID) } -func (nh *nodesHolder) addToOutOfSyncUnprotected(node *data.NodeData) { - if node.IsFallback { - nh.addFallbackToOutOfSyncUnprotected(node) - return - } - - nh.addRegularToOutOfSyncUnprotected(node) -} - -func (nh *nodesHolder) addRegularToOutOfSyncUnprotected(node *data.NodeData) { - for _, oosNode := range nh.outOfSyncNodes { - if oosNode.Address == node.Address && oosNode.ShardId == node.ShardId { - return - } - } - - nh.outOfSyncNodes = append(nh.outOfSyncNodes, node) -} - -func (nh *nodesHolder) addFallbackToOutOfSyncUnprotected(node *data.NodeData) { - for _, oosNode := range nh.outOfSyncFallbackNodes { - if oosNode.Address == node.Address && oosNode.ShardId == node.ShardId { - return - } - } - - nh.outOfSyncFallbackNodes = append(nh.outOfSyncFallbackNodes, node) +// IsInterfaceNil returns true if there is no value under the interface +func (nh *nodesHolder) IsInterfaceNil() bool { + return nh == nil } -func (nh *nodesHolder) removeOutOfSyncNodesUnprotected( - outOfSyncNodes []*data.NodeData, - syncedNodesMap map[uint32][]*data.NodeData, - syncedFallbackNodesMap map[uint32][]*data.NodeData, -) { - minSyncedNodes := 1 - if nh.availability == data.AvailabilityRecent { - minSyncedNodes = 0 // allow the snapshotless list to be empty so regular observers can be used - } - if len(outOfSyncNodes) == 0 { - nh.outOfSyncNodes = make([]*data.NodeData, 0) - nh.outOfSyncFallbackNodes = make([]*data.NodeData, 0) - return - } - - for _, outOfSyncNode := range outOfSyncNodes { - hasOneSyncedNode := len(syncedNodesMap[outOfSyncNode.ShardId]) >= minSyncedNodes - hasEnoughSyncedFallbackNodes := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) > minSyncedNodes - canDeleteFallbackNode := hasOneSyncedNode || hasEnoughSyncedFallbackNodes - if outOfSyncNode.IsFallback && canDeleteFallbackNode { - nh.removeNodeUnprotected(outOfSyncNode) - continue - } +func (nh *nodesHolder) printNodesInShardsUnprotected() { + nodesByType := make(map[uint32]map[cacheType][]*data.NodeData) - // if trying to delete last fallback, use last known synced node - // if backup node does not exist, keep fallback - hasBackup := nh.lastSyncedNodes[outOfSyncNode.ShardId] != nil - if outOfSyncNode.IsFallback && hasBackup { - nh.removeNodeUnprotected(outOfSyncNode) - continue + // define a function to get the cache type for a node + getCacheType := func(node *data.NodeData) cacheType { + if node.IsFallback { + if node.IsSynced { + return syncedFallbackCache + } + return outOfSyncFallbackNodes } - - hasEnoughSyncedNodes := len(syncedNodesMap[outOfSyncNode.ShardId]) >= minSyncedNodes - if hasEnoughSyncedNodes { - nh.removeNodeUnprotected(outOfSyncNode) - continue + if node.IsSynced { + return syncedNodesCache } + return outOfSyncNodesCache + } - // trying to remove last synced node - // if fallbacks are available, save this one as backup and use fallbacks - // else, keep using this one - // save this last regular observer as backup in case fallbacks go offline - // also, if this is the old fallback observer which didn't get synced, keep it in list - wasSyncedAtPreviousStep := nh.isReceivedSyncedNodeExistent(outOfSyncNode) - isBackupObserver := nh.lastSyncedNodes[outOfSyncNode.ShardId] == outOfSyncNode - isRegularSyncedBefore := !outOfSyncNode.IsFallback && wasSyncedAtPreviousStep - if isRegularSyncedBefore || isBackupObserver { - log.Info("backup observer updated", - "address", outOfSyncNode.Address, - "is fallback", outOfSyncNode.IsFallback, - "shard", outOfSyncNode.ShardId) - nh.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode - } - hasOneSyncedFallbackNode := len(syncedFallbackNodesMap[outOfSyncNode.ShardId]) >= minSyncedNodes - if hasOneSyncedFallbackNode { - nh.removeNodeUnprotected(outOfSyncNode) - continue + // populate nodesByType map + for shard, nodes := range nh.allNodes { + if nodesByType[shard] == nil { + nodesByType[shard] = make(map[cacheType][]*data.NodeData) } - // safe to delete regular observer, as it is already in lastSyncedNodes map - if !outOfSyncNode.IsFallback { - nh.removeNodeUnprotected(outOfSyncNode) - continue + for _, node := range nodes { + cache := getCacheType(node) + nodesByType[shard][cache] = append(nodesByType[shard][cache], node) } - - // this is a fallback node, with no synced nodes. - // save it as backup and delete it from its list - nh.lastSyncedNodes[outOfSyncNode.ShardId] = outOfSyncNode - nh.removeNodeUnprotected(outOfSyncNode) - } -} - -func (nh *nodesHolder) removeNodeUnprotected(node *data.NodeData) { - nh.removeNodeFromSyncedNodesUnprotected(node) - nh.addToOutOfSyncUnprotected(node) -} - -func (nh *nodesHolder) removeNodeFromSyncedNodesUnprotected(nodeToRemove *data.NodeData) { - if nodeToRemove.IsFallback { - nh.removeFallbackFromSyncedListUnprotected(nodeToRemove) - return - } - - nh.removeRegularFromSyncedListUnprotected(nodeToRemove) -} - -func (nh *nodesHolder) removeRegularFromSyncedListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, nh.syncedNodes) - if nodeIndex == -1 { - return } - copy(nh.syncedNodes[nodeIndex:], nh.syncedNodes[nodeIndex+1:]) - nh.syncedNodes[len(nh.syncedNodes)-1] = nil - nh.syncedNodes = nh.syncedNodes[:len(nh.syncedNodes)-1] -} - -func (nh *nodesHolder) removeFallbackFromSyncedListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, nh.syncedFallbackNodes) - if nodeIndex == -1 { - return + printHeader := "regular nodes" + if nh.availability == data.AvailabilityRecent { + printHeader = "snapshotless nodes" } - copy(nh.syncedFallbackNodes[nodeIndex:], nh.syncedFallbackNodes[nodeIndex+1:]) - nh.syncedFallbackNodes[len(nh.syncedFallbackNodes)-1] = nil - nh.syncedFallbackNodes = nh.syncedFallbackNodes[:len(nh.syncedFallbackNodes)-1] -} - -func (nh *nodesHolder) removeRegularFromOutOfSyncListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, nh.outOfSyncNodes) - if nodeIndex == -1 { - return + for shard, nodesByCache := range nodesByType { + log.Info(fmt.Sprintf("shard %d %s", shard, printHeader), + "synced observers", getNodesListAsString(nodesByCache[syncedNodesCache]), + "synced fallback observers", getNodesListAsString(nodesByCache[syncedFallbackCache]), + "out of sync observers", getNodesListAsString(nodesByCache[outOfSyncNodesCache]), + "out of sync fallback observers", getNodesListAsString(nodesByCache[outOfSyncFallbackNodes])) } - - copy(nh.outOfSyncNodes[nodeIndex:], nh.outOfSyncNodes[nodeIndex+1:]) - nh.outOfSyncNodes[len(nh.outOfSyncNodes)-1] = nil - nh.outOfSyncNodes = nh.outOfSyncNodes[:len(nh.outOfSyncNodes)-1] } -func (nh *nodesHolder) removeFallbackFromOutOfSyncListUnprotected(nodeToRemove *data.NodeData) { - nodeIndex := getIndexFromList(nodeToRemove, nh.outOfSyncFallbackNodes) - if nodeIndex == -1 { - return +func getNodesListAsString(nodes []*data.NodeData) string { + addressesString := "" + for _, node := range nodes { + addressesString += fmt.Sprintf("%s, ", node.Address) } - copy(nh.outOfSyncFallbackNodes[nodeIndex:], nh.outOfSyncFallbackNodes[nodeIndex+1:]) - nh.outOfSyncFallbackNodes[len(nh.outOfSyncFallbackNodes)-1] = nil - nh.outOfSyncFallbackNodes = nh.outOfSyncFallbackNodes[:len(nh.outOfSyncFallbackNodes)-1] + return strings.TrimSuffix(addressesString, ", ") } -func getIndexFromList(providedNode *data.NodeData, list []*data.NodeData) int { - nodeIndex := -1 - for idx, node := range list { - if node.Address == providedNode.Address && node.ShardId == providedNode.ShardId { - nodeIndex = idx - break - } +func computeInitialNodeList(regularNodes []*data.NodeData, fallbackNodes []*data.NodeData) map[uint32][]*data.NodeData { + mapToReturn := make(map[uint32][]*data.NodeData) + // in the first step, consider all the nodes to be active + for _, node := range regularNodes { + node.IsSynced = true + mapToReturn[node.ShardId] = append(mapToReturn[node.ShardId], node) } - - return nodeIndex -} - -func nodesSliceToShardedMap(nodes []*data.NodeData) map[uint32][]*data.NodeData { - newNodes := make(map[uint32][]*data.NodeData) - for _, node := range nodes { - shardId := node.ShardId - newNodes[shardId] = append(newNodes[shardId], node) + for _, node := range fallbackNodes { + node.IsSynced = true + mapToReturn[node.ShardId] = append(mapToReturn[node.ShardId], node) } - - return newNodes + return mapToReturn } diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go index 0c392aa0..79f6f878 100644 --- a/observer/holder/nodesHolder_test.go +++ b/observer/holder/nodesHolder_test.go @@ -1,284 +1,200 @@ package holder import ( - "errors" "fmt" - "sort" + "sync" "testing" "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-proxy-go/data" "github.com/stretchr/testify/require" ) -func TestNodesHolder_GetLastSyncedNodes(t *testing.T) { - t.Parallel() +func TestNodesHolder_ConstructorAndGetters(t *testing.T) { + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityRecent) + require.Equal(t, errEmptyNodesList, err) + require.True(t, check.IfNil(nh)) - syncedNodes := []*data.NodeData{{Address: "addr0", ShardId: core.MetachainShardId}, {Address: "addr1", ShardId: 0}} - fallbackNodes := []*data.NodeData{{Address: "fallback-addr0", ShardId: core.MetachainShardId}, {Address: "fallback-addr1", ShardId: 0}} - shardIds := []uint32{0, core.MetachainShardId} + syncedNodes := createTestNodes(3) + setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) - nodesHolder, err := NewNodesHolder(syncedNodes, fallbackNodes, shardIds, data.AvailabilityAll) - require.NoError(t, err) + fallbackNodes := createTestNodes(3) + setPropertyToNodes(fallbackNodes, "synced", true, 0, 1, 2) + setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2) - require.Equal(t, syncedNodes, nodesHolder.GetSyncedNodes()) - require.Equal(t, fallbackNodes, nodesHolder.GetSyncedFallbackNodes()) - require.Empty(t, nodesHolder.GetOutOfSyncFallbackNodes()) - require.Empty(t, nodesHolder.GetOutOfSyncNodes()) - require.Empty(t, nodesHolder.GetLastSyncedNodes()) + nh, err = NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) + require.NoError(t, err) + require.False(t, nh.IsInterfaceNil()) + + require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetSyncedNodes(0)) + require.Equal(t, []*data.NodeData{syncedNodes[1]}, nh.GetSyncedNodes(1)) + require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetSyncedNodes(core.MetachainShardId)) + + require.Equal(t, []*data.NodeData{fallbackNodes[0]}, nh.GetSyncedFallbackNodes(0)) + require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetSyncedFallbackNodes(1)) + require.Equal(t, []*data.NodeData{fallbackNodes[2]}, nh.GetSyncedFallbackNodes(core.MetachainShardId)) + + setPropertyToNodes(syncedNodes, "synced", false, 0, 2) + setPropertyToNodes(fallbackNodes, "synced", false, 1) + nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) + require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetOutOfSyncNodes(0)) + require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncNodes(1)) + require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetOutOfSyncNodes(core.MetachainShardId)) + + require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(0)) + require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetOutOfSyncFallbackNodes(1)) + require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(core.MetachainShardId)) } -func TestComputeSyncAndOutOfSyncNodes(t *testing.T) { - t.Parallel() +func TestNodesHolder_UpdateNodesAvailabilityAll(t *testing.T) { + syncedNodes := createTestNodes(3) + setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) - t.Run("all nodes synced", testComputeSyncedAndOutOfSyncNodesAllNodesSynced) - t.Run("enough synced nodes", testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers) - t.Run("all nodes are out of sync", testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced) - t.Run("invalid config - no node", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll) - t.Run("invalid config - no node in a shard", testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard) - t.Run("snapshotless nodes should work with no node in a shard", testSnapshotlessNodesShouldWorkIfNoNodeInShardExists) - t.Run("edge case - address should not exist in both sync and not-synced lists", testEdgeCaseAddressShouldNotExistInBothLists) -} + fallbackNodes := createTestNodes(3) + setPropertyToNodes(fallbackNodes, "synced", true, 0, 1, 2) + setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2) -func testComputeSyncedAndOutOfSyncNodesAllNodesSynced(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "2", ShardId: 1, IsSynced: true}, - {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, - } + nh, err := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) + require.NoError(t, err) - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) - require.Equal(t, []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "2", ShardId: 1, IsSynced: true}, - }, synced) - require.Equal(t, []*data.NodeData{ - {Address: "1", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "3", ShardId: 1, IsSynced: true, IsFallback: true}, - }, syncedFb) - require.Empty(t, notSynced) -} + syncedNodes[0].IsSynced = false + syncedNodes[1].IsSynced = false + nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) -func testComputeSyncedAndOutOfSyncNodesEnoughSyncedObservers(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "1", ShardId: 0, IsSynced: false}, - {Address: "2", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "3", ShardId: 1, IsSynced: true}, - {Address: "4", ShardId: 1, IsSynced: false}, - {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, - } + require.Equal(t, []*data.NodeData{}, nh.GetSyncedNodes(0)) + require.Equal(t, []*data.NodeData{}, nh.GetSyncedNodes(1)) + require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetSyncedNodes(core.MetachainShardId)) - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) - require.Equal(t, []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: true}, - {Address: "3", ShardId: 1, IsSynced: true}, - }, synced) - require.Equal(t, []*data.NodeData{ - {Address: "2", ShardId: 0, IsSynced: true, IsFallback: true}, - {Address: "5", ShardId: 1, IsSynced: true, IsFallback: true}, - }, syncedFb) - require.Equal(t, []*data.NodeData{ - {Address: "1", ShardId: 0, IsSynced: false}, - {Address: "4", ShardId: 1, IsSynced: false}, - }, notSynced) + require.Equal(t, []*data.NodeData{fallbackNodes[0]}, nh.GetSyncedFallbackNodes(0)) + require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetSyncedFallbackNodes(1)) + require.Equal(t, []*data.NodeData{fallbackNodes[2]}, nh.GetSyncedFallbackNodes(core.MetachainShardId)) } -func testComputeSyncedAndOutOfSyncNodesAllNodesNotSynced(t *testing.T) { - t.Parallel() +func TestNodesHolder_UpdateNodesAvailabilityRecent(t *testing.T) { + syncedNodes := createTestNodes(3) + setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) + setPropertyToNodes(syncedNodes, "snapshotless", true, 0, 1, 2) - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - {Address: "0", ShardId: 0, IsSynced: false}, - {Address: "1", ShardId: 0, IsSynced: false, IsFallback: true}, - {Address: "2", ShardId: 1, IsSynced: false}, - {Address: "3", ShardId: 1, IsSynced: false, IsFallback: true}, - } + fallbackNodes := createTestNodes(3) + setPropertyToNodes(fallbackNodes, "synced", true, 0, 1, 2) + setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2) + setPropertyToNodes(fallbackNodes, "snapshotless", true, 0, 1, 2) - synced, syncedFb, notSynced, _ := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) - require.Equal(t, []*data.NodeData{}, synced) - require.Equal(t, []*data.NodeData{}, syncedFb) - require.Equal(t, input, notSynced) -} + nh, err := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityRecent) + require.NoError(t, err) -func testEdgeCaseAddressShouldNotExistInBothLists(t *testing.T) { - t.Parallel() + syncedNodes[0].IsSynced = false + syncedNodes[2].IsSnapshotless = false // this will force the nodesHolder to remove it - allNodes := prepareNodes(10) + nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) - nodesMap := nodesSliceToShardedMap(allNodes) - nh := &nodesHolder{ - shardIDs: getSortedShardIDsSlice(nodesMap), - syncedNodes: allNodes, - } + require.Equal(t, []*data.NodeData{}, nh.GetSyncedNodes(0)) + require.Equal(t, []*data.NodeData{syncedNodes[1]}, nh.GetSyncedNodes(1)) + require.Equal(t, []*data.NodeData{}, nh.GetSyncedNodes(core.MetachainShardId)) - setSyncedStateToNodes(allNodes, false, 1, 3, 5, 7, 9) - - nh.UpdateNodes(allNodes) - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr6", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(nh.syncedNodes)) - require.Equal(t, []data.NodeData{ - {Address: "addr1", ShardId: 0, IsSynced: false}, - {Address: "addr3", ShardId: 0, IsSynced: false}, - {Address: "addr5", ShardId: 1, IsSynced: false}, - {Address: "addr7", ShardId: 1, IsSynced: false}, - {Address: "addr9", ShardId: 1, IsSynced: false}, - }, convertAndSortSlice(nh.outOfSyncNodes)) - require.False(t, slicesHaveCommonObjects(nh.syncedNodes, nh.outOfSyncNodes)) - - allNodes = prepareNodes(10) - - nh.UpdateNodes(allNodes) - - require.Equal(t, []data.NodeData{ - {Address: "addr0", ShardId: 0, IsSynced: true}, - {Address: "addr1", ShardId: 0, IsSynced: true}, - {Address: "addr2", ShardId: 0, IsSynced: true}, - {Address: "addr3", ShardId: 0, IsSynced: true}, - {Address: "addr4", ShardId: 0, IsSynced: true}, - {Address: "addr5", ShardId: 1, IsSynced: true}, - {Address: "addr6", ShardId: 1, IsSynced: true}, - {Address: "addr7", ShardId: 1, IsSynced: true}, - {Address: "addr8", ShardId: 1, IsSynced: true}, - {Address: "addr9", ShardId: 1, IsSynced: true}, - }, convertAndSortSlice(nh.syncedNodes)) - require.False(t, slicesHaveCommonObjects(nh.syncedNodes, nh.outOfSyncNodes)) + require.Equal(t, []*data.NodeData{fallbackNodes[0]}, nh.GetSyncedFallbackNodes(0)) + require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetSyncedFallbackNodes(1)) + require.Equal(t, []*data.NodeData{fallbackNodes[2]}, nh.GetSyncedFallbackNodes(core.MetachainShardId)) } -func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeAtAll(t *testing.T) { - t.Parallel() - - shardIDs := []uint32{0, 1} - var input []*data.NodeData - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) - require.Error(t, err) - require.Nil(t, synced) - require.Nil(t, syncedFb) - require.Nil(t, notSynced) - - // no node in one shard - shardIDs = []uint32{0, 1} - input = []*data.NodeData{ - { - Address: "0", ShardId: 0, IsSynced: true, - }, - } - synced, syncedFb, notSynced, err = computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) - require.True(t, errors.Is(err, errWrongConfiguration)) - require.Nil(t, synced) - require.Nil(t, syncedFb) - require.Nil(t, notSynced) -} +func TestNodesHolder_GettersShouldUseCachedValues(t *testing.T) { + syncedNodes := createTestNodes(1) + setPropertyToNodes(syncedNodes, "synced", true, 0) + setPropertyToNodes(syncedNodes, "snapshotless", true, 0) -func testComputeSyncedAndOutOfSyncNodesInvalidConfigurationNoNodeInAShard(t *testing.T) { - t.Parallel() + fallbackNodes := createTestNodes(0) - // no node in one shard - shardIDs := []uint32{0, 1} - input := []*data.NodeData{ - { - Address: "0", ShardId: 0, IsSynced: true, - }, - } - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityAll) - require.True(t, errors.Is(err, errWrongConfiguration)) - require.Nil(t, synced) - require.Nil(t, syncedFb) - require.Nil(t, notSynced) -} + nh, err := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityRecent) + require.NoError(t, err) -func testSnapshotlessNodesShouldWorkIfNoNodeInShardExists(t *testing.T) { - t.Parallel() + // warm the cache + require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetSyncedNodes(0)) - shardIDs := []uint32{0, core.MetachainShardId} - input := []*data.NodeData{ - { - Address: "m", ShardId: core.MetachainShardId, IsSynced: true, IsSnapshotless: true, - }, - } - synced, syncedFb, notSynced, err := computeSyncedAndOutOfSyncNodes(input, shardIDs, data.AvailabilityRecent) - require.NoError(t, err) - require.Empty(t, notSynced) - require.Empty(t, syncedFb) - require.Equal(t, input, synced) -} + // check the cache + require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.cache[getCacheKey(syncedNodesCache, 0)]) -func slicesHaveCommonObjects(firstSlice []*data.NodeData, secondSlice []*data.NodeData) bool { - nodeDataToStr := func(nd *data.NodeData) string { - return fmt.Sprintf("%s%d", nd.Address, nd.ShardId) - } - firstSliceItems := make(map[string]struct{}) - for _, el := range firstSlice { - firstSliceItems[nodeDataToStr(el)] = struct{}{} - } + // put something else in the cache and test it + newValue := []*data.NodeData{{Address: "test-cached-observer"}} + nh.cache[getCacheKey(syncedNodesCache, 0)] = newValue + require.Equal(t, newValue, nh.GetSyncedNodes(0)) - for _, el := range secondSlice { - nodeDataStr := nodeDataToStr(el) - _, found := firstSliceItems[nodeDataStr] - if found { - return true - } - } + // invalid nodes update - should not invalidate the cache + nh.UpdateNodes([]*data.NodeData{}) + require.Equal(t, newValue, nh.GetSyncedNodes(0)) + + // invalidate the cache by updating the nodes + nh.UpdateNodes(syncedNodes) + require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetSyncedNodes(0)) +} - return false +func TestNodesHolder_ConcurrentOperations(t *testing.T) { + syncedNodes := createTestNodes(100) + fallbackNodes := createTestNodes(100) + nh, _ := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityRecent) + + numOperations := 100_000 + wg := sync.WaitGroup{} + wg.Add(numOperations) + for i := 0; i < numOperations; i++ { + go func(index int) { + switch index { + case 0: + nh.UpdateNodes(createTestNodes(100)) + case 1: + _ = nh.Count() + case 2: + _ = nh.GetSyncedFallbackNodes(uint32(index % 3)) + case 3: + _ = nh.GetOutOfSyncFallbackNodes(uint32(index % 3)) + case 4: + _ = nh.GetSyncedNodes(uint32(index % 3)) + case 5: + _ = nh.GetOutOfSyncNodes(uint32(index % 3)) + } + wg.Done() + }(i) + } + wg.Wait() } -func prepareNodes(count int) []*data.NodeData { - nodes := make([]*data.NodeData, 0, count) - for i := 0; i < count; i++ { - shardID := uint32(0) - if i >= count/2 { - shardID = 1 +func createTestNodes(numNodes int) []*data.NodeData { + getShard := func(index int) uint32 { + switch index % 3 { + case 0: + return 0 + case 1: + return 1 + case 2: + return core.MetachainShardId + default: + return 0 } + } + nodes := make([]*data.NodeData, 0, numNodes) + for i := 0; i < numNodes; i++ { nodes = append(nodes, &data.NodeData{ - ShardId: shardID, - Address: fmt.Sprintf("addr%d", i), - IsSynced: true, + Address: fmt.Sprintf("https://observer-%d:8080", i), + ShardId: getShard(i), }) } return nodes } -func getSortedShardIDsSlice(nodesOnShards map[uint32][]*data.NodeData) []uint32 { - shardIDs := make([]uint32, 0) - for shardID := range nodesOnShards { - shardIDs = append(shardIDs, shardID) - } - sort.SliceStable(shardIDs, func(i, j int) bool { - return shardIDs[i] < shardIDs[j] - }) - - return shardIDs -} - -func setSyncedStateToNodes(nodes []*data.NodeData, state bool, indices ...int) { - for _, idx := range indices { - nodes[idx].IsSynced = state - } -} - -func convertAndSortSlice(nodes []*data.NodeData) []data.NodeData { - newSlice := make([]data.NodeData, 0, len(nodes)) - for _, node := range nodes { - newSlice = append(newSlice, *node) +func setPropertyToNodes(nodes []*data.NodeData, property string, propertyVal bool, indices ...int) { + switch property { + case "snapshotless": + for _, i := range indices { + nodes[i].IsSnapshotless = propertyVal + } + case "fallback": + for _, i := range indices { + nodes[i].IsFallback = propertyVal + } + case "synced": + for _, i := range indices { + nodes[i].IsSynced = propertyVal + } } - - sort.Slice(newSlice, func(i, j int) bool { - return newSlice[i].Address < newSlice[j].Address - }) - - return newSlice } diff --git a/observer/interface.go b/observer/interface.go index 0e545a59..edaf92ec 100644 --- a/observer/interface.go +++ b/observer/interface.go @@ -15,10 +15,10 @@ type NodesProviderHandler interface { // NodesHolder defines the actions of a component that is able to hold nodes type NodesHolder interface { UpdateNodes(nodesWithSyncStatus []*data.NodeData) - GetSyncedNodes() []*data.NodeData - GetSyncedFallbackNodes() []*data.NodeData - GetOutOfSyncNodes() []*data.NodeData - GetOutOfSyncFallbackNodes() []*data.NodeData - GetLastSyncedNodes() map[uint32]*data.NodeData + GetSyncedNodes(shardID uint32) []*data.NodeData + GetSyncedFallbackNodes(shardID uint32) []*data.NodeData + GetOutOfSyncNodes(shardID uint32) []*data.NodeData + GetOutOfSyncFallbackNodes(shardID uint32) []*data.NodeData + Count() int IsInterfaceNil() bool } From 394794c36fac758973ce55b2ead75db276396c7c Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Thu, 21 Sep 2023 16:53:29 +0300 Subject: [PATCH 08/13] fix after review --- process/scQueryProcessor.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/process/scQueryProcessor.go b/process/scQueryProcessor.go index 3cdeb9b1..24508ac5 100644 --- a/process/scQueryProcessor.go +++ b/process/scQueryProcessor.go @@ -50,8 +50,8 @@ func (scQueryProcessor *SCQueryProcessor) ExecuteQuery(query *data.SCQuery) (*vm return nil, data.BlockInfo{}, err } - // TODO: if vm queries will allow historical block coordinates, adjust the data availability here - observers, err := scQueryProcessor.proc.GetObservers(shardID, data.AvailabilityRecent) + availability := getAvailabilityBasedOnVmQueryOptions(query) + observers, err := scQueryProcessor.proc.GetObservers(shardID, availability) if err != nil { return nil, data.BlockInfo{}, err } @@ -118,3 +118,11 @@ func (scQueryProcessor *SCQueryProcessor) createRequestFromQuery(query *data.SCQ func (scQueryProcessor *SCQueryProcessor) IsInterfaceNil() bool { return scQueryProcessor == nil } + +func getAvailabilityBasedOnVmQueryOptions(query *data.SCQuery) data.ObserverDataAvailabilityType { + availability := data.AvailabilityRecent + if query.BlockNonce.HasValue || len(query.BlockHash) > 0 { + availability = data.AvailabilityAll + } + return availability +} From d603fedf41f1510fefb18d300cac919e8765effe Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Thu, 21 Sep 2023 16:54:30 +0300 Subject: [PATCH 09/13] unit test fix --- observer/holder/nodesHolder_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go index 79f6f878..eb578bb7 100644 --- a/observer/holder/nodesHolder_test.go +++ b/observer/holder/nodesHolder_test.go @@ -12,7 +12,7 @@ import ( ) func TestNodesHolder_ConstructorAndGetters(t *testing.T) { - nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityRecent) + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityAll) require.Equal(t, errEmptyNodesList, err) require.True(t, check.IfNil(nh)) From f8fd10f786f4ef557d95c5e8a3a566c942151c71 Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Fri, 22 Sep 2023 11:55:26 +0300 Subject: [PATCH 10/13] fixes after review --- observer/holder/nodesHolder.go | 30 ++++++++++++++++------------- observer/holder/nodesHolder_test.go | 26 ++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 16 deletions(-) diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go index cf4b5841..04443c82 100644 --- a/observer/holder/nodesHolder.go +++ b/observer/holder/nodesHolder.go @@ -13,10 +13,10 @@ import ( type cacheType string const ( - syncedNodesCache cacheType = "syncedNodes" - outOfSyncNodesCache cacheType = "outOfSyncNodes" - syncedFallbackCache cacheType = "syncedFallbackNodes" - outOfSyncFallbackNodes cacheType = "outOfSyncFallbackNodes" + syncedNodesCache cacheType = "syncedNodes" + outOfSyncNodesCache cacheType = "outOfSyncNodes" + syncedFallbackNodesCache cacheType = "syncedFallbackNodes" + outOfSyncFallbackNodesCache cacheType = "outOfSyncFallbackNodes" ) var ( @@ -73,7 +73,7 @@ func (nh *nodesHolder) GetSyncedNodes(shardID uint32) []*data.NodeData { // GetSyncedFallbackNodes returns all the synced fallback nodes func (nh *nodesHolder) GetSyncedFallbackNodes(shardID uint32) []*data.NodeData { - return nh.getObservers(syncedFallbackCache, shardID) + return nh.getObservers(syncedFallbackNodesCache, shardID) } // GetOutOfSyncNodes returns all the out of sync nodes @@ -83,7 +83,7 @@ func (nh *nodesHolder) GetOutOfSyncNodes(shardID uint32) []*data.NodeData { // GetOutOfSyncFallbackNodes returns all the out of sync fallback nodes func (nh *nodesHolder) GetOutOfSyncFallbackNodes(shardID uint32) []*data.NodeData { - return nh.getObservers(outOfSyncFallbackNodes, shardID) + return nh.getObservers(outOfSyncFallbackNodesCache, shardID) } // Count computes and returns the total number of nodes @@ -102,7 +102,7 @@ func (nh *nodesHolder) Count() int { func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.NodeData { cacheKey := getCacheKey(cache, shardID) nh.mut.RLock() - cachedValues, exists := nh.cache[getCacheKey(cache, shardID)] + cachedValues, exists := nh.cache[cacheKey] nh.mut.RUnlock() if exists { @@ -112,6 +112,10 @@ func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.Nod // nodes not cached, compute the list and update the cache recomputedList := make([]*data.NodeData, 0) nh.mut.Lock() + cachedValues, exists = nh.cache[cacheKey] + if exists { + return cachedValues + } for _, node := range nh.allNodes[shardID] { if areCompatibleParameters(cache, node) { recomputedList = append(recomputedList, node) @@ -125,10 +129,10 @@ func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.Nod func areCompatibleParameters(cache cacheType, node *data.NodeData) bool { isSynced, isFallback := node.IsSynced, node.IsFallback - if cache == syncedFallbackCache && isSynced && isFallback { + if cache == syncedFallbackNodesCache && isSynced && isFallback { return true } - if cache == outOfSyncFallbackNodes && !isSynced && isFallback { + if cache == outOfSyncFallbackNodesCache && !isSynced && isFallback { return true } if cache == syncedNodesCache && isSynced && !isFallback { @@ -157,9 +161,9 @@ func (nh *nodesHolder) printNodesInShardsUnprotected() { getCacheType := func(node *data.NodeData) cacheType { if node.IsFallback { if node.IsSynced { - return syncedFallbackCache + return syncedFallbackNodesCache } - return outOfSyncFallbackNodes + return outOfSyncFallbackNodesCache } if node.IsSynced { return syncedNodesCache @@ -187,9 +191,9 @@ func (nh *nodesHolder) printNodesInShardsUnprotected() { for shard, nodesByCache := range nodesByType { log.Info(fmt.Sprintf("shard %d %s", shard, printHeader), "synced observers", getNodesListAsString(nodesByCache[syncedNodesCache]), - "synced fallback observers", getNodesListAsString(nodesByCache[syncedFallbackCache]), + "synced fallback observers", getNodesListAsString(nodesByCache[syncedFallbackNodesCache]), "out of sync observers", getNodesListAsString(nodesByCache[outOfSyncNodesCache]), - "out of sync fallback observers", getNodesListAsString(nodesByCache[outOfSyncFallbackNodes])) + "out of sync fallback observers", getNodesListAsString(nodesByCache[outOfSyncFallbackNodesCache])) } } diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go index eb578bb7..6e296867 100644 --- a/observer/holder/nodesHolder_test.go +++ b/observer/holder/nodesHolder_test.go @@ -6,15 +6,16 @@ import ( "testing" "github.com/multiversx/mx-chain-core-go/core" - "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-proxy-go/data" "github.com/stretchr/testify/require" ) func TestNodesHolder_ConstructorAndGetters(t *testing.T) { + t.Parallel() + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityAll) require.Equal(t, errEmptyNodesList, err) - require.True(t, check.IfNil(nh)) + require.Nil(t, nh) syncedNodes := createTestNodes(3) setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) @@ -25,7 +26,7 @@ func TestNodesHolder_ConstructorAndGetters(t *testing.T) { nh, err = NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) require.NoError(t, err) - require.False(t, nh.IsInterfaceNil()) + require.NotNil(t, nh) require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetSyncedNodes(0)) require.Equal(t, []*data.NodeData{syncedNodes[1]}, nh.GetSyncedNodes(1)) @@ -45,9 +46,22 @@ func TestNodesHolder_ConstructorAndGetters(t *testing.T) { require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(0)) require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetOutOfSyncFallbackNodes(1)) require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(core.MetachainShardId)) + require.Equal(t, 6, nh.Count()) +} + +func TestNodesHolder_IsInterfaceNil(t *testing.T) { + t.Parallel() + + var nh *nodesHolder + require.True(t, nh.IsInterfaceNil()) + + nh, _ = NewNodesHolder([]*data.NodeData{{Address: "adr"}}, []*data.NodeData{}, data.AvailabilityAll) + require.False(t, nh.IsInterfaceNil()) } func TestNodesHolder_UpdateNodesAvailabilityAll(t *testing.T) { + t.Parallel() + syncedNodes := createTestNodes(3) setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) @@ -72,6 +86,8 @@ func TestNodesHolder_UpdateNodesAvailabilityAll(t *testing.T) { } func TestNodesHolder_UpdateNodesAvailabilityRecent(t *testing.T) { + t.Parallel() + syncedNodes := createTestNodes(3) setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) setPropertyToNodes(syncedNodes, "snapshotless", true, 0, 1, 2) @@ -99,6 +115,8 @@ func TestNodesHolder_UpdateNodesAvailabilityRecent(t *testing.T) { } func TestNodesHolder_GettersShouldUseCachedValues(t *testing.T) { + t.Parallel() + syncedNodes := createTestNodes(1) setPropertyToNodes(syncedNodes, "synced", true, 0) setPropertyToNodes(syncedNodes, "snapshotless", true, 0) @@ -129,6 +147,8 @@ func TestNodesHolder_GettersShouldUseCachedValues(t *testing.T) { } func TestNodesHolder_ConcurrentOperations(t *testing.T) { + t.Parallel() + syncedNodes := createTestNodes(100) fallbackNodes := createTestNodes(100) nh, _ := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityRecent) From adff3fc66b69048d86a0ffa450f75dbddb2572de Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Fri, 22 Sep 2023 15:30:23 +0300 Subject: [PATCH 11/13] fixes after second review --- common/options_test.go | 38 +++++ .../availabilityProvider.go | 48 ++++++ .../availabilityProvider_test.go | 66 ++++++++ observer/holder/nodesHolder.go | 82 +++++---- observer/holder/nodesHolder_test.go | 155 ++++++++++++++---- process/accountProcessor.go | 47 +++--- process/scQueryProcessor.go | 21 +-- 7 files changed, 356 insertions(+), 101 deletions(-) create mode 100644 observer/availabilityCommon/availabilityProvider.go create mode 100644 observer/availabilityCommon/availabilityProvider_test.go diff --git a/common/options_test.go b/common/options_test.go index 6ca12eab..81c459ec 100644 --- a/common/options_test.go +++ b/common/options_test.go @@ -9,6 +9,8 @@ import ( ) func TestBuildUrlWithBlockQueryOptions_ShouldWork(t *testing.T) { + t.Parallel() + builtUrl := BuildUrlWithBlockQueryOptions("/block/by-nonce/15", BlockQueryOptions{}) require.Equal(t, "/block/by-nonce/15", builtUrl) @@ -29,6 +31,8 @@ func TestBuildUrlWithBlockQueryOptions_ShouldWork(t *testing.T) { } func TestBuildUrlWithAccountQueryOptions_ShouldWork(t *testing.T) { + t.Parallel() + builtUrl := BuildUrlWithAccountQueryOptions("/address/erd1alice", AccountQueryOptions{}) require.Equal(t, "/address/erd1alice", builtUrl) @@ -65,6 +69,8 @@ func TestBuildUrlWithAccountQueryOptions_ShouldWork(t *testing.T) { } func TestBuildUrlWithAlteredAccountsQueryOptions(t *testing.T) { + t.Parallel() + resultedUrl := BuildUrlWithAlteredAccountsQueryOptions("path", GetAlteredAccountsForBlockOptions{}) require.Equal(t, "path", resultedUrl) @@ -74,3 +80,35 @@ func TestBuildUrlWithAlteredAccountsQueryOptions(t *testing.T) { // 2C is the ascii hex encoding of (,) require.Equal(t, "path?tokens=token1%2Ctoken2%2Ctoken3", resultedUrl) } + +func TestAccountQueryOptions_AreHistoricalCoordinatesSet(t *testing.T) { + t.Parallel() + + emptyQuery := AccountQueryOptions{} + require.False(t, emptyQuery.AreHistoricalCoordinatesSet()) + + queryWithNonce := AccountQueryOptions{ + BlockNonce: core.OptionalUint64{HasValue: true, Value: 37}, + } + require.True(t, queryWithNonce.AreHistoricalCoordinatesSet()) + + queryWithBlockHash := AccountQueryOptions{ + BlockHash: []byte("hash"), + } + require.True(t, queryWithBlockHash.AreHistoricalCoordinatesSet()) + + queryWithBlockRootHash := AccountQueryOptions{ + BlockRootHash: []byte("rootHash"), + } + require.True(t, queryWithBlockRootHash.AreHistoricalCoordinatesSet()) + + queryWithEpochStart := AccountQueryOptions{ + OnStartOfEpoch: core.OptionalUint32{HasValue: true, Value: 37}, + } + require.True(t, queryWithEpochStart.AreHistoricalCoordinatesSet()) + + queryWithHintEpoch := AccountQueryOptions{ + HintEpoch: core.OptionalUint32{HasValue: false, Value: 37}, + } + require.True(t, queryWithHintEpoch.AreHistoricalCoordinatesSet()) +} diff --git a/observer/availabilityCommon/availabilityProvider.go b/observer/availabilityCommon/availabilityProvider.go new file mode 100644 index 00000000..1d6e41db --- /dev/null +++ b/observer/availabilityCommon/availabilityProvider.go @@ -0,0 +1,48 @@ +package availabilityCommon + +import ( + "github.com/multiversx/mx-chain-proxy-go/common" + "github.com/multiversx/mx-chain-proxy-go/data" +) + +// AvailabilityProvider is a stateless component that aims to group common operations regarding observers' data availability +type AvailabilityProvider struct { +} + +// AvailabilityForAccountQueryOptions returns the availability needed for the provided query options +func (ap *AvailabilityProvider) AvailabilityForAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { + availability := data.AvailabilityRecent + if options.AreHistoricalCoordinatesSet() { + availability = data.AvailabilityAll + } + return availability +} + +// AvailabilityForVmQuery returns the availability needed for the provided query options +func (ap *AvailabilityProvider) AvailabilityForVmQuery(query *data.SCQuery) data.ObserverDataAvailabilityType { + availability := data.AvailabilityRecent + if query.BlockNonce.HasValue || len(query.BlockHash) > 0 { + availability = data.AvailabilityAll + } + return availability +} + +// IsNodeValid returns true if the provided node is valid based on the availability +func (ap *AvailabilityProvider) IsNodeValid(node *data.NodeData, availability data.ObserverDataAvailabilityType) bool { + isInvalidSnapshotlessNode := availability == data.AvailabilityRecent && !node.IsSnapshotless + isInvalidRegularNode := availability == data.AvailabilityAll && node.IsSnapshotless + isInvalidNode := isInvalidSnapshotlessNode || isInvalidRegularNode + return !isInvalidNode +} + +// GetDescriptionForAvailability returns a short description string about the provided availability +func (ap *AvailabilityProvider) GetDescriptionForAvailability(availability data.ObserverDataAvailabilityType) string { + switch availability { + case data.AvailabilityAll: + return "regular nodes" + case data.AvailabilityRecent: + return "snapshotless nodes" + default: + return "N/A" + } +} diff --git a/observer/availabilityCommon/availabilityProvider_test.go b/observer/availabilityCommon/availabilityProvider_test.go new file mode 100644 index 00000000..c1e7f725 --- /dev/null +++ b/observer/availabilityCommon/availabilityProvider_test.go @@ -0,0 +1,66 @@ +package availabilityCommon + +import ( + "testing" + + "github.com/multiversx/mx-chain-core-go/core" + "github.com/multiversx/mx-chain-proxy-go/common" + "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/stretchr/testify/require" +) + +func TestAvailabilityForAccountQueryOptions(t *testing.T) { + ap := &AvailabilityProvider{} + + // Test with historical coordinates set + options := common.AccountQueryOptions{BlockHash: []byte("hash")} + require.Equal(t, data.AvailabilityAll, ap.AvailabilityForAccountQueryOptions(options)) + + // Test without historical coordinates set + options = common.AccountQueryOptions{} + require.Equal(t, data.AvailabilityRecent, ap.AvailabilityForAccountQueryOptions(options)) +} + +func TestAvailabilityForVmQuery(t *testing.T) { + ap := &AvailabilityProvider{} + + // Test with BlockNonce set + query := &data.SCQuery{BlockNonce: core.OptionalUint64{HasValue: true, Value: 37}} + require.Equal(t, data.AvailabilityAll, ap.AvailabilityForVmQuery(query)) + + // Test without BlockNonce set but with BlockHash + query = &data.SCQuery{BlockHash: []byte("hash")} + require.Equal(t, data.AvailabilityAll, ap.AvailabilityForVmQuery(query)) + + // Test without BlockNonce and BlockHash + query = &data.SCQuery{} + require.Equal(t, data.AvailabilityRecent, ap.AvailabilityForVmQuery(query)) +} + +func TestIsNodeValid(t *testing.T) { + ap := &AvailabilityProvider{} + + // Test with AvailabilityRecent and snapshotless node + node := &data.NodeData{IsSnapshotless: true} + require.True(t, ap.IsNodeValid(node, data.AvailabilityRecent)) + + // Test with AvailabilityRecent and regular node + node = &data.NodeData{} + require.False(t, ap.IsNodeValid(node, data.AvailabilityRecent)) + + // Test with AvailabilityAll and regular node + node = &data.NodeData{} + require.True(t, ap.IsNodeValid(node, data.AvailabilityAll)) + + // Test with AvailabilityAll and Snapshotless node + node = &data.NodeData{IsSnapshotless: true} + require.False(t, ap.IsNodeValid(node, data.AvailabilityAll)) +} + +func TestGetDescriptionForAvailability(t *testing.T) { + ap := &AvailabilityProvider{} + + require.Equal(t, "regular nodes", ap.GetDescriptionForAvailability(data.AvailabilityAll)) + require.Equal(t, "snapshotless nodes", ap.GetDescriptionForAvailability(data.AvailabilityRecent)) + require.Equal(t, "N/A", ap.GetDescriptionForAvailability("invalid")) // Invalid value +} diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go index 04443c82..ea28e3d5 100644 --- a/observer/holder/nodesHolder.go +++ b/observer/holder/nodesHolder.go @@ -8,6 +8,7 @@ import ( logger "github.com/multiversx/mx-chain-logger-go" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/availabilityCommon" ) type cacheType string @@ -25,10 +26,11 @@ var ( ) type nodesHolder struct { - mut sync.RWMutex - allNodes map[uint32][]*data.NodeData - cache map[string][]*data.NodeData - availability data.ObserverDataAvailabilityType + mut sync.RWMutex + allNodes map[uint32][]*data.NodeData + cache map[string][]*data.NodeData + availability data.ObserverDataAvailabilityType + availabilityProvider availabilityCommon.AvailabilityProvider } // NewNodesHolder will return a new instance of a nodesHolder @@ -37,9 +39,10 @@ func NewNodesHolder(syncedNodes []*data.NodeData, fallbackNodes []*data.NodeData return nil, errEmptyNodesList } return &nodesHolder{ - allNodes: computeInitialNodeList(syncedNodes, fallbackNodes), - cache: make(map[string][]*data.NodeData), - availability: availability, + allNodes: computeInitialNodeList(syncedNodes, fallbackNodes), + cache: make(map[string][]*data.NodeData), + availability: availability, + availabilityProvider: availabilityCommon.AvailabilityProvider{}, }, nil } @@ -55,9 +58,7 @@ func (nh *nodesHolder) UpdateNodes(nodesWithSyncStatus []*data.NodeData) { nh.allNodes = make(map[uint32][]*data.NodeData) nh.cache = make(map[string][]*data.NodeData) for _, node := range nodesWithSyncStatus { - shouldSkipNode := nh.availability == data.AvailabilityRecent && !node.IsSnapshotless || - nh.availability == data.AvailabilityAll && node.IsSnapshotless - if shouldSkipNode { + if !nh.availabilityProvider.IsNodeValid(node, nh.availability) { continue } nh.allNodes[node.ShardId] = append(nh.allNodes[node.ShardId], node) @@ -112,6 +113,8 @@ func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.Nod // nodes not cached, compute the list and update the cache recomputedList := make([]*data.NodeData, 0) nh.mut.Lock() + defer nh.mut.Unlock() + cachedValues, exists = nh.cache[cacheKey] if exists { return cachedValues @@ -122,7 +125,6 @@ func (nh *nodesHolder) getObservers(cache cacheType, shardID uint32) []*data.Nod } } nh.cache[cacheKey] = recomputedList - nh.mut.Unlock() return recomputedList } @@ -157,20 +159,6 @@ func (nh *nodesHolder) IsInterfaceNil() bool { func (nh *nodesHolder) printNodesInShardsUnprotected() { nodesByType := make(map[uint32]map[cacheType][]*data.NodeData) - // define a function to get the cache type for a node - getCacheType := func(node *data.NodeData) cacheType { - if node.IsFallback { - if node.IsSynced { - return syncedFallbackNodesCache - } - return outOfSyncFallbackNodesCache - } - if node.IsSynced { - return syncedNodesCache - } - return outOfSyncNodesCache - } - // populate nodesByType map for shard, nodes := range nh.allNodes { if nodesByType[shard] == nil { @@ -183,11 +171,7 @@ func (nh *nodesHolder) printNodesInShardsUnprotected() { } } - printHeader := "regular nodes" - if nh.availability == data.AvailabilityRecent { - printHeader = "snapshotless nodes" - } - + printHeader := nh.availabilityProvider.GetDescriptionForAvailability(nh.availability) for shard, nodesByCache := range nodesByType { log.Info(fmt.Sprintf("shard %d %s", shard, printHeader), "synced observers", getNodesListAsString(nodesByCache[syncedNodesCache]), @@ -197,6 +181,19 @@ func (nh *nodesHolder) printNodesInShardsUnprotected() { } } +func getCacheType(node *data.NodeData) cacheType { + if node.IsFallback { + if node.IsSynced { + return syncedFallbackNodesCache + } + return outOfSyncFallbackNodesCache + } + if node.IsSynced { + return syncedNodesCache + } + return outOfSyncNodesCache +} + func getNodesListAsString(nodes []*data.NodeData) string { addressesString := "" for _, node := range nodes { @@ -206,14 +203,33 @@ func getNodesListAsString(nodes []*data.NodeData) string { return strings.TrimSuffix(addressesString, ", ") } +func cloneNodesSlice(input []*data.NodeData) []*data.NodeData { + clonedSlice := make([]*data.NodeData, len(input)) + for idx, node := range input { + clonedSlice[idx] = &data.NodeData{ + ShardId: node.ShardId, + Address: node.Address, + IsFallback: node.IsFallback, + IsSynced: node.IsSynced, + IsSnapshotless: node.IsSnapshotless, + } + } + + return clonedSlice +} + func computeInitialNodeList(regularNodes []*data.NodeData, fallbackNodes []*data.NodeData) map[uint32][]*data.NodeData { + // clone the original maps as not to affect the input + clonedRegularNodes := cloneNodesSlice(regularNodes) + clonedFallbackNodes := cloneNodesSlice(fallbackNodes) + mapToReturn := make(map[uint32][]*data.NodeData) - // in the first step, consider all the nodes to be active - for _, node := range regularNodes { + // since this function is called at constructor level, consider that all the nodes are active + for _, node := range clonedRegularNodes { node.IsSynced = true mapToReturn[node.ShardId] = append(mapToReturn[node.ShardId], node) } - for _, node := range fallbackNodes { + for _, node := range clonedFallbackNodes { node.IsSynced = true mapToReturn[node.ShardId] = append(mapToReturn[node.ShardId], node) } diff --git a/observer/holder/nodesHolder_test.go b/observer/holder/nodesHolder_test.go index 6e296867..807c5710 100644 --- a/observer/holder/nodesHolder_test.go +++ b/observer/holder/nodesHolder_test.go @@ -10,43 +10,138 @@ import ( "github.com/stretchr/testify/require" ) -func TestNodesHolder_ConstructorAndGetters(t *testing.T) { +func TestNewNodesHolder(t *testing.T) { t.Parallel() - nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityAll) - require.Equal(t, errEmptyNodesList, err) - require.Nil(t, nh) + t.Run("empty regular nodes slice - should error", func(t *testing.T) { + t.Parallel() - syncedNodes := createTestNodes(3) - setPropertyToNodes(syncedNodes, "synced", true, 0, 1, 2) + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityAll) + require.Equal(t, errEmptyNodesList, err) + require.Nil(t, nh) + }) - fallbackNodes := createTestNodes(3) - setPropertyToNodes(fallbackNodes, "synced", true, 0, 1, 2) - setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2) + t.Run("empty snapshotless nodes slice - should not error", func(t *testing.T) { + t.Parallel() + + nh, err := NewNodesHolder([]*data.NodeData{}, []*data.NodeData{}, data.AvailabilityRecent) + require.NoError(t, err) + require.NotNil(t, nh) + }) + + t.Run("should work for regular nodes", func(t *testing.T) { + t.Parallel() + + nh, err := NewNodesHolder([]*data.NodeData{{Address: "addr"}}, []*data.NodeData{}, data.AvailabilityAll) + require.NoError(t, err) + require.NotNil(t, nh) + }) + + t.Run("should work for snapshotless nodes", func(t *testing.T) { + t.Parallel() + + nh, err := NewNodesHolder([]*data.NodeData{{Address: "addr"}}, []*data.NodeData{}, data.AvailabilityRecent) + require.NoError(t, err) + require.NotNil(t, nh) + }) +} - nh, err = NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) +func TestNodesHolder_Getters(t *testing.T) { + t.Parallel() + + shardIDs := []uint32{0, 1, core.MetachainShardId} + syncedNodes := createTestNodes(6) + fallbackNodes := createTestNodes(6) + setPropertyToNodes(fallbackNodes, "fallback", true, 0, 1, 2, 3, 4, 5) + + nh, err := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityAll) require.NoError(t, err) require.NotNil(t, nh) - require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetSyncedNodes(0)) - require.Equal(t, []*data.NodeData{syncedNodes[1]}, nh.GetSyncedNodes(1)) - require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetSyncedNodes(core.MetachainShardId)) + t.Run("test getters before updating the nodes", func(t *testing.T) { + for _, shardID := range shardIDs { + indices := getIndicesOfNodesInShard(syncedNodes, shardID) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(shardID), syncedNodes, indices) + } + for _, shardID := range shardIDs { + require.Empty(t, nh.GetOutOfSyncNodes(shardID)) + } + for _, shardID := range shardIDs { + indices := getIndicesOfNodesInShard(fallbackNodes, shardID) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(shardID), fallbackNodes, indices) + } + for _, shardID := range shardIDs { + require.Empty(t, nh.GetOutOfSyncFallbackNodes(shardID)) + } + }) + + t.Run("test getters after updating the nodes", func(t *testing.T) { + setPropertyToNodes(syncedNodes, "synced", true, 3, 4, 5) + setPropertyToNodes(syncedNodes, "synced", false, 0, 1, 2) + + setPropertyToNodes(fallbackNodes, "synced", true, 0, 2, 3, 4, 5) + setPropertyToNodes(fallbackNodes, "synced", false, 1) + nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) + + // check synced regular nodes + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(0), syncedNodes, []int{3}) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(1), syncedNodes, []int{4}) + compareNodesBasedOnIndices(t, nh.GetSyncedNodes(core.MetachainShardId), syncedNodes, []int{5}) + + // check out of sync regular nodes + compareNodesBasedOnIndices(t, nh.GetOutOfSyncNodes(0), syncedNodes, []int{0}) + compareNodesBasedOnIndices(t, nh.GetOutOfSyncNodes(1), syncedNodes, []int{1}) + compareNodesBasedOnIndices(t, nh.GetOutOfSyncNodes(core.MetachainShardId), syncedNodes, []int{2}) + + // check synced fallback nodes + compareNodesBasedOnIndices(t, nh.GetSyncedFallbackNodes(0), syncedNodes, []int{0, 3}) + compareNodesBasedOnIndices(t, nh.GetSyncedFallbackNodes(1), syncedNodes, []int{4}) + compareNodesBasedOnIndices(t, nh.GetSyncedFallbackNodes(core.MetachainShardId), syncedNodes, []int{2, 5}) + + // check out of sync fallback nodes + require.Empty(t, nh.GetOutOfSyncFallbackNodes(0)) + compareNodesBasedOnIndices(t, nh.GetOutOfSyncFallbackNodes(1), syncedNodes, []int{1}) + require.Empty(t, nh.GetOutOfSyncFallbackNodes(core.MetachainShardId)) + }) +} - require.Equal(t, []*data.NodeData{fallbackNodes[0]}, nh.GetSyncedFallbackNodes(0)) - require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetSyncedFallbackNodes(1)) - require.Equal(t, []*data.NodeData{fallbackNodes[2]}, nh.GetSyncedFallbackNodes(core.MetachainShardId)) +func compareNodesBasedOnIndices(t *testing.T, firstSlice []*data.NodeData, secondSlice []*data.NodeData, indices []int) { + if len(firstSlice) > len(indices) { + t.Fail() + } - setPropertyToNodes(syncedNodes, "synced", false, 0, 2) - setPropertyToNodes(fallbackNodes, "synced", false, 1) - nh.UpdateNodes(append(syncedNodes, fallbackNodes...)) - require.Equal(t, []*data.NodeData{syncedNodes[0]}, nh.GetOutOfSyncNodes(0)) - require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncNodes(1)) - require.Equal(t, []*data.NodeData{syncedNodes[2]}, nh.GetOutOfSyncNodes(core.MetachainShardId)) - - require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(0)) - require.Equal(t, []*data.NodeData{fallbackNodes[1]}, nh.GetOutOfSyncFallbackNodes(1)) - require.Equal(t, []*data.NodeData{}, nh.GetOutOfSyncFallbackNodes(core.MetachainShardId)) - require.Equal(t, 6, nh.Count()) + if len(firstSlice) == 0 { + t.Fail() + } + + for i, node := range firstSlice { + indexInSecondSlice := indices[i] + if indexInSecondSlice > len(secondSlice) { + t.Fail() + } + require.Equal(t, node.Address, secondSlice[indexInSecondSlice].Address) + } +} + +func getIndicesOfNodesInShard(nodes []*data.NodeData, shardID uint32) []int { + intSlice := make([]int, 0) + for idx, node := range nodes { + if node.ShardId != shardID { + continue + } + + intSlice = append(intSlice, idx) + } + + return intSlice +} + +func TestNodesHolder_Count(t *testing.T) { + t.Parallel() + + syncedNodes := createTestNodes(3) + nh, _ := NewNodesHolder(syncedNodes, syncedNodes, data.AvailabilityAll) + require.Equal(t, 2*len(syncedNodes), nh.Count()) } func TestNodesHolder_IsInterfaceNil(t *testing.T) { @@ -153,7 +248,7 @@ func TestNodesHolder_ConcurrentOperations(t *testing.T) { fallbackNodes := createTestNodes(100) nh, _ := NewNodesHolder(syncedNodes, fallbackNodes, data.AvailabilityRecent) - numOperations := 100_000 + numOperations := 1_000 wg := sync.WaitGroup{} wg.Add(numOperations) for i := 0; i < numOperations; i++ { @@ -173,7 +268,7 @@ func TestNodesHolder_ConcurrentOperations(t *testing.T) { _ = nh.GetOutOfSyncNodes(uint32(index % 3)) } wg.Done() - }(i) + }(i % 6) } wg.Wait() } @@ -181,8 +276,6 @@ func TestNodesHolder_ConcurrentOperations(t *testing.T) { func createTestNodes(numNodes int) []*data.NodeData { getShard := func(index int) uint32 { switch index % 3 { - case 0: - return 0 case 1: return 1 case 2: diff --git a/process/accountProcessor.go b/process/accountProcessor.go index a916af85..5a1161a1 100644 --- a/process/accountProcessor.go +++ b/process/accountProcessor.go @@ -9,6 +9,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-proxy-go/common" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/availabilityCommon" ) // addressPath defines the address path at which the nodes answer @@ -16,9 +17,10 @@ const addressPath = "/address/" // AccountProcessor is able to process account requests type AccountProcessor struct { - connector ExternalStorageConnector - proc Processor - pubKeyConverter core.PubkeyConverter + connector ExternalStorageConnector + proc Processor + pubKeyConverter core.PubkeyConverter + availabilityProvider availabilityCommon.AvailabilityProvider } // NewAccountProcessor creates a new instance of AccountProcessor @@ -34,9 +36,10 @@ func NewAccountProcessor(proc Processor, pubKeyConverter core.PubkeyConverter, c } return &AccountProcessor{ - proc: proc, - pubKeyConverter: pubKeyConverter, - connector: connector, + proc: proc, + pubKeyConverter: pubKeyConverter, + connector: connector, + availabilityProvider: availabilityCommon.AvailabilityProvider{}, }, nil } @@ -50,9 +53,9 @@ func (ap *AccountProcessor) GetShardIDForAddress(address string) (uint32, error) return ap.proc.ComputeShardId(addressBytes) } -// GetAccount resolves the request by sending the request to the right observer and replies back the answer +// GetAccount resolves the request by sending the request to the right observer and returns the response func (ap *AccountProcessor) GetAccount(address string, options common.AccountQueryOptions) (*data.AccountModel, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -76,7 +79,7 @@ func (ap *AccountProcessor) GetAccount(address string, options common.AccountQue // GetValueForKey returns the value for the given address and key func (ap *AccountProcessor) GetValueForKey(address string, key string, options common.AccountQueryOptions) (string, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return "", err @@ -108,7 +111,7 @@ func (ap *AccountProcessor) GetValueForKey(address string, key string, options c // GetESDTTokenData returns the token data for a token with the given name func (ap *AccountProcessor) GetESDTTokenData(address string, key string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -141,7 +144,7 @@ func (ap *AccountProcessor) GetESDTTokenData(address string, key string, options // GetESDTsWithRole returns the token identifiers where the given address has the given role assigned func (ap *AccountProcessor) GetESDTsWithRole(address string, role string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err @@ -174,7 +177,7 @@ func (ap *AccountProcessor) GetESDTsWithRole(address string, role string, option // GetESDTsRoles returns all the tokens and their roles for a given address func (ap *AccountProcessor) GetESDTsRoles(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err @@ -208,7 +211,7 @@ func (ap *AccountProcessor) GetESDTsRoles(address string, options common.Account func (ap *AccountProcessor) GetNFTTokenIDsRegisteredByAddress(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { //TODO: refactor the entire proxy so endpoints like this which simply forward the response will use a common // component, as described in task EN-9857. - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.proc.GetObservers(core.MetachainShardId, availability) if err != nil { return nil, err @@ -240,7 +243,7 @@ func (ap *AccountProcessor) GetNFTTokenIDsRegisteredByAddress(address string, op // GetESDTNftTokenData returns the nft token data for a token with the given identifier and nonce func (ap *AccountProcessor) GetESDTNftTokenData(address string, key string, nonce uint64, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -274,7 +277,7 @@ func (ap *AccountProcessor) GetESDTNftTokenData(address string, key string, nonc // GetAllESDTTokens returns all the tokens for a given address func (ap *AccountProcessor) GetAllESDTTokens(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -306,7 +309,7 @@ func (ap *AccountProcessor) GetAllESDTTokens(address string, options common.Acco // GetKeyValuePairs returns all the key-value pairs for a given address func (ap *AccountProcessor) GetKeyValuePairs(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -338,7 +341,7 @@ func (ap *AccountProcessor) GetKeyValuePairs(address string, options common.Acco // GetGuardianData returns the guardian data for the given address func (ap *AccountProcessor) GetGuardianData(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -379,7 +382,7 @@ func (ap *AccountProcessor) GetTransactions(address string) ([]data.DatabaseTran // GetCodeHash returns the code hash for a given address func (ap *AccountProcessor) GetCodeHash(address string, options common.AccountQueryOptions) (*data.GenericAPIResponse, error) { - availability := getAvailabilityBasedOnAccountQueryOptions(options) + availability := ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) observers, err := ap.getObserversForAddress(address, availability) if err != nil { return nil, err @@ -464,10 +467,6 @@ func (ap *AccountProcessor) IsDataTrieMigrated(address string, options common.Ac return nil, ErrSendingRequest } -func getAvailabilityBasedOnAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { - availability := data.AvailabilityRecent - if options.AreHistoricalCoordinatesSet() { - availability = data.AvailabilityAll - } - return availability +func (ap *AccountProcessor) getAvailabilityBasedOnAccountQueryOptions(options common.AccountQueryOptions) data.ObserverDataAvailabilityType { + return ap.availabilityProvider.AvailabilityForAccountQueryOptions(options) } diff --git a/process/scQueryProcessor.go b/process/scQueryProcessor.go index 24508ac5..0ba01ead 100644 --- a/process/scQueryProcessor.go +++ b/process/scQueryProcessor.go @@ -10,6 +10,7 @@ import ( "github.com/multiversx/mx-chain-core-go/core/check" "github.com/multiversx/mx-chain-core-go/data/vm" "github.com/multiversx/mx-chain-proxy-go/data" + "github.com/multiversx/mx-chain-proxy-go/observer/availabilityCommon" ) // scQueryServicePath defines the get values path at which the nodes answer @@ -19,8 +20,9 @@ const blockHash = "blockHash" // SCQueryProcessor is able to process smart contract queries type SCQueryProcessor struct { - proc Processor - pubKeyConverter core.PubkeyConverter + proc Processor + pubKeyConverter core.PubkeyConverter + availabilityProvider availabilityCommon.AvailabilityProvider } // NewSCQueryProcessor creates a new instance of SCQueryProcessor @@ -33,8 +35,9 @@ func NewSCQueryProcessor(proc Processor, pubKeyConverter core.PubkeyConverter) ( } return &SCQueryProcessor{ - proc: proc, - pubKeyConverter: pubKeyConverter, + proc: proc, + pubKeyConverter: pubKeyConverter, + availabilityProvider: availabilityCommon.AvailabilityProvider{}, }, nil } @@ -50,7 +53,7 @@ func (scQueryProcessor *SCQueryProcessor) ExecuteQuery(query *data.SCQuery) (*vm return nil, data.BlockInfo{}, err } - availability := getAvailabilityBasedOnVmQueryOptions(query) + availability := scQueryProcessor.availabilityProvider.AvailabilityForVmQuery(query) observers, err := scQueryProcessor.proc.GetObservers(shardID, availability) if err != nil { return nil, data.BlockInfo{}, err @@ -118,11 +121,3 @@ func (scQueryProcessor *SCQueryProcessor) createRequestFromQuery(query *data.SCQ func (scQueryProcessor *SCQueryProcessor) IsInterfaceNil() bool { return scQueryProcessor == nil } - -func getAvailabilityBasedOnVmQueryOptions(query *data.SCQuery) data.ObserverDataAvailabilityType { - availability := data.AvailabilityRecent - if query.BlockNonce.HasValue || len(query.BlockHash) > 0 { - availability = data.AvailabilityAll - } - return availability -} From 62c03fafd42654a3dcf01d6c089bc62cd5e6bba8 Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Fri, 22 Sep 2023 15:32:33 +0300 Subject: [PATCH 12/13] fix test --- common/options_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/options_test.go b/common/options_test.go index 81c459ec..c70524e4 100644 --- a/common/options_test.go +++ b/common/options_test.go @@ -108,7 +108,7 @@ func TestAccountQueryOptions_AreHistoricalCoordinatesSet(t *testing.T) { require.True(t, queryWithEpochStart.AreHistoricalCoordinatesSet()) queryWithHintEpoch := AccountQueryOptions{ - HintEpoch: core.OptionalUint32{HasValue: false, Value: 37}, + HintEpoch: core.OptionalUint32{HasValue: true, Value: 37}, } require.True(t, queryWithHintEpoch.AreHistoricalCoordinatesSet()) } From fcd67a47a9469c65342ef2f01752406095cb4482 Mon Sep 17 00:00:00 2001 From: Bogdan Rosianu Date: Mon, 25 Sep 2023 10:52:24 +0300 Subject: [PATCH 13/13] change clone function --- observer/holder/nodesHolder.go | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/observer/holder/nodesHolder.go b/observer/holder/nodesHolder.go index ea28e3d5..41d418b8 100644 --- a/observer/holder/nodesHolder.go +++ b/observer/holder/nodesHolder.go @@ -206,13 +206,8 @@ func getNodesListAsString(nodes []*data.NodeData) string { func cloneNodesSlice(input []*data.NodeData) []*data.NodeData { clonedSlice := make([]*data.NodeData, len(input)) for idx, node := range input { - clonedSlice[idx] = &data.NodeData{ - ShardId: node.ShardId, - Address: node.Address, - IsFallback: node.IsFallback, - IsSynced: node.IsSynced, - IsSnapshotless: node.IsSnapshotless, - } + clonedNodeData := *node + clonedSlice[idx] = &clonedNodeData } return clonedSlice