Skip to content
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

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from

Conversation

kfa-aguda
Copy link
Contributor

closes #12984

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>
Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Restart) Results

2 tests   2 ✅  5m 36s ⏱️
2 suites  0 💤
2 files    0 ❌

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Node Death Reconnect) Results

1 tests   1 ✅  24s ⏱️
1 suites  0 💤
2 files    0 ❌
1 errors

For more details on these parsing errors, see this check.

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Token) Results

228 tests   227 ✅  17m 37s ⏱️
 16 suites    1 💤
 16 files      0 ❌

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Misc) Results

457 tests   447 ✅  35m 42s ⏱️
 76 suites   10 💤
 77 files      0 ❌
  1 errors

For more details on these parsing errors, see this check.

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Crypto) Results

335 tests   335 ✅  36m 23s ⏱️
 25 suites    0 💤
 25 files      0 ❌

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Time Consuming) Results

21 tests   21 ✅  54m 18s ⏱️
 3 suites   0 💤
 3 files     0 ❌

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: Unit Test Results

  2 267 files  ±0    2 267 suites  ±0   3h 27m 7s ⏱️ + 25m 0s
112 332 tests ±0  112 265 ✅ ±0  67 💤 ±0  0 ❌ ±0 
120 795 runs  ±0  120 728 ✅ ±0  67 💤 ±0  0 ❌ ±0 

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.

  
             IssuerDN: CN=s-aaaa
            SubjectDN: CN=s-aaaa
           Final Date: Fri Jan 01 00:00:00 UTC 2100
           Public Key: RSA Public Key [2e:28:bc:1e:d3:83:25:92:8e:cb:98:b1:b6:84:06:9c:d5:d8:14:d5],[56:66:d1:a4]
           Start Date: Sat Jan 01 00:00:00 UTC 2000
         SerialNumber: 12482092706667292405
        modulus: c1a0ff5d2372b53d12d12bb87dd03f5e…
        modulus: c1a0ff5d2372b53d12d12bb87dd03f5…
…
com.hedera.node.app.grpc.impl.netty.GrpcServiceBuilderTest ‑ [4] 

com.hedera.node.app.grpc.impl.netty.GrpcServiceBuilderTest ‑ [6] 

com.hedera.node.app.grpc.impl.netty.GrpcServiceBuilderTest ‑ [7]   
  
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [10] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@2cb0b062
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [11] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@51e97a97
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [12] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@c72d914e
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [13] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@26d8436a
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [14] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@81725f31
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [15] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@b0ed13b7
com.hedera.node.app.service.mono.state.codec.VirtualKeySerdesAdapterTest ‑ [16] com.hedera.node.app.service.mono.state.codec.VirtualBlobKey@a00f2fc7
…

♻️ This comment has been updated with latest results.

Copy link

github-actions bot commented Apr 25, 2024

Node: HAPI Test (Smart Contract) Results

585 tests   585 ✅  1h 5m 10s ⏱️
 62 suites    0 💤
 62 files      0 ❌

Results for commit dd516d0.

♻️ This comment has been updated with latest results.

@kfa-aguda kfa-aguda changed the title feat: Eliminate addressBook from Network component feat: Eliminate addressBook from Network components Apr 25, 2024
@kfa-aguda kfa-aguda marked this pull request as ready for review April 26, 2024 01:02
@kfa-aguda kfa-aguda requested review from a team as code owners April 26, 2024 01:02
@kfa-aguda kfa-aguda self-assigned this Apr 26, 2024
Comment on lines 57 to 58
this.peerNodes =
Objects.requireNonNull(peers.stream().map(PeerInfo::nodeId).toList(), "peers must not be null");
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point.

Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

@alittley alittley left a 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) {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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) {
Copy link
Contributor

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.

Copy link
Contributor Author

@kfa-aguda kfa-aguda Apr 26, 2024

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Contributor Author

@kfa-aguda kfa-aguda Apr 26, 2024

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).

Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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());
Copy link
Member

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

Copy link
Contributor

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.

@kfa-aguda
Copy link
Contributor Author

Why have the files sigCerts.data and agrCerts.data been added in this PR?

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());
Copy link
Contributor

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);
Copy link
Contributor

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<>();
Copy link
Contributor

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,
Copy link
Contributor

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?

Copy link
Contributor Author

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();
Copy link
Contributor

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.

Copy link
Contributor Author

@kfa-aguda kfa-aguda May 3, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Eliminate addressBook from Network component
5 participants