Skip to content

Commit 5563c1c

Browse files
author
Michael Bryan
authored
Merge pull request #4326 from wasmerio/retry-registry-queries
Always re-execute a registry query when cache lookups fail
2 parents 5afcc59 + 837977c commit 5563c1c

File tree

1 file changed

+166
-79
lines changed

1 file changed

+166
-79
lines changed

lib/wasix/src/runtime/resolver/wapm_source.rs

+166-79
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66

77
use anyhow::{Context, Error};
88
use http::{HeaderMap, Method};
9-
use semver::Version;
9+
use semver::{Version, VersionReq};
1010
use url::Url;
1111
use webc::metadata::Manifest;
1212

@@ -60,39 +60,6 @@ impl WapmSource {
6060
&self.registry_endpoint
6161
}
6262

63-
async fn lookup_package(&self, package_name: &str) -> Result<WapmWebQuery, Error> {
64-
if let Some(cache) = &self.cache {
65-
match cache.lookup_cached_query(package_name) {
66-
Ok(Some(cached)) => {
67-
tracing::debug!("Cache hit!");
68-
return Ok(cached);
69-
}
70-
Ok(None) => {}
71-
Err(e) => {
72-
tracing::warn!(
73-
package_name,
74-
error = &*e,
75-
"An unexpected error occurred while checking the local query cache",
76-
);
77-
}
78-
}
79-
}
80-
81-
let response = self.query_graphql(package_name).await?;
82-
83-
if let Some(cache) = &self.cache {
84-
if let Err(e) = cache.update(package_name, &response) {
85-
tracing::warn!(
86-
package_name,
87-
error = &*e,
88-
"An error occurred while caching the GraphQL response",
89-
);
90-
}
91-
}
92-
93-
Ok(response)
94-
}
95-
9663
#[tracing::instrument(level = "debug", skip_all)]
9764
async fn query_graphql(&self, package_name: &str) -> Result<WapmWebQuery, Error> {
9865
#[derive(serde::Serialize)]
@@ -166,68 +133,104 @@ impl WapmSource {
166133
impl Source for WapmSource {
167134
#[tracing::instrument(level = "debug", skip_all, fields(%package))]
168135
async fn query(&self, package: &PackageSpecifier) -> Result<Vec<PackageSummary>, QueryError> {
169-
let (full_name, version_constraint) = match package {
136+
let (package_name, version_constraint) = match package {
170137
PackageSpecifier::Registry { full_name, version } => (full_name, version),
171138
_ => return Err(QueryError::Unsupported),
172139
};
173140

174-
let response: WapmWebQuery = self.lookup_package(full_name).await?;
175-
176-
let mut summaries = Vec::new();
177-
178-
let WapmWebQueryGetPackage {
179-
package_name,
180-
namespace,
181-
versions,
182-
} = response.data.get_package.ok_or(QueryError::NotFound)?;
183-
let mut archived_versions = Vec::new();
184-
185-
for pkg_version in versions {
186-
let version = match Version::parse(&pkg_version.version) {
187-
Ok(v) => v,
141+
if let Some(cache) = &self.cache {
142+
match cache.lookup_cached_query(package_name) {
143+
Ok(Some(cached)) => {
144+
if let Ok(cached) = matching_package_summaries(cached, version_constraint) {
145+
tracing::debug!("Cache hit!");
146+
return Ok(cached);
147+
}
148+
}
149+
Ok(None) => {}
188150
Err(e) => {
189-
tracing::debug!(
190-
pkg.version = pkg_version.version.as_str(),
191-
error = &e as &dyn std::error::Error,
192-
"Skipping a version because it doesn't have a valid version numer",
151+
tracing::warn!(
152+
package_name,
153+
error = &*e,
154+
"An unexpected error occurred while checking the local query cache",
193155
);
194-
continue;
195156
}
196-
};
157+
}
158+
}
197159

198-
if pkg_version.is_archived {
160+
let response = self.query_graphql(package_name).await?;
161+
162+
if let Some(cache) = &self.cache {
163+
if let Err(e) = cache.update(package_name, &response) {
164+
tracing::warn!(
165+
package_name,
166+
error = &*e,
167+
"An error occurred while caching the GraphQL response",
168+
);
169+
}
170+
}
171+
172+
matching_package_summaries(response, version_constraint)
173+
}
174+
}
175+
176+
fn matching_package_summaries(
177+
response: WapmWebQuery,
178+
version_constraint: &VersionReq,
179+
) -> Result<Vec<PackageSummary>, QueryError> {
180+
let mut summaries = Vec::new();
181+
182+
let WapmWebQueryGetPackage {
183+
package_name,
184+
namespace,
185+
versions,
186+
} = response.data.get_package.ok_or(QueryError::NotFound)?;
187+
let mut archived_versions = Vec::new();
188+
189+
for pkg_version in versions {
190+
let version = match Version::parse(&pkg_version.version) {
191+
Ok(v) => v,
192+
Err(e) => {
199193
tracing::debug!(
200-
pkg.version=%version,
201-
"Skipping an archived version",
194+
pkg.version = pkg_version.version.as_str(),
195+
error = &e as &dyn std::error::Error,
196+
"Skipping a version because it doesn't have a valid version numer",
202197
);
203-
archived_versions.push(version);
204198
continue;
205199
}
200+
};
206201

207-
if version_constraint.matches(&version) {
208-
match decode_summary(
209-
pkg_version,
210-
&response.data.info.default_frontend,
211-
&namespace,
212-
&package_name,
213-
) {
214-
Ok(summary) => summaries.push(summary),
215-
Err(e) => {
216-
tracing::debug!(
217-
version=%version,
218-
error=&*e,
219-
"Skipping version because its metadata couldn't be parsed"
220-
);
221-
}
202+
if pkg_version.is_archived {
203+
tracing::debug!(
204+
pkg.version=%version,
205+
"Skipping an archived version",
206+
);
207+
archived_versions.push(version);
208+
continue;
209+
}
210+
211+
if version_constraint.matches(&version) {
212+
match decode_summary(
213+
pkg_version,
214+
&response.data.info.default_frontend,
215+
&namespace,
216+
&package_name,
217+
) {
218+
Ok(summary) => summaries.push(summary),
219+
Err(e) => {
220+
tracing::debug!(
221+
version=%version,
222+
error=&*e,
223+
"Skipping version because its metadata couldn't be parsed"
224+
);
222225
}
223226
}
224227
}
228+
}
225229

226-
if summaries.is_empty() {
227-
Err(QueryError::NoMatches { archived_versions })
228-
} else {
229-
Ok(summaries)
230-
}
230+
if summaries.is_empty() {
231+
Err(QueryError::NoMatches { archived_versions })
232+
} else {
233+
Ok(summaries)
231234
}
232235
}
233236

@@ -719,4 +722,88 @@ mod tests {
719722
assert_eq!(summaries.len(), 1);
720723
assert_eq!(summaries[0].pkg.version.to_string(), "3.12.1");
721724
}
725+
726+
#[tokio::test]
727+
async fn query_the_backend_again_if_cached_queries_dont_match() {
728+
let cached_value = serde_json::from_value(serde_json::json! {
729+
{
730+
"data": {
731+
"getPackage": {
732+
"packageName": "python",
733+
"namespace": "wasmer",
734+
"versions": [
735+
{
736+
"version": "3.12.0",
737+
"piritaManifest": "{\"package\": {\"wapm\": {\"name\": \"wasmer/python\", \"version\": \"3.12.0\", \"description\": \"Python\"}}}",
738+
"distribution": {
739+
"piritaDownloadUrl": "https://wasmer.io/wasmer/[email protected]",
740+
"piritaSha256Hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
741+
}
742+
},
743+
]
744+
},
745+
"info": {
746+
"defaultFrontend": "https://wasmer.io/",
747+
},
748+
}
749+
}
750+
}).unwrap();
751+
let body = serde_json::json! {
752+
{
753+
"data": {
754+
"getPackage": {
755+
"packageName": "python",
756+
"namespace": "wasmer",
757+
"versions": [
758+
{
759+
"version": "4.0.0",
760+
"piritaManifest": "{\"package\": {\"wapm\": {\"name\": \"wasmer/python\", \"version\": \"4.0.0\", \"description\": \"Python\"}}}",
761+
"distribution": {
762+
"piritaDownloadUrl": "https://wasmer.io/wasmer/[email protected]",
763+
"piritaSha256Hash": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
764+
}
765+
},
766+
{
767+
"version": "3.12.0",
768+
"piritaManifest": "{\"package\": {\"wapm\": {\"name\": \"wasmer/python\", \"version\": \"3.12.0\", \"description\": \"Python\"}}}",
769+
"distribution": {
770+
"piritaDownloadUrl": "https://wasmer.io/wasmer/[email protected]",
771+
"piritaSha256Hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
772+
}
773+
},
774+
]
775+
},
776+
"info": {
777+
"defaultFrontend": "https://wasmer.io/",
778+
},
779+
}
780+
}
781+
};
782+
let response = HttpResponse {
783+
body: Some(serde_json::to_vec(&body).unwrap()),
784+
redirected: false,
785+
status: StatusCode::OK,
786+
headers: HeaderMap::new(),
787+
};
788+
let client = Arc::new(DummyClient::new(vec![response]));
789+
let registry_endpoint = WapmSource::WASMER_PROD_ENDPOINT.parse().unwrap();
790+
let request = PackageSpecifier::Registry {
791+
full_name: "wasmer/python".to_string(),
792+
version: "4.0.0".parse().unwrap(),
793+
};
794+
let temp = tempfile::tempdir().unwrap();
795+
let source = WapmSource::new(registry_endpoint, client.clone())
796+
.with_local_cache(temp.path(), Duration::from_secs(0));
797+
source
798+
.cache
799+
.as_ref()
800+
.unwrap()
801+
.update("wasmer/python", &cached_value)
802+
.unwrap();
803+
804+
let summaries = source.query(&request).await.unwrap();
805+
806+
assert_eq!(summaries.len(), 1);
807+
assert_eq!(summaries[0].pkg.version.to_string(), "4.0.0");
808+
}
722809
}

0 commit comments

Comments
 (0)