Skip to content

Commit c36fdaa

Browse files
paritytech-cmd-bot-polkadot-sdk[bot]lexnvEgorPopelyaev
authored
backport/stable2412: litep2p: Provide partial results to speedup GetRecord queries (#7192)
Backport #7099 into `stable2412` from lexnv. See the [documentation](https://github.com/paritytech/polkadot-sdk/blob/master/docs/BACKPORT.md) on how to use this bot. <!-- # To be used by other automation, do not modify: original-pr-number: #${pull_number} --> --------- Signed-off-by: Alexandru Vasile <[email protected]> Co-authored-by: Alexandru Vasile <[email protected]> Co-authored-by: Alexandru Vasile <[email protected]> Co-authored-by: Egor_P <[email protected]>
1 parent 60832af commit c36fdaa

File tree

5 files changed

+87
-68
lines changed

5 files changed

+87
-68
lines changed

Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -846,7 +846,7 @@ linked-hash-map = { version = "0.5.4" }
846846
linked_hash_set = { version = "0.1.4" }
847847
linregress = { version = "0.5.1" }
848848
lite-json = { version = "0.2.0", default-features = false }
849-
litep2p = { version = "0.8.4", features = ["websocket"] }
849+
litep2p = { version = "0.9.0", features = ["websocket"] }
850850
log = { version = "0.4.22", default-features = false }
851851
macro_magic = { version = "0.5.1" }
852852
maplit = { version = "1.0.2" }

prdoc/pr_7099.prdoc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
title: Provide partial results to speedup GetRecord queries
2+
3+
doc:
4+
- audience: Node Dev
5+
description: |
6+
This PR provides the partial results of the GetRecord kademlia query.
7+
8+
This significantly improves the authority discovery records, from ~37 minutes to ~2/3 minutes.
9+
In contrast, libp2p discovers authority records in around ~10 minutes.
10+
11+
The authority discovery was slow because litep2p provided the records only after the Kademlia query was completed. A normal Kademlia query completes in around 40 seconds to a few minutes.
12+
In this PR, partial records are provided as soon as they are discovered from the network.
13+
14+
crates:
15+
- name: sc-network
16+
bump: patch

substrate/client/network/src/litep2p/discovery.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ use litep2p::{
3434
identify::{Config as IdentifyConfig, IdentifyEvent},
3535
kademlia::{
3636
Config as KademliaConfig, ConfigBuilder as KademliaConfigBuilder,
37-
IncomingRecordValidationMode, KademliaEvent, KademliaHandle, QueryId, Quorum,
38-
Record, RecordKey, RecordsType,
37+
IncomingRecordValidationMode, KademliaEvent, KademliaHandle, PeerRecord, QueryId,
38+
Quorum, Record, RecordKey,
3939
},
4040
ping::{Config as PingConfig, PingEvent},
4141
},
@@ -129,13 +129,19 @@ pub enum DiscoveryEvent {
129129
address: Multiaddr,
130130
},
131131

132-
/// Record was found from the DHT.
132+
/// `GetRecord` query succeeded.
133133
GetRecordSuccess {
134134
/// Query ID.
135135
query_id: QueryId,
136+
},
136137

137-
/// Records.
138-
records: RecordsType,
138+
/// Record was found from the DHT.
139+
GetRecordPartialResult {
140+
/// Query ID.
141+
query_id: QueryId,
142+
143+
/// Record.
144+
record: PeerRecord,
139145
},
140146

141147
/// Record was successfully stored on the DHT.
@@ -550,13 +556,24 @@ impl Stream for Discovery {
550556
peers: peers.into_iter().collect(),
551557
}))
552558
},
553-
Poll::Ready(Some(KademliaEvent::GetRecordSuccess { query_id, records })) => {
559+
Poll::Ready(Some(KademliaEvent::GetRecordSuccess { query_id })) => {
554560
log::trace!(
555561
target: LOG_TARGET,
556-
"`GET_RECORD` succeeded for {query_id:?}: {records:?}",
562+
"`GET_RECORD` succeeded for {query_id:?}",
557563
);
558564

559-
return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id, records }));
565+
return Poll::Ready(Some(DiscoveryEvent::GetRecordSuccess { query_id }));
566+
},
567+
Poll::Ready(Some(KademliaEvent::GetRecordPartialResult { query_id, record })) => {
568+
log::trace!(
569+
target: LOG_TARGET,
570+
"`GET_RECORD` intermediary succeeded for {query_id:?}: {record:?}",
571+
);
572+
573+
return Poll::Ready(Some(DiscoveryEvent::GetRecordPartialResult {
574+
query_id,
575+
record,
576+
}));
560577
},
561578
Poll::Ready(Some(KademliaEvent::PutRecordSuccess { query_id, key: _ })) =>
562579
return Poll::Ready(Some(DiscoveryEvent::PutRecordSuccess { query_id })),

