New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Eliminate addressBook from Network components #13010
base: develop
Are you sure you want to change the base?
Conversation
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
This reverts commit f23c2ac. Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Node: HAPI Test (Restart) Results2 tests 2 ✅ 5m 36s ⏱️ Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
Node: HAPI Test (Node Death Reconnect) Results1 tests 1 ✅ 24s ⏱️ For more details on these parsing errors, see this check. Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
Node: HAPI Test (Token) Results228 tests 227 ✅ 17m 37s ⏱️ Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
Node: HAPI Test (Misc) Results457 tests 447 ✅ 35m 42s ⏱️ For more details on these parsing errors, see this check. Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
Node: HAPI Test (Crypto) Results335 tests 335 ✅ 36m 23s ⏱️ Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
Node: HAPI Test (Time Consuming) Results21 tests 21 ✅ 54m 18s ⏱️ Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
Node: Unit Test Results 2 267 files ±0 2 267 suites ±0 3h 27m 7s ⏱️ + 25m 0s Results for commit dd516d0. ± Comparison against base commit b1ad671. This pull request removes 4002 and adds 3765 tests. Note that renamed tests count towards both.
♻️ This comment has been updated with latest results. |
Node: HAPI Test (Smart Contract) Results585 tests 585 ✅ 1h 5m 10s ⏱️ Results for commit dd516d0. ♻️ This comment has been updated with latest results. |
this.peerNodes = | ||
Objects.requireNonNull(peers.stream().map(PeerInfo::nodeId).toList(), "peers must not be null"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Objects.requireNonNull()
is not necessary. If peers
is null then peers.stream()
will throw an exception first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
change not made in latest commit
* @return the index of the node in the peer list | ||
*/ | ||
private int getIndexOfNodeId(@NonNull final NodeId nodeId) { | ||
final int index = peerNodes.indexOf(nodeId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of iterating the list to find the index, we should instead create a Map<NodeId, Integer>
for O(1)
lookup time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O(1) here would be nice but i still have to keep a list of peers for the neighbors. To use a map, the values of the map would be a set of peers which must still be converted to a list in O(n). No good trade-off here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
O(1)
is non-optional for stuff like this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we can get O(1)
with a much similar approach. Is there any reason we can't just compare node IDs directly? The index is really just a proxy for node IDs. All we really want to do is figure out if the other node comes first in the address book, and if you have a lower node ID you will always come first in the address book.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why have the files sigCerts.data
and agrCerts.data
been added in this PR?
@@ -76,19 +73,16 @@ public List<NodeId> getNeighbors() { | |||
*/ | |||
@Override | |||
public List<NodeId> getNeighbors(final Predicate<NodeId> filter) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method used to return neighbors, but now it's returning all peers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the peers are all neighbors, but not necessarily adjacent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this. If all peers are neighbors, why do we have 2 terms here? To me, neighbor
implies adjacent
* @param nodeId the node ID | ||
* @return the index of the node in the peer list | ||
*/ | ||
private int getIndexOfNodeId(@NonNull final NodeId nodeId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Won't this method have an off by 1 error for every node with index > self index? You're using position in peerNodes
, but self is missing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for every node with index > self index, we need the index returned by this method to be off by 1 (to match their position similar to the original addressBook).
There's an edge case for when being off by 1 can lead to this method returning an index number > peer list upper bound, but we deal with that correctly in the isAdjacent check so it's ok.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this either. The previous code in this class did the following, to get the index of the other node in the address book. This computation includes self id in the indices
addressBook.getIndexOfNodeId(nodeId);
But the new code instead defines the index of the other node via its position in the peer list, which doesn't contain self id. So for every node with index greater than this self id, the returned node index will be 1 less.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I think I understand why this works.
If the node ID of the neighbor is greater than ours, then this method returns a value that is one less than its actual index. If the node ID of the neighbor is equal to our node ID plus one, getIndexOfNodeId() increments the number so that it doesn't actually equal our index.
This class still returns the correct results because we only care if the other node index is greater or less than our own index, so it doesn't matter that it's off by 1 as long as it is greater than our index.
Although this technically yields the correct result, I feel like this is one of those times where the code is "accidentally returning the right results". At the very least, the code here does not return values consistent with the javadocs. Better to refactor this code I think. I can imagine that the reason why/how this works may not be apparent to a developer who looks at this in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we modifying this class right now? We discussed as a group a few weeks back, and as I recall the decision was to make the network fully connected and not to try and modify the partial connectivity code until we had the bandwidth to test it properly.
The danger of modifying this code now is that we don't actually have any integration tests where the network is not fully connected, meaning we may be introducing bugs we don't catch for a long time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All that this change does is to get rid of the addressBook within the network code so we're operating on peers, making tests so much easier. AB is limiting to the effectiveness of writing tests, and also just a completely orthogonal concept to the network code. I have deliberately made some choices to closely mimic the old functionality (until we decide to revisit fully connected vs otherwise), like doing an index lookup via iteration vs map (for ex).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like doing an index lookup via iteration vs map
The existing code uses AddressBook::getIndexOfNodeId
to determine node index, which has a map lookup under the hood. Where are you seeing index lookup via iteration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StaticTopology.getNeighbors
is one.
Anyway, doing away with node indices altogether has made this a lot easier as we no longer have to match the old behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To fully do away with node indeces, we have to drop use of RandomGraph
and officially only support fully connected networks until we introduce a new implementation of connecting to less peers than the number of nodes in the address book.
final List<PeerInfo> peers = Utilities.createPeerInfoList(addressBook, selfId); | ||
|
||
topology = | ||
new StaticTopology(random, peers, addressBook.getIndexOfNodeId(selfId), basicConfig.numConnections()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this index is an AB concept, so we should get rid of it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@lpetrovic05 Is the scope of this PR to simply remove the address book references or also drop our support for non-fully connected network topologies? The RandomGraph
class uses hard-coded arrays of size addressbook.getSize()
. The translation from id
to index
is to support the use of RandomGraph
to decide who is a neighbor when there are more nodes in the network than the degree of edges allowed per node.
If we drop the use of RandomGraph
, then we can get rid of the translation from id
to index
. But if we keep RandomGraph
, then we still need to translate from id
to index
. We can do it without the address book, using the List<PeerInfo>
, but we'll have to reconstruct the Map<id, index>
, and be sensitive to where the selfId
falls into the sequence.
It is also important to note that RandomGraph
is returned and used in the FallenBehindManager
where it gets the neighbors of the node based off of the selfId's mapping to index in the address book. To truly drop RandomGraph, we'd need to supply a replacement in other locations where it is used.
I need the peers to have certificates (though the cert doesn't have to be valid in this case). The certificate generator knows to look for those resource files, so it was easier to just give it the files. The alternative is to get the peers and add them mock certificates which is kind of hacky. |
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
Signed-off-by: Kore Aguda <kore@swirldslabs.com>
@NonNull | ||
private Map<NodeId, Long> map(@NonNull final List<PeerInfo> peers) { | ||
for (final PeerInfo peer : peers) { | ||
peerNodeToIdMap.put(peer.nodeId(), peer.nodeId().id()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This definition of a map is not meaningful. If you have the key, then you can call .id()
and get the value without using the map.
I think your intent may be to create a new index ordering based on PeerInfo and map to indexes for use in compatibility with RandomGraph?
final int selfIndex = addressBook.getIndexOfNodeId(selfId); | ||
final int nodeIndex = addressBook.getIndexOfNodeId(nodeId); | ||
return connectionGraph.isAdjacent(selfIndex, nodeIndex); | ||
return peerNodeToIdMap.containsKey(nodeId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've dropped the isAdjacent
check with connectionGraph. If the PR is just to remove the address book dependency then you still need to query the index from the map you construct once it has the right value in the key-value pair.
|
||
/** | ||
* A bidirectional topology that never changes. | ||
*/ | ||
public class StaticTopology implements NetworkTopology { | ||
private static final long SEED = 0; | ||
|
||
private final NodeId selfId; | ||
/** nodes are mapped so lookups are efficient. **/ | ||
private Map<NodeId, Long> peerNodeToIdMap = new HashMap<>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name of the variable is more appropriately named as nodeIdToIndexMap
assuming the intent is to replace the address book lookup for index.
* @param selfId the ID of this node | ||
* @param numberOfNeighbors the number of neighbors each node should have | ||
*/ | ||
public StaticTopology( | ||
@NonNull final Random random, | ||
@NonNull final AddressBook addressBook, | ||
@NonNull final List<PeerInfo> peers, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there an invariant on this list that the PeerInfo is in monotonically ascending order with respect to NodeId?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes there is an invariant, but it's that PeerInfo are ordered as they appear in the original addressBook. I understand they should be monotonically ascending as defined in the AB, but I don't know if that's mandated anywhere.
.filter(filter) | ||
.toList(); | ||
public Set<NodeId> getNeighbors() { | ||
return peerNodeToIdMap.keySet(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the previous implementation this method called the predicate argument method. That method has been deleted. In the implementation of that deleted method, the definition of Neighbor was scoped to the connectIonGraph
which is a RandomGraph
that also models the number of allowed degrees or edges between nodes. If there are 100 nodes and each node has only 10 degrees in the graph then each node has 10 neighbors. The old implementation would provide the 10 neighbors a node is allowed to talk to. This implementation returns all nodes in the List<PeerInfo>
as being allowed to talk to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Discussed with @edward-swirldslabs. The StaticTopology
implementation, given nodes ranging from 1-100, returned what's equivalent to the peersList
(i.e. the list of peers) all the time. There's a specific test case for this. Agreed it did that based on what's returned by connectionGraph.getNeighbors()
but this PR trivially returned the peers list, which is equivalent. It may be helpful to have a discussion on why that may not be right.
I understand there's been some historic discussions around what the future of this class should look like, but the goal of this PR is to introduce an equivalent logic that does not use the AB.
closes #12984