···11_A [gravitational slingshot](https://en.wikipedia.org/wiki/Gravity_assist) makes use of the gravity and relative movements of celestial bodies to accelerate a spacecraft and change its trajectory._
223344-# Slingshot: edge record cache
44+# Slingshot: edge record and identity cache
5566Applications in [ATProtocol](https://atproto.com/) store data in users' own [PDS](https://atproto.com/guides/self-hosting) (Personal Data Server), which are distributed across thousands of independently-run servers all over the world. Trying to access this data poses challenges for client applications:
77
+42-23
slingshot/src/identity.rs
···1111/// 1. handle -> DID resolution: getRecord must accept a handle for `repo` param
1212/// 2. DID -> PDS resolution: so we know where to getRecord
1313/// 3. DID -> handle resolution: for bidirectional handle validation and in case we want to offer this
1414-use std::time::Duration;
1414+use std::time::{Duration, Instant};
1515use tokio::sync::Mutex;
1616use tokio_util::sync::CancellationToken;
1717···264264 handle: &Handle,
265265 ) -> Result<Option<Did>, IdentityError> {
266266 let key = IdentityKey::Handle(handle.clone());
267267+ metrics::counter!("slingshot_get_handle").increment(1);
267268 let entry = self
268269 .cache
269270 .get_or_fetch(&key, {
270271 let handle = handle.clone();
271272 let resolver = self.handle_resolver.clone();
272273 || async move {
273273- match resolver.resolve(&handle).await {
274274- Ok(did) => Ok(IdentityVal(UtcDateTime::now(), IdentityData::Did(did))),
275275- Err(atrium_identity::Error::NotFound) => {
276276- Ok(IdentityVal(UtcDateTime::now(), IdentityData::NotFound))
277277- }
274274+ let t0 = Instant::now();
275275+ let (res, success) = match resolver.resolve(&handle).await {
276276+ Ok(did) => (
277277+ Ok(IdentityVal(UtcDateTime::now(), IdentityData::Did(did))),
278278+ "true",
279279+ ),
280280+ Err(atrium_identity::Error::NotFound) => (
281281+ Ok(IdentityVal(UtcDateTime::now(), IdentityData::NotFound)),
282282+ "false",
283283+ ),
278284 Err(other) => {
279285 log::debug!("other error resolving handle: {other:?}");
280280- Err(IdentityError::ResolutionFailed(other))
286286+ (Err(IdentityError::ResolutionFailed(other)), "false")
281287 }
282282- }
288288+ };
289289+ metrics::histogram!("slingshot_fetch_handle", "success" => success)
290290+ .record(t0.elapsed());
291291+ res
283292 }
284293 })
285294 .await?;
···314323 did: &Did,
315324 ) -> Result<Option<PartialMiniDoc>, IdentityError> {
316325 let key = IdentityKey::Did(did.clone());
326326+ metrics::counter!("slingshot_get_did_doc").increment(1);
317327 let entry = self
318328 .cache
319329 .get_or_fetch(&key, {
320330 let did = did.clone();
321331 let resolver = self.did_resolver.clone();
322332 || async move {
323323- match resolver.resolve(&did).await {
324324- Ok(did_doc) => {
333333+ let t0 = Instant::now();
334334+ let (res, success) = match resolver.resolve(&did).await {
335335+ Ok(did_doc) if did_doc.id != did.to_string() => (
325336 // TODO: fix in atrium: should verify id is did
326326- if did_doc.id != did.to_string() {
327327- return Err(IdentityError::BadDidDoc(
328328- "did doc's id did not match did".to_string(),
329329- ));
330330- }
331331- let mini_doc = did_doc.try_into().map_err(IdentityError::BadDidDoc)?;
332332- Ok(IdentityVal(UtcDateTime::now(), IdentityData::Doc(mini_doc)))
333333- }
334334- Err(atrium_identity::Error::NotFound) => {
335335- Ok(IdentityVal(UtcDateTime::now(), IdentityData::NotFound))
336336- }
337337- Err(other) => Err(IdentityError::ResolutionFailed(other)),
338338- }
337337+ Err(IdentityError::BadDidDoc(
338338+ "did doc's id did not match did".to_string(),
339339+ )),
340340+ "false",
341341+ ),
342342+ Ok(did_doc) => match did_doc.try_into() {
343343+ Ok(mini_doc) => (
344344+ Ok(IdentityVal(UtcDateTime::now(), IdentityData::Doc(mini_doc))),
345345+ "true",
346346+ ),
347347+ Err(e) => (Err(IdentityError::BadDidDoc(e)), "false"),
348348+ },
349349+ Err(atrium_identity::Error::NotFound) => (
350350+ Ok(IdentityVal(UtcDateTime::now(), IdentityData::NotFound)),
351351+ "false",
352352+ ),
353353+ Err(other) => (Err(IdentityError::ResolutionFailed(other)), "false"),
354354+ };
355355+ metrics::histogram!("slingshot_fetch_did_doc", "success" => success)
356356+ .record(t0.elapsed());
357357+ res
339358 }
340359 })
341360 .await?;
+13-4
slingshot/src/server.rs
···99use std::path::PathBuf;
1010use std::str::FromStr;
1111use std::sync::Arc;
1212+use std::time::Instant;
1213use tokio_util::sync::CancellationToken;
13141415use poem::{
···609610610611 let at_uri = format!("at://{}/{}/{}", &*did, &*collection, &*rkey);
611612613613+ metrics::counter!("slingshot_get_record").increment(1);
612614 let fr = self
613615 .cache
614616 .get_or_fetch(&at_uri, {
615617 let cid = cid.clone();
616618 let repo_api = self.repo.clone();
617617- || async move { repo_api.get_record(&did, &collection, &rkey, &cid).await }
619619+ || async move {
620620+ let t0 = Instant::now();
621621+ let res = repo_api.get_record(&did, &collection, &rkey, &cid).await;
622622+ let success = if res.is_ok() { "true" } else { "false" };
623623+ metrics::histogram!("slingshot_fetch_record", "success" => success)
624624+ .record(t0.elapsed());
625625+ res
626626+ }
618627 })
619628 .await;
620629···690699 }
691700692701 // TODO
693693- // #[oai(path = "/com.atproto.identity.resolveHandle", method = "get")]
694702 // #[oai(path = "/com.atproto.identity.resolveDid", method = "get")]
695703 // but these are both not specified to do bidirectional validation, which is what we want to offer
696704 // com.atproto.identity.resolveIdentity seems right, but requires returning the full did-doc
···699707 // handle -> verified did + pds url
700708 //
701709 // we could do horrible things and implement resolveIdentity with only a stripped-down fake did doc
702702- // but this will *definitely* cause problems because eg. we're not currently storing pubkeys and
703703- // those are a little bit important
710710+ // but this will *definitely* cause problems probably
711711+ //
712712+ // resolveMiniDoc gets most of this well enough.
704713}
705714706715#[derive(Debug, Clone, Serialize)]