tangled
alpha
login
or
join now
ptr.pet
/
hydrant
28
fork
atom
at protocol indexer with flexible filtering, xrpc queries, and a cursor-backed event stream, built on fjall
at-protocol
atproto
indexer
rust
fjall
28
fork
atom
overview
issues
6
pulls
pipelines
[types] use Did for did's in event types
ptr.pet
1 month ago
85291e9d
61539b51
verified
This commit was signed with the committer's
known signature
.
ptr.pet
SSH Key Fingerprint:
SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw=
+41
-33
6 changed files
expand all
collapse all
unified
split
src
backfill
mod.rs
buffer
processor.rs
db
keys.rs
ingest
mod.rs
ops.rs
types.rs
+3
-3
src/backfill/mod.rs
···
4
4
use crate::types::{AccountEvt, BroadcastEvent, RepoState, RepoStatus, ResyncState, StoredEvent};
5
5
use futures::TryFutureExt;
6
6
use jacquard::api::com_atproto::sync::get_repo::{GetRepo, GetRepoError};
7
7
-
use jacquard::prelude::*;
8
7
use jacquard::types::did::Did;
8
8
+
use jacquard::{prelude::*, IntoStatic};
9
9
use jacquard_common::xrpc::XrpcError;
10
10
use jacquard_repo::mst::Mst;
11
11
use jacquard_repo::MemoryBlockStore;
···
237
237
238
238
let emit_identity = |status: &RepoStatus| {
239
239
let evt = AccountEvt {
240
240
-
did: did.as_str().into(),
240
240
+
did: did.clone(),
241
241
active: !matches!(
242
242
status,
243
243
RepoStatus::Deactivated | RepoStatus::Takendown | RepoStatus::Suspended
···
403
403
app_state.db.next_event_id.fetch_add(1, Ordering::SeqCst);
404
404
let evt = StoredEvent::Record {
405
405
live: false,
406
406
-
did: did.as_str().into(),
406
406
+
did: did.clone().into_static(),
407
407
rev: rev.as_str().into(),
408
408
collection: collection.into(),
409
409
rkey: rkey.into(),
+5
-8
src/buffer/processor.rs
···
40
40
let mut to_remove: Vec<Did<'static>> = Vec::new();
41
41
42
42
loop {
43
43
-
// receive new messages (non-blocking drain)
44
43
while let Ok(msg) = self.rx.try_recv() {
45
44
queues.entry(msg.did.clone()).or_default().push_back(msg);
46
45
}
47
46
48
48
-
// process unblocked DIDs
49
47
for (did, queue) in &mut queues {
50
48
if self.state.blocked_dids.contains_sync(did) {
51
49
continue;
···
98
96
debug!("processing buffered identity for {did}");
99
97
let handle = identity.handle.as_ref().map(|h| h.as_str().to_smolstr());
100
98
let evt = IdentityEvt {
101
101
-
did: did.to_smolstr(),
99
99
+
did: did.clone(),
102
100
handle,
103
101
};
104
102
ops::emit_identity_event(&self.state.db, evt);
···
106
104
SubscribeReposMessage::Account(account) => {
107
105
debug!("processing buffered account for {did}");
108
106
let evt = AccountEvt {
109
109
-
did: did.to_smolstr(),
107
107
+
did: did.clone(),
110
108
active: account.active,
111
109
status: account.status.as_ref().map(|s| s.to_smolstr()),
112
110
};
113
111
ops::emit_account_event(&self.state.db, evt);
114
112
113
113
+
let did = did.clone();
115
114
let state = self.state.clone();
116
116
-
let did = did.clone();
117
117
-
let account = account.clone(); // Account is 'static in BufferedMessage
115
115
+
let account = account.clone();
118
116
119
117
tokio::task::spawn_blocking(move || -> Result<()> {
120
120
-
// handle status updates
121
118
if !account.active {
122
119
use jacquard::api::com_atproto::sync::subscribe_repos::AccountStatus;
123
120
if let Some(status) = &account.status {
···
208
205
.flatten()
209
206
}
210
207
211
211
-
async fn remove_from_db_buffer(&self, did: &str, buffered_at: i64) -> Result<()> {
208
208
+
async fn remove_from_db_buffer(&self, did: &Did<'_>, buffered_at: i64) -> Result<()> {
212
209
let key = keys::buffer_key(did, buffered_at);
213
210
self.state.db.buffer.remove(key).into_diagnostic()?;
214
211
Ok(())
+2
-2
src/db/keys.rs
···
66
66
}
67
67
68
68
// key format: {DID}\x00{timestamp} (for buffer entries)
69
69
-
pub fn buffer_key(did: &str, timestamp: i64) -> Vec<u8> {
69
69
+
pub fn buffer_key(did: &Did, timestamp: i64) -> Vec<u8> {
70
70
let mut key = Vec::with_capacity(did.len() + 1 + 8);
71
71
-
key.extend_from_slice(did.as_bytes());
71
71
+
key.extend_from_slice(did_prefix(did).as_bytes());
72
72
key.push(SEP);
73
73
key.extend_from_slice(×tamp.to_be_bytes());
74
74
key
+3
-2
src/ingest/mod.rs
···
8
8
use n0_future::StreamExt;
9
9
use std::sync::atomic::Ordering;
10
10
use std::sync::Arc;
11
11
+
use std::time::Duration;
11
12
use tracing::{debug, error, info};
12
13
use url::Url;
13
14
···
62
63
Ok(s) => s,
63
64
Err(e) => {
64
65
error!("failed to connect to firehose: {e}, retrying in 5s...");
65
65
-
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
66
66
+
tokio::time::sleep(Duration::from_secs(5)).await;
66
67
continue;
67
68
}
68
69
};
···
83
84
}
84
85
85
86
error!("firehose disconnected, reconnecting in 5s...");
86
86
-
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
87
87
+
tokio::time::sleep(Duration::from_secs(5)).await;
87
88
}
88
89
}
89
90
+4
-3
src/ops.rs
···
2
2
use crate::types::{AccountEvt, BroadcastEvent, IdentityEvt, MarshallableEvt, StoredEvent};
3
3
use jacquard::api::com_atproto::sync::subscribe_repos::Commit;
4
4
use jacquard::cowstr::ToCowStr;
5
5
+
use jacquard::IntoStatic;
5
6
use jacquard_repo::car::reader::parse_car_bytes;
6
7
use miette::{IntoDiagnostic, Result};
7
8
use smol_str::{SmolStr, ToSmolStr};
···
12
13
13
14
// emitting identity is ephemeral
14
15
// we dont replay these, consumers can just fetch identity themselves if they need it
15
15
-
pub fn emit_identity_event(db: &Db, evt: IdentityEvt) {
16
16
+
pub fn emit_identity_event(db: &Db, evt: IdentityEvt<'static>) {
16
17
let event_id = db.next_event_id.fetch_add(1, Ordering::SeqCst);
17
18
let marshallable = MarshallableEvt {
18
19
id: event_id,
···
24
25
let _ = db.event_tx.send(BroadcastEvent::Ephemeral(marshallable));
25
26
}
26
27
27
27
-
pub fn emit_account_event(db: &Db, evt: AccountEvt) {
28
28
+
pub fn emit_account_event(db: &Db, evt: AccountEvt<'static>) {
28
29
let event_id = db.next_event_id.fetch_add(1, Ordering::SeqCst);
29
30
let marshallable = MarshallableEvt {
30
31
id: event_id,
···
186
187
187
188
let evt = StoredEvent::Record {
188
189
live,
189
189
-
did: did.as_str().into(),
190
190
+
did: did.clone().into_static(),
190
191
rev: commit.rev.as_str().into(),
191
192
collection: collection.into(),
192
193
rkey: rkey.into(),
+24
-15
src/types.rs
···
69
69
// from src/api/event.rs
70
70
71
71
#[derive(Debug, Serialize, Deserialize, Clone)]
72
72
-
pub struct MarshallableEvt {
72
72
+
pub struct MarshallableEvt<'i> {
73
73
pub id: u64,
74
74
#[serde(rename = "type")]
75
75
pub event_type: SmolStr,
76
76
+
#[serde(borrow)]
76
77
#[serde(skip_serializing_if = "Option::is_none")]
77
77
-
pub record: Option<RecordEvt>,
78
78
+
pub record: Option<RecordEvt<'i>>,
79
79
+
#[serde(borrow)]
78
80
#[serde(skip_serializing_if = "Option::is_none")]
79
79
-
pub identity: Option<IdentityEvt>,
81
81
+
pub identity: Option<IdentityEvt<'i>>,
82
82
+
#[serde(borrow)]
80
83
#[serde(skip_serializing_if = "Option::is_none")]
81
81
-
pub account: Option<AccountEvt>,
84
84
+
pub account: Option<AccountEvt<'i>>,
82
85
}
83
86
84
87
#[derive(Clone, Debug)]
85
88
pub enum BroadcastEvent {
86
89
Persisted(u64),
87
87
-
Ephemeral(MarshallableEvt),
90
90
+
Ephemeral(MarshallableEvt<'static>),
88
91
}
89
92
90
93
#[derive(Debug, Serialize, Deserialize, Clone)]
91
91
-
pub struct RecordEvt {
94
94
+
pub struct RecordEvt<'i> {
92
95
pub live: bool,
93
93
-
pub did: SmolStr,
96
96
+
#[serde(borrow)]
97
97
+
pub did: Did<'i>,
94
98
pub rev: SmolStr,
95
99
pub collection: SmolStr,
96
100
pub rkey: SmolStr,
···
102
106
}
103
107
104
108
#[derive(Debug, Serialize, Deserialize, Clone)]
105
105
-
pub struct IdentityEvt {
106
106
-
pub did: SmolStr,
109
109
+
pub struct IdentityEvt<'i> {
110
110
+
#[serde(borrow)]
111
111
+
pub did: Did<'i>,
107
112
#[serde(skip_serializing_if = "Option::is_none")]
108
113
pub handle: Option<SmolStr>,
109
114
}
110
115
111
116
#[derive(Debug, Serialize, Deserialize, Clone)]
112
112
-
pub struct AccountEvt {
113
113
-
pub did: SmolStr,
117
117
+
pub struct AccountEvt<'i> {
118
118
+
#[serde(borrow)]
119
119
+
pub did: Did<'i>,
114
120
pub active: bool,
115
121
#[serde(skip_serializing_if = "Option::is_none")]
116
122
pub status: Option<SmolStr>,
117
123
}
118
124
119
125
#[derive(Debug, Serialize, Deserialize, Clone)]
120
120
-
pub enum StoredEvent {
126
126
+
pub enum StoredEvent<'i> {
121
127
Record {
122
128
live: bool,
123
123
-
did: SmolStr,
129
129
+
#[serde(borrow)]
130
130
+
did: Did<'i>,
124
131
rev: SmolStr,
125
132
collection: SmolStr,
126
133
rkey: SmolStr,
127
134
action: SmolStr,
128
135
cid: Option<SmolStr>,
129
136
},
130
130
-
Identity(IdentityEvt),
131
131
-
Account(AccountEvt),
137
137
+
#[serde(borrow)]
138
138
+
Identity(IdentityEvt<'i>),
139
139
+
#[serde(borrow)]
140
140
+
Account(AccountEvt<'i>),
132
141
}