Skip to content

Commit f427071

Browse files
committed
retry upstream getRecord without CID if NotFound
1 parent aa3f28b commit f427071

File tree

3 files changed

+57
-21
lines changed

3 files changed

+57
-21
lines changed

slingshot/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::ErrorResponseObject;
12
use thiserror::Error;
23

34
#[derive(Debug, Error)]
@@ -75,4 +76,8 @@ pub enum RecordError {
7576
MissingUpstreamCid,
7677
#[error("upstream CID was not valid: {0}")]
7778
BadUpstreamCid(String),
79+
#[error("upstream atproto-looking bad request")]
80+
UpstreamBadRequest(ErrorResponseObject),
81+
#[error("upstream non-atproto bad request")]
82+
UpstreamBadBadNotGoodRequest(reqwest::Error),
7883
}

slingshot/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ mod server;
88
pub use consumer::consume;
99
pub use firehose_cache::firehose_cache;
1010
pub use identity::Identity;
11-
pub use record::{CachedRecord, Repo};
11+
pub use record::{CachedRecord, ErrorResponseObject, Repo};
1212
pub use server::serve;

slingshot/src/record.rs

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use crate::{Identity, error::RecordError};
44
use atrium_api::types::string::{Cid, Did, Nsid, RecordKey};
5-
use reqwest::Client;
5+
use reqwest::{Client, StatusCode};
66
use serde::{Deserialize, Serialize};
77
use serde_json::value::RawValue;
88
use std::str::FromStr;
@@ -56,6 +56,13 @@ struct RecordResponseObject {
5656
value: Box<RawValue>,
5757
}
5858

59+
#[derive(Debug, Deserialize)]
60+
pub struct ErrorResponseObject {
61+
error: String,
62+
#[allow(dead_code)]
63+
message: String,
64+
}
65+
5966
#[derive(Clone)]
6067
pub struct Repo {
6168
identity: Identity,
@@ -87,39 +94,63 @@ impl Repo {
8794
return Err(RecordError::NotFound("could not get pds for DID"));
8895
};
8996

90-
// TODO: throttle by host probably, generally guard against outgoing requests
97+
// cid gets set to None for a retry, if it's Some and we got NotFound
98+
let mut cid = cid;
9199

92-
let mut params = vec![
93-
("repo", did.to_string()),
94-
("collection", collection.to_string()),
95-
("rkey", rkey.to_string()),
96-
];
97-
if let Some(cid) = cid {
98-
params.push(("cid", cid.as_ref().to_string()));
99-
}
100-
let mut url = Url::parse_with_params(&pds, &params)?;
101-
url.set_path("/xrpc/com.atproto.repo.getRecord");
100+
let res = loop {
101+
// TODO: throttle outgoing requests by host probably, generally guard against outgoing requests
102+
let mut params = vec![
103+
("repo", did.to_string()),
104+
("collection", collection.to_string()),
105+
("rkey", rkey.to_string()),
106+
];
107+
if let Some(cid) = cid {
108+
params.push(("cid", cid.as_ref().to_string()));
109+
}
110+
let mut url = Url::parse_with_params(&pds, &params)?;
111+
url.set_path("/xrpc/com.atproto.repo.getRecord");
102112

103-
let res = self
104-
.client
105-
.get(url)
106-
.send()
107-
.await
108-
.map_err(RecordError::SendError)?
113+
let res = self
114+
.client
115+
.get(url.clone())
116+
.send()
117+
.await
118+
.map_err(RecordError::SendError)?;
119+
120+
if res.status() == StatusCode::BAD_REQUEST {
121+
// 1. if we're not able to parse json, it's not something we can handle
122+
let err = res
123+
.json::<ErrorResponseObject>()
124+
.await
125+
.map_err(RecordError::UpstreamBadBadNotGoodRequest)?;
126+
// 2. if we are, is it a NotFound? and if so, did we try with a CID?
127+
// if so, retry with no CID (api handler will reject for mismatch but
128+
// with a nice error + warm cache)
129+
if err.error == "NotFound" && cid.is_some() {
130+
cid = &None;
131+
continue;
132+
} else {
133+
return Err(RecordError::UpstreamBadRequest(err));
134+
}
135+
}
136+
break res;
137+
};
138+
139+
let data = res
109140
.error_for_status()
110141
.map_err(RecordError::StatusError)? // TODO atproto error handling (think about handling not found)
111142
.json::<RecordResponseObject>()
112143
.await
113144
.map_err(RecordError::ParseJsonError)?; // todo...
114145

115-
let Some(cid) = res.cid else {
146+
let Some(cid) = data.cid else {
116147
return Err(RecordError::MissingUpstreamCid);
117148
};
118149
let cid = Cid::from_str(&cid).map_err(|e| RecordError::BadUpstreamCid(e.to_string()))?;
119150

120151
Ok(CachedRecord::Found(RawRecord {
121152
cid,
122-
record: res.value.to_string(),
153+
record: data.value.to_string(),
123154
}))
124155
}
125156
}

0 commit comments

Comments
 (0)