···3131use mini_moka::sync::Cache;
3232use tracing::{debug, info, warn};
33333434+use crate::util::TokenExt;
3535+3436#[derive(Debug, thiserror::Error)]
3537pub enum IdentityError {
3638 #[error("jacquard: {0}")]
···176178 primary: JacquardResolver,
177179 fallback: Option<JacquardResolver>,
178180 cache: IdentityCache,
181181+ /// Cancellation token for cooperative shutdown.
182182+ token: tokio_util::sync::CancellationToken,
179183}
180184181185impl Resolver {
···240244 did: &Did<'_>,
241245 ) -> Result<(Url, PublicKey<'static>), IdentityError> {
242246 let doc = match &self.fallback {
243243- None => Self::try_backend(&self.primary, did).await,
244244- Some(fb) => match Self::try_backend(&self.primary, did).await {
247247+ None => self.try_backend(&self.primary, did).await,
248248+ Some(fb) => match self.try_backend(&self.primary, did).await {
245249 ok @ Ok(_) => ok,
246250 Err(primary_err) => {
247251 debug!(
···249253 error = %primary_err,
250254 "primary identity resolution failed, trying fallback"
251255 );
252252- Self::try_backend(fb, did).await
256256+ self.try_backend(fb, did).await
253257 }
254258 },
255259 }?;
···275279 ///
276280 /// Non-rate-limit errors return immediately. After exhausting retries the
277281 /// final 429 error is returned so the caller can fall through to a fallback
278278- /// backend or propagate the error.
282282+ /// backend or propagate the error. Sleeps are cancellation-aware when a
283283+ /// token has been provided via [`Resolver::set_cancellation_token`].
279284 async fn try_backend(
285285+ &self,
280286 backend: &JacquardResolver,
281287 did: &Did<'_>,
282288 ) -> std::result::Result<
···297303 delay_ms = delay.as_millis() as u64,
298304 "identity service rate-limited, retrying"
299305 );
300300- tokio::time::sleep(delay).await;
306306+ if !self.token.sleep(delay).await {
307307+ return Err(e); // shutting down
308308+ }
301309 }
302310 Err(e) => {
303311 if is_rate_limited(&e) {
···327335 slingshot_url: Option<Url>,
328336 plc_url: Option<Url>,
329337 cache_size: u64,
338338+ token: tokio_util::sync::CancellationToken,
330339) -> Resolver {
331340 let cache = IdentityCache::new(cache_size);
332341···343352 primary: make_resolver(PlcSource::PlcDirectory { base }),
344353 fallback: None,
345354 cache,
355355+ token: token.clone(),
346356 }
347357 }
348358 (Some(slingshot), None) => {
···357367 primary: make_resolver(PlcSource::Slingshot { base: slingshot }),
358368 fallback: None,
359369 cache,
370370+ token: token.clone(),
360371 }
361372 }
362373 (Some(slingshot), Some(plc)) => {
···371382 primary: make_resolver(PlcSource::Slingshot { base: slingshot }),
372383 fallback: Some(make_resolver(PlcSource::PlcDirectory { base: plc })),
373384 cache,
385385+ token,
374386 }
375387 }
376388 }
+3-1
src/main.rs
···129129 "could not get host from --upstream".to_string(),
130130 ))?;
131131132132+ let token = CancellationToken::new();
133133+132134 let slingshot_url = args.slingshot_url;
133135 let plc_url = args.plc_url;
134136 let ident_cache_size = args.ident_cache_size;
···136138 slingshot_url,
137139 plc_url,
138140 ident_cache_size,
141141+ token.clone(),
139142 ));
140143141144 if let Some(addr) = args.metrics_listen {
···144147145148 let db = storage::open(&args.db_path, args.fjall_cache_mb)?;
146149 let client = lightrail::http::build_client(args.crawl_qps);
147147- let token = CancellationToken::new();
148150149151 let dispatcher_state: resync::dispatcher::DispatcherState = std::sync::Arc::new(
150152 std::sync::Mutex::new(resync::dispatcher::DispatcherSnapshot::default()),
+4-1
src/sync/backfill.rs
···114114 let dids_with_hosts: Vec<(Did<'static>, Arc<Url>)> = {
115115 let mut out = Vec::with_capacity(dids.len());
116116 for did in dids {
117117- let pds_host = match resolver.resolve(&did).await {
117117+ let Some(res) = token.run(resolver.resolve(&did)).await else {
118118+ return Ok(false); // cancelled
119119+ };
120120+ let pds_host = match res {
118121 Ok(resolved) => resolved.pds.clone(),
119122 Err(e) => {
120123 error!(did = %did, error = %e, "failed to resolve host for validated did; not enqueuing resync");
+6-1
src/sync/firehose/event_dispatcher.rs
···432432 }
433433434434 fn make_resolver() -> Arc<crate::identity::Resolver> {
435435- Arc::new(crate::identity::build_resolver(None, None, 1_000))
435435+ Arc::new(crate::identity::build_resolver(
436436+ None,
437437+ None,
438438+ 1_000,
439439+ tokio_util::sync::CancellationToken::new(),
440440+ ))
436441 }
437442438443 /// Drive the dispatcher loop until no workers remain or a cancellation