diff --git a/.github/workflows/cd.yaml b/.github/workflows/cd.yaml index d50b9c21c..713509821 100644 --- a/.github/workflows/cd.yaml +++ b/.github/workflows/cd.yaml @@ -107,7 +107,6 @@ jobs: if: needs.publish_rust.outputs.rust_changed == 'true' name: Publish for Docker runs-on: ubuntu-latest - timeout-minutes: 40 env: REGISTRY: ghcr.io diff --git a/packages/libraries/router/src/registry.rs b/packages/libraries/router/src/registry.rs index 8509d7027..2e8840fc9 100644 --- a/packages/libraries/router/src/registry.rs +++ b/packages/libraries/router/src/registry.rs @@ -10,6 +10,7 @@ pub struct HiveRegistry { endpoint: String, key: String, file_name: String, + etag: Option, } impl HiveRegistry { @@ -44,10 +45,11 @@ impl HiveRegistry { env::set_var("APOLLO_ROUTER_SUPERGRAPH_PATH", file_name.clone()); env::set_var("APOLLO_ROUTER_HOT_RELOAD", "true"); - let registry = HiveRegistry { + let mut registry = HiveRegistry { endpoint, key, file_name, + etag: None, }; match registry.initial_supergraph() { @@ -68,36 +70,75 @@ impl HiveRegistry { Ok(()) } - fn fetch_supergraph(&self) -> Result { + fn fetch_supergraph(&mut self, etag: Option) -> Result, String> { let client = reqwest::blocking::Client::new(); + let mut headers = reqwest::header::HeaderMap::new(); + + headers.insert("X-Hive-CDN-Key", self.key.parse().unwrap()); + + if let Some(checksum) = etag { + headers.insert("If-None-Match", checksum.parse().unwrap()); + } + let resp = client .get(format!("{}/supergraph", self.endpoint)) - .header("X-Hive-CDN-Key", self.key.to_string()) + .headers(headers) .send() .map_err(|e| e.to_string())?; - Ok(resp.text().map_err(|e| e.to_string())?) + match resp.headers().get("etag") { + Some(checksum) => { + let etag = checksum.to_str().map_err(|e| e.to_string())?; + self.update_latest_etag(Some(etag.to_string())); + } + None => { + self.update_latest_etag(None); + } + } + + if resp.status().as_u16() == 304 { + return Ok(None); + } + + Ok(Some(resp.text().map_err(|e| e.to_string())?)) } - fn initial_supergraph(&self) -> Result<(), String> { + fn initial_supergraph(&mut self) -> Result<(), String> { let mut file = std::fs::File::create(self.file_name.clone()).map_err(|e| e.to_string())?; - let resp = self.fetch_supergraph()?; - file.write_all(resp.as_bytes()).map_err(|e| e.to_string())?; + let resp = self.fetch_supergraph(None)?; + + match resp { + Some(supergraph) => { + file.write_all(supergraph.as_bytes()) + .map_err(|e| e.to_string())?; + } + None => { + return Err("Failed to fetch supergraph".to_string()); + } + } + Ok(()) } - fn poll(&self) { - let current_file = - std::fs::read_to_string(self.file_name.clone()).expect("Could not read file"); - let current_supergraph_hash = hash(current_file.as_bytes()); + fn update_latest_etag(&mut self, etag: Option) { + self.etag = etag; + } - match self.fetch_supergraph() { + fn poll(&mut self) { + match self.fetch_supergraph(self.etag.clone()) { Ok(new_supergraph) => { - let new_supergraph_hash = hash(new_supergraph.as_bytes()); - if current_supergraph_hash != new_supergraph_hash { - tracing::info!("New supergraph detected!"); - std::fs::write(self.file_name.clone(), new_supergraph) - .expect("Could not write file"); + if let Some(new_supergraph) = new_supergraph { + let current_file = std::fs::read_to_string(self.file_name.clone()) + .expect("Could not read file"); + let current_supergraph_hash = hash(current_file.as_bytes()); + + let new_supergraph_hash = hash(new_supergraph.as_bytes()); + + if current_supergraph_hash != new_supergraph_hash { + tracing::info!("New supergraph detected!"); + std::fs::write(self.file_name.clone(), new_supergraph) + .expect("Could not write file"); + } } } Err(e) => tracing::error!("{}", e),