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
[db,appwide] add poisoning checks on errors and crash early
ptr.pet
1 month ago
f3035d76
9eb1bd6b
verified
This commit was signed with the committer's
known signature
.
ptr.pet
SSH Key Fingerprint:
SHA256:Abmvag+juovVufZTxyWY8KcVgrznxvBjQpJesv071Aw=
+40
-7
5 changed files
expand all
collapse all
unified
split
src
backfill
manager.rs
mod.rs
db
mod.rs
ingest
mod.rs
main.rs
+3
src/backfill/manager.rs
···
66
66
.into_diagnostic()
67
67
.unwrap_or_else(|e| {
68
68
warn!("failed to scan errors: {e}");
69
69
+
Db::check_poisoned_report(&e);
69
70
Ok(Vec::new())
70
71
})
71
72
.unwrap_or_else(|e| {
72
73
warn!("failed to scan errors: {e}");
74
74
+
Db::check_poisoned_report(&e);
73
75
Vec::new()
74
76
});
75
77
···
86
88
// move back to pending
87
89
if let Err(e) = Db::insert(db.pending.clone(), key, Vec::new()).await {
88
90
warn!("failed to move {did} to pending: {e}");
91
91
+
Db::check_poisoned_report(&e);
89
92
continue;
90
93
}
91
94
+5
-1
src/backfill/mod.rs
···
66
66
did.clone(),
67
67
permit,
68
68
)
69
69
-
.inspect_err(move |e| error!("backfill process failed for {did}: {e}")),
69
69
+
.inspect_err(move |e| {
70
70
+
error!("backfill process failed for {did}: {e}");
71
71
+
Db::check_poisoned_report(e);
72
72
+
}),
70
73
);
71
74
}
72
75
}
···
405
408
406
409
if let Err(e) = ops::apply_commit(&state.db, &commit, true) {
407
410
error!("failed to apply buffered commit for {did}: {e}");
411
411
+
Db::check_poisoned_report(&e);
408
412
}
409
413
410
414
// delete from buffer
+16
-3
src/db/mod.rs
···
9
9
10
10
use std::sync::atomic::AtomicU64;
11
11
use tokio::sync::broadcast;
12
12
+
use tracing::error;
12
13
13
14
#[derive(Clone)]
14
15
pub struct Db {
···
98
99
}
99
100
100
101
pub fn persist(&self) -> Result<()> {
101
101
-
self.inner
102
102
-
.persist(PersistMode::SyncData)
103
103
-
.into_diagnostic()?;
102
102
+
self.inner.persist(PersistMode::SyncAll).into_diagnostic()?;
104
103
Ok(())
105
104
}
106
105
···
189
188
.await
190
189
.into_diagnostic()
191
190
.flatten()
191
191
+
}
192
192
+
193
193
+
pub fn check_poisoned(e: &fjall::Error) {
194
194
+
if matches!(e, fjall::Error::Poisoned) {
195
195
+
error!("!!! DATABASE POISONED !!! exiting");
196
196
+
std::process::exit(10);
197
197
+
}
198
198
+
}
199
199
+
200
200
+
pub fn check_poisoned_report(e: &miette::Report) {
201
201
+
let Some(err) = e.downcast_ref::<fjall::Error>() else {
202
202
+
return;
203
203
+
};
204
204
+
Self::check_poisoned(err);
192
205
}
193
206
}
+7
-2
src/ingest/mod.rs
···
57
57
let res = tokio::task::spawn_blocking(move || batch.commit()).await;
58
58
match res {
59
59
Ok(Ok(_)) => {}
60
60
-
Ok(Err(e)) => error!("failed to persist buffer batch: {}", e),
60
60
+
Ok(Err(e)) => {
61
61
+
Db::check_poisoned(&e);
62
62
+
error!("failed to persist buffer batch: {}", e)
63
63
+
}
61
64
Err(e) => error!("buffer worker join error: {}", e),
62
65
}
63
66
}
···
146
149
147
150
if let Err(e) = self.process_commit(&commit).await {
148
151
error!("failed to process commit {}: {e}", commit.seq);
152
152
+
Db::check_poisoned_report(&e);
149
153
// buffer for later inspection/retry
150
154
let _ = self.buffer_event(&commit).await;
151
155
}
···
241
245
.into_diagnostic()?;
242
246
243
247
if let Err(e) = res {
244
244
-
error!("failed to apply live commit for {}: {}", did_static, e);
248
248
+
error!("failed to apply live commit for {did_static}: {e}");
249
249
+
Db::check_poisoned_report(&e);
245
250
self.buffer_event(commit).await?;
246
251
} else {
247
252
debug!("synced event for {}, {} ops", did_static, commit.ops.len());
+9
-1
src/main.rs
···
56
56
57
57
if let Err(e) = crate::backfill::manager::queue_pending_backfills(&state).await {
58
58
error!("failed to queue pending backfills: {e}");
59
59
+
Db::check_poisoned_report(&e);
59
60
}
60
61
61
62
tokio::spawn({
···
113
114
.await
114
115
{
115
116
error!("failed to save cursor: {e}");
117
117
+
Db::check_poisoned_report(&e);
116
118
}
117
119
118
120
let state = state.clone();
···
121
123
match res {
122
124
Ok(Err(e)) => {
123
125
error!("db persist failed: {e}");
126
126
+
Db::check_poisoned_report(&e);
124
127
}
125
128
Err(e) => {
126
129
error!("persistence task join failed: {e}");
···
139
142
let crawler = Crawler::new(state, crawler_host);
140
143
if let Err(e) = crawler.run().await {
141
144
error!("crawler died: {e}");
145
145
+
Db::check_poisoned_report(&e);
142
146
}
143
147
}
144
148
});
···
148
152
149
153
if let Err(e) = ingestor.run().await {
150
154
error!("ingestor died: {e}");
155
155
+
Db::check_poisoned_report(&e);
151
156
}
152
157
153
153
-
state.db.persist()?;
158
158
+
if let Err(e) = state.db.persist() {
159
159
+
Db::check_poisoned_report(&e);
160
160
+
return Err(e);
161
161
+
}
154
162
155
163
Ok(())
156
164
}