at protocol indexer with flexible filtering, xrpc queries, and a cursor-backed event stream, built on fjall
at-protocol atproto indexer rust fjall

[resolver] implement failover for plc requests

ptr.pet 4ef5c943 8e6111ba

verified
+37 -6
+37 -6
src/resolver.rs
··· 13 13 IdentityError, IdentityErrorKind, IdentityResolver, PlcSource, ResolverOptions, 14 14 }; 15 15 use miette::{Diagnostic, IntoDiagnostic}; 16 + use reqwest::StatusCode; 16 17 use scc::HashCache; 17 18 use smol_str::SmolStr; 18 19 use thiserror::Error; ··· 87 88 } 88 89 } 89 90 90 - fn get_jacquard(&self) -> &JacquardResolver { 91 - let idx = self.inner.next_idx.fetch_add(1, Ordering::Relaxed) % self.inner.jacquards.len(); 92 - &self.inner.jacquards[idx] 91 + async fn req<'r, T, Fut>( 92 + &'r self, 93 + is_plc: bool, 94 + f: impl Fn(&'r JacquardResolver) -> Fut, 95 + ) -> Result<T, ResolverError> 96 + where 97 + Fut: Future<Output = Result<T, IdentityError>>, 98 + { 99 + let mut idx = 100 + self.inner.next_idx.fetch_add(1, Ordering::Relaxed) % self.inner.jacquards.len(); 101 + let mut try_count = 0; 102 + loop { 103 + let res = f(&self.inner.jacquards[idx]).await; 104 + try_count += 1; 105 + // retry these with the different plc resolvers 106 + if is_plc { 107 + let is_retriable = matches!( 108 + res.as_ref().map_err(|e| e.kind()), 109 + Err(IdentityErrorKind::HttpStatus(StatusCode::TOO_MANY_REQUESTS) 110 + | IdentityErrorKind::Transport(_)) 111 + ); 112 + // check if retriable and we haven't gone through all the plc resolvers 113 + if is_retriable && try_count < self.inner.jacquards.len() { 114 + idx = (idx + 1) % self.inner.jacquards.len(); 115 + continue; 116 + } 117 + } 118 + return res.map_err(Into::into); 119 + } 93 120 } 94 121 95 122 pub async fn resolve_did( ··· 99 126 match identifier { 100 127 AtIdentifier::Did(did) => Ok(did.clone().into_static()), 101 128 AtIdentifier::Handle(handle) => { 102 - let did = self.get_jacquard().resolve_handle(handle).await?; 129 + let did = self.req(false, |j| j.resolve_handle(handle)).await?; 103 130 Ok(did.into_static()) 104 131 } 105 132 } ··· 109 136 &self, 110 137 did: &Did<'_>, 111 138 ) -> Result<(Url, Option<Handle<'_>>), ResolverError> { 112 - let doc_resp = self.get_jacquard().resolve_did_doc(did).await?; 139 + let doc_resp = self 140 + .req(did.starts_with("did:plc:"), |j| j.resolve_did_doc(did)) 141 + .await?; 113 142 let doc = doc_resp.parse()?; 114 143 115 144 let pds = doc ··· 131 160 return Ok(entry.get().clone()); 132 161 } 133 162 134 - let doc_resp = self.get_jacquard().resolve_did_doc(&did).await?; 163 + let doc_resp = self 164 + .req(did.starts_with("did:plc:"), |j| j.resolve_did_doc(&did)) 165 + .await?; 135 166 let doc = doc_resp.parse()?; 136 167 137 168 let key = doc