feat(appview): PDS-Firehose konsumieren
Gegenstück zum subscribeRepos-Endpoint: WebSocket-Consumer mit persistiertem seq-Cursor, Reconnect-Backoff und Behandlung von #info/OutdatedCursor. Eigene Cursor-Tabelle statt einer Zeile in jetstream_cursor: dort steht ein time_us in der Größenordnung 1.7e15, die seq ist ein kleiner Zähler ab 1. Geteilt hätte GREATEST den PDS-Cursor sofort in eine Zukunft geschoben, die die PDS nie erreicht. Kein neuer Indexer-Pfad — jede Op wird in die Single-Op-Form übersetzt, die apply_commit schon vom Jetstream kennt. Push und Firehose liefern denselben Commit doppelt; das ist unkritisch, weil die Schreibpfade Upserts sind und der Dedupe-Index der Notifications den Rest abfängt. Mit einem Test festgehalten statt vorausgesetzt. Der CAR-Reader ist neu (es gab nur einen Writer, und der liegt in einem Binary-Crate ohne lib-Target). Der CBOR-Reader arbeitet mit explizitem Offset, weil ein Frame zwei hintereinander geschriebene Werte sind, und akzeptiert CID-Links in beiden Schreibweisen — die Blöcke tragen Strings. /healthz meldet beide Ströme getrennt; sie fallen unabhängig voneinander aus. Verifiziert mit totem Push-Ziel: der Post kam trotzdem an. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013HC9HLrUU1LNwkzp8nkDLX
This commit is contained in:
co-authored by
Claude Opus 5
parent
d6947c2576
commit
124a90dc07
@@ -18,6 +18,21 @@ fn default_auth_required() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
/// Default for `PDS_FIREHOSE_ENABLED`.
|
||||
///
|
||||
/// `true` — the AppView consumes the local PDS's
|
||||
/// `com.atproto.sync.subscribeRepos` stream. That stream is the only
|
||||
/// *guaranteed* path for a local user's own records: the fast
|
||||
/// `POST /internal/ingest-commit` push is best effort, and the public
|
||||
/// Jetstream never sees this PDS, so a lost push means a permanently
|
||||
/// missing post. Defaulting to on means an operator who never heard of
|
||||
/// the variable gets the durable behaviour; switching it off is the
|
||||
/// explicit choice (e.g. a PDS too old to serve the endpoint, or a
|
||||
/// second AppView instance that should not double-index).
|
||||
fn default_pds_firehose_enabled() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
pub pds_host: String,
|
||||
@@ -78,6 +93,12 @@ pub struct AppConfig {
|
||||
/// `APPVIEW_CORS_ORIGINS=tauri://localhost,http://127.0.0.1:1430`
|
||||
#[serde(default)]
|
||||
pub appview_cors_origins: Vec<String>,
|
||||
/// Whether the AppView subscribes to the local PDS firehose
|
||||
/// (`com.atproto.sync.subscribeRepos` on
|
||||
/// [`AppConfig::pds_base_url`]). Default `true` — see
|
||||
/// [`default_pds_firehose_enabled`] for why.
|
||||
#[serde(default = "default_pds_firehose_enabled")]
|
||||
pub pds_firehose_enabled: bool,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
@@ -122,6 +143,10 @@ impl AppConfig {
|
||||
.ok()
|
||||
.map(|s| parse_csv_env(&s))
|
||||
.unwrap_or_default(),
|
||||
pds_firehose_enabled: std::env::var("PDS_FIREHOSE_ENABLED")
|
||||
.ok()
|
||||
.map(|s| parse_bool_env(&s))
|
||||
.unwrap_or_else(default_pds_firehose_enabled),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user