-
Notifications
You must be signed in to change notification settings - Fork 3
SOON #18
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
base: main
Are you sure you want to change the base?
SOON #18
Changes from all commits
d8b4fb6
1dc4258
f565a51
f4ad9c4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /** | ||
| * Copyright Quadrivium LLC | ||
| * All Rights Reserved | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <chrono> | ||
| #include <optional> | ||
|
|
||
| #include <libp2p/basic/garbage_collectable.hpp> | ||
| #include <libp2p/peer/peer_id.hpp> | ||
|
|
||
| namespace libp2p::peer { | ||
|
|
||
| /** | ||
| * @brief Repository to store Round Trip Time (RTT) for peers. | ||
| * It calculates smoothed RTT using EWMA. | ||
| */ | ||
| class RttRepository : public basic::GarbageCollectable { | ||
| public: | ||
| virtual ~RttRepository() = default; | ||
|
|
||
| /** | ||
| * @brief Update RTT for a peer. | ||
| * @param p PeerId | ||
| * @param rtt Measured RTT | ||
| */ | ||
| virtual void updateRtt(const PeerId &p, std::chrono::microseconds rtt) = 0; | ||
|
|
||
| /** | ||
| * @brief Get smoothed RTT for a peer. | ||
| * @param p PeerId | ||
| * @return Smoothed RTT if available, std::nullopt otherwise. | ||
| */ | ||
| virtual std::optional<std::chrono::microseconds> getRtt( | ||
| const PeerId &p) const = 0; | ||
|
|
||
| /** | ||
| * @brief Remove RTT information for a peer. | ||
| * @param p PeerId | ||
| */ | ||
| virtual void clear(const PeerId &p) = 0; | ||
| }; | ||
|
|
||
| } // namespace libp2p::peer |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| * Copyright Quadrivium LLC | ||
| * All Rights Reserved | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <libp2p/peer/rtt_repository.hpp> | ||
|
|
||
| #include <unordered_map> | ||
|
|
||
| namespace libp2p::peer { | ||
|
|
||
| class InmemRttRepository : public RttRepository { | ||
| public: | ||
| void updateRtt(const PeerId &p, std::chrono::microseconds rtt) override; | ||
|
|
||
| std::optional<std::chrono::microseconds> getRtt( | ||
| const PeerId &p) const override; | ||
|
|
||
| void clear(const PeerId &p) override; | ||
|
|
||
| void collectGarbage() override; | ||
|
|
||
| private: | ||
| struct PeerRtt { | ||
| std::chrono::microseconds value; | ||
| std::chrono::steady_clock::time_point last_updated; | ||
| }; | ||
|
|
||
| std::unordered_map<PeerId, PeerRtt> rtt_map_; | ||
| }; | ||
|
|
||
| } // namespace libp2p::peer |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -192,6 +192,11 @@ namespace libp2p::protocol::gossip { | |||||
| size_t opportunistic_graft_ticks = 60; | ||||||
| size_t opportunistic_graft_peers = 2; | ||||||
|
|
||||||
| /// Number of peers from the mesh to which we propagate messages. | ||||||
| /// Peers are selected by the smallest RTT. | ||||||
| /// If -1, propagate to all peers in the mesh. | ||||||
| int soon_delta = -1; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| ScoreConfig score; | ||||||
| }; | ||||||
| } // namespace libp2p::protocol::gossip | ||||||
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -23,13 +23,16 @@ namespace libp2p::peer { | |||||||
| PeerRepository::PeerRepository( | ||||||||
| std::shared_ptr<AddressRepository> addr_repo, | ||||||||
| std::shared_ptr<KeyRepository> key_repo, | ||||||||
| std::shared_ptr<ProtocolRepository> protocol_repo) | ||||||||
| std::shared_ptr<ProtocolRepository> protocol_repo, | ||||||||
| std::shared_ptr<RttRepository> rtt_repo) | ||||||||
| : addr_(std::move(addr_repo)), | ||||||||
| key_(std::move(key_repo)), | ||||||||
| proto_(std::move(protocol_repo)) { | ||||||||
| proto_(std::move(protocol_repo)), | ||||||||
| rtt_(std::move(rtt_repo)) { | ||||||||
| BOOST_ASSERT(addr_ != nullptr); | ||||||||
| BOOST_ASSERT(key_ != nullptr); | ||||||||
| BOOST_ASSERT(proto_ != nullptr); | ||||||||
| BOOST_ASSERT(rtt_ != nullptr); | ||||||||
| } | ||||||||
|
|
||||||||
| AddressRepository &PeerRepository::getAddressRepository() { | ||||||||
|
|
@@ -44,11 +47,17 @@ namespace libp2p::peer { | |||||||
| return *proto_; | ||||||||
| } | ||||||||
|
|
||||||||
| RttRepository &PeerRepository::getRttRepository() { | ||||||||
| return *rtt_; | ||||||||
| } | ||||||||
|
|
||||||||
| std::unordered_set<PeerId> PeerRepository::getPeers() const { | ||||||||
| std::unordered_set<PeerId> peers; | ||||||||
| merge_sets<PeerId>(peers, addr_->getPeers()); | ||||||||
| merge_sets<PeerId>(peers, key_->getPeers()); | ||||||||
| merge_sets<PeerId>(peers, proto_->getPeers()); | ||||||||
| // RttRepository doesn't have getPeers() yet, but it's fine. | ||||||||
| // Usually peers in RttRepository should be in others too. | ||||||||
|
Comment on lines
+59
to
+60
|
||||||||
| // RttRepository doesn't have getPeers() yet, but it's fine. | |
| // Usually peers in RttRepository should be in others too. | |
| // TODO: extend RttRepository with getPeers() and merge its peers here as well. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| # | ||
| # Copyright Quadrivium LLC | ||
| # All Rights Reserved | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| # | ||
|
|
||
| libp2p_add_library(p2p_inmem_rtt_repository | ||
| inmem_rtt_repository.cpp | ||
| ) | ||
| target_link_libraries(p2p_inmem_rtt_repository | ||
| p2p_peer_id | ||
| ) | ||
|
|
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,53 @@ | ||||||||||||||||
| /** | ||||||||||||||||
| * Copyright Quadrivium LLC | ||||||||||||||||
| * All Rights Reserved | ||||||||||||||||
| * SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||
| */ | ||||||||||||||||
|
|
||||||||||||||||
| #include <libp2p/peer/rtt_repository/inmem_rtt_repository.hpp> | ||||||||||||||||
|
|
||||||||||||||||
| namespace libp2p::peer { | ||||||||||||||||
|
|
||||||||||||||||
| void InmemRttRepository::updateRtt(const PeerId &p, | ||||||||||||||||
| std::chrono::microseconds rtt) { | ||||||||||||||||
| auto now = std::chrono::steady_clock::now(); | ||||||||||||||||
| auto it = rtt_map_.find(p); | ||||||||||||||||
| if (it == rtt_map_.end()) { | ||||||||||||||||
| rtt_map_.emplace(p, PeerRtt{rtt, now}); | ||||||||||||||||
| } else { | ||||||||||||||||
| // EWMA: SRTT = (1 - alpha) * SRTT + alpha * RTT | ||||||||||||||||
| // alpha = 0.125 (1/8) is standard for TCP | ||||||||||||||||
| // SRTT = (7 * SRTT + RTT) / 8 | ||||||||||||||||
| it->second.value = (it->second.value * 7 + rtt) / 8; | ||||||||||||||||
|
||||||||||||||||
| it->second.value = (it->second.value * 7 + rtt) / 8; | |
| auto srtt_us = static_cast<double>(it->second.value.count()); | |
| auto rtt_us = static_cast<double>(rtt.count()); | |
| auto new_srtt_us = (7.0 * srtt_us + rtt_us) / 8.0; | |
| it->second.value = | |
| std::chrono::microseconds{ | |
| static_cast<std::chrono::microseconds::rep>(new_srtt_us)}; |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,7 @@ | |||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||
| #include <generated/protocol/gossip/gossip.pb.h> | ||||||||||||||||||||||||||||||||||||||||||||||||
| #include <algorithm> | ||||||||||||||||||||||||||||||||||||||||||||||||
| #include <boost/asio/io_context.hpp> | ||||||||||||||||||||||||||||||||||||||||||||||||
| #include <boost/asio/steady_timer.hpp> | ||||||||||||||||||||||||||||||||||||||||||||||||
| #include <boost/endian/conversion.hpp> | ||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -729,8 +730,34 @@ namespace libp2p::protocol::gossip { | |||||||||||||||||||||||||||||||||||||||||||||||
| add_peer(peer); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| for (auto &peer : topic.mesh_peers_) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| add_peer(peer); | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (config_.soon_delta >= 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| if (topic.mesh_peers_.size() | ||||||||||||||||||||||||||||||||||||||||||||||||
| <= static_cast<size_t>(config_.soon_delta)) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| for (auto &peer : topic.mesh_peers_) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| add_peer(peer); | ||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||
| std::vector<PeerPtr> sorted_mesh_peers(topic.mesh_peers_.begin(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| topic.mesh_peers_.end()); | ||||||||||||||||||||||||||||||||||||||||||||||||
| auto &rtt_repo = host_->getPeerRepository().getRttRepository(); | ||||||||||||||||||||||||||||||||||||||||||||||||
| std::sort(sorted_mesh_peers.begin(), sorted_mesh_peers.end(), | ||||||||||||||||||||||||||||||||||||||||||||||||
| [&](const PeerPtr &a, const PeerPtr &b) { | ||||||||||||||||||||||||||||||||||||||||||||||||
| auto rtt_a = rtt_repo.getRtt(a->peer_id_); | ||||||||||||||||||||||||||||||||||||||||||||||||
| auto rtt_b = rtt_repo.getRtt(b->peer_id_); | ||||||||||||||||||||||||||||||||||||||||||||||||
| auto val_a = | ||||||||||||||||||||||||||||||||||||||||||||||||
| rtt_a.value_or(std::chrono::microseconds::max()); | ||||||||||||||||||||||||||||||||||||||||||||||||
| auto val_b = | ||||||||||||||||||||||||||||||||||||||||||||||||
| rtt_b.value_or(std::chrono::microseconds::max()); | ||||||||||||||||||||||||||||||||||||||||||||||||
| return val_a < val_b; | ||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+743
to
+752
|
||||||||||||||||||||||||||||||||||||||||||||||||
| std::sort(sorted_mesh_peers.begin(), sorted_mesh_peers.end(), | |
| [&](const PeerPtr &a, const PeerPtr &b) { | |
| auto rtt_a = rtt_repo.getRtt(a->peer_id_); | |
| auto rtt_b = rtt_repo.getRtt(b->peer_id_); | |
| auto val_a = | |
| rtt_a.value_or(std::chrono::microseconds::max()); | |
| auto val_b = | |
| rtt_b.value_or(std::chrono::microseconds::max()); | |
| return val_a < val_b; | |
| }); | |
| std::partial_sort( | |
| sorted_mesh_peers.begin(), | |
| sorted_mesh_peers.begin() + mesh_n, | |
| sorted_mesh_peers.end(), | |
| [&](const PeerPtr &a, const PeerPtr &b) { | |
| auto rtt_a = rtt_repo.getRtt(a->peer_id_); | |
| auto rtt_b = rtt_repo.getRtt(b->peer_id_); | |
| auto val_a = | |
| rtt_a.value_or(std::chrono::microseconds::max()); | |
| auto val_b = | |
| rtt_b.value_or(std::chrono::microseconds::max()); | |
| return val_a < val_b; | |
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -81,12 +81,16 @@ namespace libp2p::protocol { | |||||||||
| } | ||||||||||
| stream = stream_result.value(); | ||||||||||
| } | ||||||||||
| auto r = co_await ping( | ||||||||||
| stream, | ||||||||||
| std::chrono::duration_cast<std::chrono::milliseconds>(config_.timeout)); | ||||||||||
| auto r = | ||||||||||
| co_await ping(stream, | ||||||||||
| std::chrono::duration_cast<std::chrono::milliseconds>( | ||||||||||
| config_.timeout)); | ||||||||||
| if (not r.has_value()) { | ||||||||||
| stream->reset(); | ||||||||||
| stream.reset(); | ||||||||||
| } else { | ||||||||||
| host_->getPeerRepository().getRttRepository().updateRtt( | ||||||||||
| connection->remotePeer(), r.value()); | ||||||||||
| } | ||||||||||
| timer.expires_after(config_.interval); | ||||||||||
| co_await timer.async_wait(boost::asio::use_awaitable); | ||||||||||
|
|
@@ -103,6 +107,10 @@ namespace libp2p::protocol { | |||||||||
| auto stream = stream_result.value(); | ||||||||||
| auto res = co_await ping(stream, timeout); | ||||||||||
| stream->close(); | ||||||||||
| if (res.has_value()) { | ||||||||||
| host_->getPeerRepository().getRttRepository().updateRtt( | ||||||||||
| conn->remotePeer(), res.value()); | ||||||||||
| } | ||||||||||
|
Comment on lines
+110
to
+113
|
||||||||||
| if (res.has_value()) { | |
| host_->getPeerRepository().getRttRepository().updateRtt( | |
| conn->remotePeer(), res.value()); | |
| } |
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.
Missing documentation for the new "soon" configuration field. This field should have a comment explaining what it does, specifically that it enables RTT-based peer selection for mesh broadcasting where peers with lower latency are preferred when the mesh size exceeds the configured mesh_n threshold.