Skip to content

Commit be315ae

Browse files
lmvasquezgfacebook-github-bot
authored andcommitted
aosp: implement streaming changelog on slapi
Summary: In our last hackathon we worked on deprecating wireproto, but one of the big blockers was AOSP clones regression when we used commit graph segments. Therefore, we decided to migrate the streaming changelog endpoint over to SLAPI. This is a first draft that mimics what the wireproto endpoint was doing, still need to do the client side but figured I'd put this up for review for now since I'm working on this on my bits of free time. Reviewed By: muirdm Differential Revision: D77028873 fbshipit-source-id: afab86424ecd192071d42bfd62a1a8a405f664ac
1 parent ba81ff8 commit be315ae

File tree

4 files changed

+129
-7
lines changed

4 files changed

+129
-7
lines changed

eden/mononoke/edenapi_service/BUCK

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ rust_library(
2020
"fbsource//third-party/rust:anyhow",
2121
"fbsource//third-party/rust:async-stream",
2222
"fbsource//third-party/rust:async-trait",
23+
"//common/rust/shed/futures_ext:futures_ext",
2324
"fbsource//third-party/rust:base64",
2425
"fbsource//third-party/rust:bytes",
2526
"fbsource//third-party/rust:futures",
@@ -58,6 +59,7 @@ rust_library(
5859
"//common/rust/shed/stats:stats",
5960
"//common/rust/shed/time_ext:time_ext",
6061
"//eden/mononoke/repo_attributes/repo_blobstore:repo_blobstore",
62+
"//eden/mononoke/repo_client:streaming_clone",
6163
"//eden/mononoke/blobstore:blobstore",
6264
"//eden/mononoke/blobstore:ephemeral_blobstore",
6365
"//eden/mononoke/bookmarks:bookmarks",

eden/mononoke/edenapi_service/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ ephemeral_blobstore = { version = "0.1.0", path = "../blobstore/ephemeral_blobst
2626
fbinit = { version = "0.2.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" }
2727
filestore = { version = "0.1.0", path = "../filestore" }
2828
futures = { version = "0.3.31", features = ["async-await", "compat"] }
29+
futures_ext = { version = "0.1.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" }
2930
futures_stats = { version = "0.1.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" }
3031
git_types = { version = "0.1.0", path = "../git/git_types" }
3132
gotham = "0.7.1"
@@ -71,6 +72,7 @@ slog = { package = "tracing_slog_compat", version = "0.1.0", git = "https://gith
7172
smallvec = { version = "1.15", features = ["impl_bincode", "serde", "specialization", "union"] }
7273
sorted_vector_map = { version = "0.2.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" }
7374
stats = { version = "0.1.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" }
75+
streaming_clone = { version = "0.1.0", path = "../repo_client/streaming_clone" }
7476
thiserror = "2.0.12"
7577
time_ext = { version = "0.1.0", git = "https://github.com/facebookexperimental/rust-shed.git", branch = "main" }
7678
time_window_counter = { version = "0.1.0", path = "../time_window_counter" }

eden/mononoke/edenapi_service/src/handlers/legacy.rs

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,35 @@
55
* GNU General Public License version 2.
66
*/
77

8+
use std::time::Duration;
9+
810
use async_trait::async_trait;
11+
use cloned::cloned;
12+
use context::PerfCounterType;
13+
use edenapi_types::ServerError;
914
use edenapi_types::StreamingChangelogRequest;
1015
use edenapi_types::StreamingChangelogResponse;
16+
use edenapi_types::legacy::Metadata;
17+
use edenapi_types::legacy::StreamingChangelogBlob;
18+
use edenapi_types::legacy::StreamingChangelogData;
19+
use futures::future::FutureExt;
20+
use futures::stream;
21+
use futures::stream::StreamExt;
22+
use futures::stream::TryStreamExt;
23+
use futures_ext::FbStreamExt;
24+
use futures_stats::TimedFutureExt;
1125
use mononoke_api::Repo;
26+
use slog::debug;
27+
use streaming_clone::StreamingCloneArc;
28+
use time_ext::DurationExt;
1229

1330
use super::HandlerResult;
1431
use super::SaplingRemoteApiHandler;
1532
use super::SaplingRemoteApiMethod;
1633
use super::handler::SaplingRemoteApiContext;
1734

35+
const TIMEOUT_SECS: Duration = Duration::from_secs(4 * 60 * 60);
36+
1837
/// Legacy streaming changelog handler from wireproto.
1938
#[allow(dead_code)]
2039
pub struct StreamingCloneHandler;
@@ -29,9 +48,108 @@ impl SaplingRemoteApiHandler for StreamingCloneHandler {
2948
const ENDPOINT: &'static str = "/streaming_clone";
3049

3150
async fn handler(
32-
_ectx: SaplingRemoteApiContext<Self::PathExtractor, Self::QueryStringExtractor, Repo>,
33-
_request: Self::Request,
51+
ectx: SaplingRemoteApiContext<Self::PathExtractor, Self::QueryStringExtractor, Repo>,
52+
request: Self::Request,
3453
) -> HandlerResult<'async_trait, Self::Response> {
35-
unimplemented!("StreamingCloneHandler is not implemented")
54+
let streaming_clone = ectx.repo().repo().streaming_clone_arc();
55+
let ctx = ectx.repo().ctx().clone();
56+
57+
let changelog = streaming_clone
58+
.fetch_changelog(ctx.clone(), request.tag.as_deref())
59+
.await?;
60+
61+
let data_blob_chunk_count = 0;
62+
let data_blobs: Vec<_> = changelog
63+
.data_blobs
64+
.into_iter()
65+
.map(|fut| {
66+
cloned!(ctx);
67+
async move {
68+
let (stats, res) = fut.timed().await;
69+
ctx.perf_counters().add_to_counter(
70+
PerfCounterType::SumManifoldPollTime,
71+
stats.poll_time.as_nanos_unchecked() as i64,
72+
);
73+
if let Ok(bytes) = res.as_ref() {
74+
ctx.perf_counters()
75+
.add_to_counter(PerfCounterType::BytesSent, bytes.len() as i64)
76+
}
77+
78+
let data = res.map(|res| {
79+
StreamingChangelogData::DataBlobChunk(StreamingChangelogBlob {
80+
chunk: res.into(),
81+
chunk_id: data_blob_chunk_count,
82+
})
83+
});
84+
85+
StreamingChangelogResponse {
86+
data: data.map_err(|e| ServerError::generic(format!("{:?}", e))),
87+
}
88+
}
89+
.boxed()
90+
})
91+
.collect();
92+
93+
let index_blob_chunk_count = 0;
94+
let index_blobs: Vec<_> = changelog
95+
.index_blobs
96+
.into_iter()
97+
.map(|fut| {
98+
cloned!(ctx);
99+
async move {
100+
let (stats, res) = fut.timed().await;
101+
ctx.perf_counters().add_to_counter(
102+
PerfCounterType::SumManifoldPollTime,
103+
stats.poll_time.as_nanos_unchecked() as i64,
104+
);
105+
if let Ok(bytes) = res.as_ref() {
106+
ctx.perf_counters()
107+
.add_to_counter(PerfCounterType::BytesSent, bytes.len() as i64)
108+
}
109+
110+
let data = res.map(|res| {
111+
StreamingChangelogData::IndexBlobChunk(StreamingChangelogBlob {
112+
chunk: res.into(),
113+
chunk_id: index_blob_chunk_count,
114+
})
115+
});
116+
117+
StreamingChangelogResponse {
118+
data: data.map_err(|e| ServerError::generic(format!("{:?}", e))),
119+
}
120+
}
121+
.boxed()
122+
})
123+
.collect();
124+
125+
debug!(
126+
ctx.logger(),
127+
"streaming changelog {} index bytes, {} data bytes",
128+
changelog.index_size,
129+
changelog.data_size
130+
);
131+
132+
let metadata = StreamingChangelogData::Metadata(Metadata {
133+
index_size: changelog.index_size as u64,
134+
data_size: changelog.data_size as u64,
135+
});
136+
let mut response_header = Vec::new();
137+
response_header.push(metadata);
138+
139+
let response = stream::iter(
140+
response_header
141+
.into_iter()
142+
.map(|data| StreamingChangelogResponse { data: Ok(data) }),
143+
);
144+
145+
let res = response
146+
.chain(stream::iter(index_blobs).buffered(100))
147+
.chain(stream::iter(data_blobs).buffered(100));
148+
149+
Ok(res
150+
.whole_stream_timeout(TIMEOUT_SECS)
151+
.yield_periodically()
152+
.map_err(|e| e.into())
153+
.boxed())
36154
}
37155
}

eden/scm/lib/edenapi/types/src/legacy.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,19 @@ impl Default for StreamingChangelogData {
5050
#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq)]
5151
pub struct StreamingChangelogBlob {
5252
#[id(0)]
53-
chunk: Bytes,
53+
pub chunk: Bytes,
5454
#[id(1)]
55-
chunk_id: u64,
55+
pub chunk_id: u64,
5656
}
5757

5858
#[auto_wire]
5959
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
6060
#[cfg_attr(any(test, feature = "for-tests"), derive(Arbitrary))]
6161
pub struct Metadata {
6262
#[id(0)]
63-
index_size: u64,
63+
pub index_size: u64,
6464
#[id(1)]
65-
data_size: u64,
65+
pub data_size: u64,
6666
}
6767

6868
#[cfg(any(test, feature = "for-tests"))]

0 commit comments

Comments
 (0)