substrate/client/network/src/litep2p/mod.rs

Lines changed: 43 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ use litep2p::{
5959
protocol::{
6060
libp2p::{
6161
bitswap::Config as BitswapConfig,
62-
kademlia::{QueryId, Record, RecordsType},
62+
kademlia::{QueryId, Record},
6363
},
6464
request_response::ConfigBuilder as RequestResponseConfigBuilder,
6565
},
@@ -820,35 +820,60 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
820820
self.peerstore_handle.add_known_peer(peer.into());
821821
}
822822
}
823-
Some(DiscoveryEvent::GetRecordSuccess { query_id, records }) => {
824-
match self.pending_get_values.remove(&query_id) {
825-
None => log::warn!(
823+
Some(DiscoveryEvent::GetRecordPartialResult { query_id, record }) => {
824+
if !self.pending_get_values.contains_key(&query_id) {
825+
log::error!(
826826
target: LOG_TARGET,
827-
"`GET_VALUE` succeeded for a non-existent query",
828-
),
827+
"Missing/invalid pending query for `GET_VALUE` partial result: {query_id:?}"
828+
);
829+
830+
continue
831+
}
832+
833+
let peer_id: sc_network_types::PeerId = record.peer.into();
834+
let record = PeerRecord {
835+
record: P2PRecord {
836+
key: record.record.key.to_vec().into(),
837+
value: record.record.value,
838+
publisher: record.record.publisher.map(|peer_id| {
839+
let peer_id: sc_network_types::PeerId = peer_id.into();
840+
peer_id.into()
841+
}),
842+
expires: record.record.expires,
843+
},
844+
peer: Some(peer_id.into()),
845+
};
846+
847+
self.event_streams.send(
848+
Event::Dht(
849+
DhtEvent::ValueFound(
850+
record.into()
851+
)
852+
)
853+
);
854+
}
855+
Some(DiscoveryEvent::GetRecordSuccess { query_id }) => {
856+
match self.pending_get_values.remove(&query_id) {
829857
Some((key, started)) => {
830858
log::trace!(
831859
target: LOG_TARGET,
832-
"`GET_VALUE` for {:?} ({query_id:?}) succeeded",
833-
key,
860+
"`GET_VALUE` for {key:?} ({query_id:?}) succeeded",
834861
);
835-
for record in litep2p_to_libp2p_peer_record(records) {
836-
self.event_streams.send(
837-
Event::Dht(
838-
DhtEvent::ValueFound(
839-
record
840-
)
841-
)
842-
);
843-
}
844862

845863
if let Some(ref metrics) = self.metrics {
846864
metrics
847865
.kademlia_query_duration
848866
.with_label_values(&["value-get"])
849867
.observe(started.elapsed().as_secs_f64());
850868
}
851-
}
869+
},
870+
None => {
871+
log::error!(
872+
target: LOG_TARGET,
873+
"Missing/invalid pending query for `GET_VALUE`: {query_id:?}"
874+
);
875+
debug_assert!(false);
876+
},
852877
}
853878
}
854879
Some(DiscoveryEvent::PutRecordSuccess { query_id }) => {
@@ -1095,42 +1120,3 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkBackend<B, H> for Litep2pNetworkBac
10951120
}
10961121
}
10971122
}
1098-
1099-
// Glue code to convert from a litep2p records type to a libp2p2 PeerRecord.
1100-
fn litep2p_to_libp2p_peer_record(records: RecordsType) -> Vec<PeerRecord> {
1101-
match records {
1102-
litep2p::protocol::libp2p::kademlia::RecordsType::LocalStore(record) => {
1103-
vec![PeerRecord {
1104-
record: P2PRecord {
1105-
key: record.key.to_vec().into(),
1106-
value: record.value,
1107-
publisher: record.publisher.map(|peer_id| {
1108-
let peer_id: sc_network_types::PeerId = peer_id.into();
1109-
peer_id.into()
1110-
}),
1111-
expires: record.expires,
1112-
},
1113-
peer: None,
1114-
}]
1115-
},
1116-
litep2p::protocol::libp2p::kademlia::RecordsType::Network(records) => records
1117-
.into_iter()
1118-
.map(|record| {
1119-
let peer_id: sc_network_types::PeerId = record.peer.into();
1120-
1121-
PeerRecord {
1122-
record: P2PRecord {
1123-
key: record.record.key.to_vec().into(),
1124-
value: record.record.value,
1125-
publisher: record.record.publisher.map(|peer_id| {
1126-
let peer_id: sc_network_types::PeerId = peer_id.into();
1127-
peer_id.into()
1128-
}),
1129-
expires: record.record.expires,
1130-
},
1131-
peer: Some(peer_id.into()),
1132-
}
1133-
})
1134-
.collect::<Vec<_>>(),
1135-
}
1136-
}

0 commit comments

Comments
 (0)