edenapi: add option to call files2 endpoint instead

Summary:
Now that we have a files2 endpoint that can return errors, let's add an
option to call it from the client.

Reviewed By: quark-zju

Differential Revision: D33283030

fbshipit-source-id: 3eda0fe870be1f4b74e0f0f60b11518c7bfe508f
This commit is contained in:
Durham Goode 2022-01-19 17:21:28 -08:00 committed by Facebook GitHub Bot
parent c43e88e2c8
commit e1307b12fc
3 changed files with 69 additions and 29 deletions

View File

@ -54,3 +54,17 @@ Check files in response.
"sha256": bin("a1fff0ffefb9eace7230c24e50731f0a91c62f9cefdfe77121c2f607125dffae"),
"content_id": bin("888dcf533a354c23e4bf67e1ada984d96bb1089b0c3c03f4c2cb773709e7aa42"),
"total_size": 13}}}}]
$ hgedenapi debugapi -e filesattrs -f req --config edenapi.use-files2=True
[{"key": {"node": bin("17b8d4e3bafd4ec4812ad7c930aace9bf07ab033"),
"path": "copy.txt"},
"result": {"Ok": {"key": {"node": bin("17b8d4e3bafd4ec4812ad7c930aace9bf07ab033"),
"path": "copy.txt"},
"content": {"metadata": {"size": None,
"flags": None},
"hg_file_blob": b"\x01\ncopy: test.txt\ncopyrev: 186cafa3319c24956783383dc44c5cbc68c5a0ca\n\x01\ntest content\n"},
"parents": None,
"aux_data": {"sha1": bin("4fe2b8dd12cd9cd6a413ea960cd8c09c25f19527"),
"sha256": bin("a1fff0ffefb9eace7230c24e50731f0a91c62f9cefdfe77121c2f607125dffae"),
"content_id": bin("888dcf533a354c23e4bf67e1ada984d96bb1089b0c3c03f4c2cb773709e7aa42"),
"total_size": 13}}}}]

View File

@ -140,8 +140,8 @@ pub struct HttpClientBuilder {
encoding: Option<Encoding>,
min_transfer_speed: Option<MinTransferSpeed>,
max_retry_per_request: usize,
http_config: http_client::Config,
use_files2: bool,
}
impl HttpClientBuilder {
@ -234,6 +234,7 @@ impl HttpClientBuilder {
);
let max_retry_per_request =
get_config::<usize>(config, "edenapi", "max-retry-per-request")?.unwrap_or(10);
let use_files2 = get_config::<bool>(config, "edenapi", "use-files2")?.unwrap_or_default();
let mut http_config = hg_http::http_config(config);
http_config.verbose_stats |= debug;
@ -261,6 +262,7 @@ impl HttpClientBuilder {
min_transfer_speed,
max_retry_per_request,
http_config,
use_files2,
})
}
@ -346,7 +348,6 @@ impl HttpClientBuilder {
self
}
/// Maximum number of locations per location to has request. Larger requests will be split up
/// into concurrently-sent batches.
pub fn max_location_to_hash(mut self, size: Option<usize>) -> Self {
@ -354,7 +355,6 @@ impl HttpClientBuilder {
self
}
/// Timeout for HTTP requests sent by the client.
pub fn timeout(mut self, timeout: Duration) -> Self {
self.timeout = Some(timeout);
@ -446,6 +446,7 @@ pub(crate) struct Config {
pub(crate) min_transfer_speed: Option<MinTransferSpeed>,
pub(crate) max_retry_per_request: usize,
pub(crate) http_config: http_client::Config,
pub(crate) use_files2: bool,
}
impl TryFrom<HttpClientBuilder> for Config {
@ -474,6 +475,7 @@ impl TryFrom<HttpClientBuilder> for Config {
min_transfer_speed,
max_retry_per_request,
http_config,
use_files2,
} = builder;
// Check for missing required fields.
@ -514,6 +516,7 @@ impl TryFrom<HttpClientBuilder> for Config {
min_transfer_speed,
max_retry_per_request,
http_config,
use_files2,
})
}
}

View File

@ -128,6 +128,7 @@ static FILES_ATTRS_INFLIGHT: Counter = Counter::new("edenapi.files_attrs_infligh
mod paths {
pub const HEALTH_CHECK: &str = "health_check";
pub const FILES: &str = "files";
pub const FILES2: &str = "files2";
pub const HISTORY: &str = "history";
pub const TREES: &str = "trees";
pub const COMMIT_REVLOG_DATA: &str = "commit/revlog_data";
@ -422,26 +423,37 @@ impl Client {
let guards = vec![FILES_INFLIGHT.entrance_guard(keys.len())];
let url = self.build_url(paths::FILES)?;
let use_files2 = self.config().use_files2;
let path = if use_files2 {
paths::FILES2
} else {
paths::FILES
};
let url = self.build_url(path)?;
let requests = self.prepare_requests(&url, keys, self.config().max_files, |keys| {
let req = FileRequest { keys, reqs: vec![] };
self.log_request(&req, "files");
req
})?;
// Convert files v1's FileEntry response into a files v2 FileResponse.
let response = self.fetch_guard::<FileEntry>(requests, guards)?;
let entries = response
.entries
.map_ok(|file_entry| FileResponse {
key: file_entry.key.clone(),
result: Ok(file_entry),
if use_files2 {
Ok(self.fetch_guard::<FileResponse>(requests, guards)?)
} else {
// Convert files v1's FileEntry response into a files v2 FileResponse.
let response = self.fetch_guard::<FileEntry>(requests, guards)?;
let entries = response
.entries
.map_ok(|file_entry| FileResponse {
key: file_entry.key.clone(),
result: Ok(file_entry),
})
.boxed();
Ok(Response {
entries,
stats: response.stats,
})
.boxed();
Ok(Response {
entries,
stats: response.stats,
})
}
}
pub(crate) async fn fetch_trees(
@ -480,26 +492,37 @@ impl Client {
let guards = vec![FILES_ATTRS_INFLIGHT.entrance_guard(reqs.len())];
let url = self.build_url(paths::FILES)?;
let use_files2 = self.config().use_files2;
let path = if use_files2 {
paths::FILES2
} else {
paths::FILES
};
let url = self.build_url(path)?;
let requests = self.prepare_requests(&url, reqs, self.config().max_files, |reqs| {
let req = FileRequest { reqs, keys: vec![] };
self.log_request(&req, "files");
req
})?;
// Convert files v1's FileEntry response into a files v2 FileResponse.
let response = self.fetch_guard::<FileEntry>(requests, guards)?;
let entries = response
.entries
.map_ok(|file_entry| FileResponse {
key: file_entry.key.clone(),
result: Ok(file_entry),
if use_files2 {
Ok(self.fetch_guard::<FileResponse>(requests, guards)?)
} else {
// Convert files v1's FileEntry response into a files v2 FileResponse.
let response = self.fetch_guard::<FileEntry>(requests, guards)?;
let entries = response
.entries
.map_ok(|file_entry| FileResponse {
key: file_entry.key.clone(),
result: Ok(file_entry),
})
.boxed();
Ok(Response {
entries,
stats: response.stats,
})
.boxed();
Ok(Response {
entries,
stats: response.stats,
})
}
}
/// Upload a single file