fix(rust-volume): parse master lookup when publicUrl is omitted (#10128)

Master /dir/lookup JSON omits publicUrl when empty (Go json omitempty).
The Rust volume server required the field, so serde failed with "lookup
parse failed: error decoding response body" and cross-DC replicated writes
failed.

Default publicUrl to empty, fall back to url for peer filtering, and
normalize addresses with to_http_address before excluding the local peer
(so host:port.grpcPort forms do not match self incorrectly).
This commit is contained in:
jk2lx
2026-06-29 14:03:58 -07:00
committed by GitHub
parent 9752286fd4
commit 96f93d8e3b
+37 -2
View File
@@ -356,12 +356,23 @@ fn parse_url_path(path: &str) -> Option<(VolumeId, NeedleId, Cookie)> {
#[derive(Clone, Debug, Deserialize)]
struct VolumeLocation {
url: String,
#[serde(rename = "publicUrl")]
// Master often omits publicUrl when it matches url (Go json omitempty).
#[serde(rename = "publicUrl", default)]
public_url: String,
#[serde(rename = "grpcPort", default)]
grpc_port: u32,
}
impl VolumeLocation {
fn public_or_url(&self) -> &str {
if self.public_url.is_empty() {
&self.url
} else {
&self.public_url
}
}
}
/// Master /dir/lookup response.
#[derive(Debug, Deserialize)]
struct LookupResult {
@@ -535,9 +546,13 @@ async fn do_replicated_request(
.await
.map_err(|e| format!("lookup volume failed: {}", e))?;
let self_http = to_http_address(&state.self_url);
let remote_locations: Vec<_> = locations
.into_iter()
.filter(|loc| loc.url != state.self_url && loc.public_url != state.self_url)
.filter(|loc| {
to_http_address(&loc.url) != self_http
&& to_http_address(loc.public_or_url()) != self_http
})
.collect();
if remote_locations.is_empty() {
@@ -4022,6 +4037,26 @@ mod tests {
);
}
/// Master /dir/lookup often omits publicUrl when empty (Go `json:"publicUrl,omitempty"`).
/// Replication must still parse locations or every cross-DC write fails.
#[test]
fn test_lookup_result_deserializes_without_public_url() {
let body = r#"{
"volumeOrFileId": "9",
"locations": [
{"url": "volume-a.example:8080", "dataCenter": "dc-a", "grpcPort": 18080},
{"url": "volume-b.example:8080", "dataCenter": "dc-b", "grpcPort": 18080}
]
}"#;
let result: LookupResult = serde_json::from_str(body).expect("parse lookup JSON");
let locations = result.locations.expect("locations");
assert_eq!(locations.len(), 2);
assert_eq!(locations[0].url, "volume-a.example:8080");
assert!(locations[0].public_url.is_empty());
assert_eq!(locations[0].public_or_url(), "volume-a.example:8080");
assert_eq!(locations[0].grpc_port, 18080);
}
/// Regression test for issue #9274.
///
/// SeaweedFS encodes a server's gRPC port by appending `.grpcPort` to