fix(at-firehose): Jetstream-Filter wirkte nie — Collections in die URL
Der Consumer verband sich auf die nackte URL und schickte danach
`{"type":"options","wantedCollections":[…]}` als Textframe. Jetstream
ignoriert das, und zwar stillschweigend: Filter sind Query-Parameter, und
der einzige nachrichtenbasierte Weg (`options_update`) verlangt, dass die
Verbindung mit `requireHello=true` geöffnet wurde.
Jede Instanz, die glaubte, sechs Collections zu abonnieren, hat also den
kompletten öffentlichen Firehose gezogen. Gemessen gegen
jetstream1.us-east: 3119 Events in 8 s ungefiltert, 520 für eine einzelne
Collection, 12 für die beiden, die dieses Projekt wirklich braucht.
Konkrete Folgen: die Dev-Datenbank ist unbemerkt auf 3,3 Mio. Posts
gewachsen, und auf der Produktionsinstanz musste die AppView abgeschaltet
und aus dem Autostart genommen werden, weil sie die Platte vollzuschreiben
drohte — dort stand `JETSTREAM_COLLECTIONS=app.twi.post` korrekt in der
.env und wurde einfach nicht beachtet.
Was der Fix NICHT löst, und das steht auch so im Code: die Collections, die
ein Bluesky-artiges Produkt normalerweise will (post/like/repost/follow),
sind ~97 % des Volumens. Richtig zu filtern ist notwendig, nicht
hinreichend.
Nebenbei: upload_blob_rejects_oversized fiel etwa jeden dritten Lauf um. Der
Server bricht die Verbindung ab, sobald das Body-Limit reißt, also sieht der
Client je nach Timing die 413 oder einen Reset beim Schreiben. Beides
beweist, dass der Upload abgelehnt wurde; der Test akzeptiert jetzt beides.
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
b7ce114677
commit
b58cb75cfe
@@ -314,19 +314,32 @@ async fn upload_blob_rejects_oversized() {
|
||||
// 413 from axum's body extractor.
|
||||
let payload = vec![0u8; 2 * 1024 * 1024];
|
||||
|
||||
let resp = c
|
||||
// Two legitimate outcomes, and which one happens is a race the test
|
||||
// cannot win: the limit trips while the client is still writing the
|
||||
// 2 MiB body. If the rejection reaches the socket first, the client
|
||||
// reads `413`; if the server closes its side first, the client's
|
||||
// write fails with a connection reset and never gets to read a
|
||||
// status. Asserting only on `413` made this test fail roughly one run
|
||||
// in three. What actually matters — and what both outcomes prove — is
|
||||
// that the upload was refused rather than accepted.
|
||||
match c
|
||||
.post(format!("{}/xrpc/com.atproto.uploadBlob", PDS_URL))
|
||||
.bearer_auth(&jwt)
|
||||
.header("Content-Type", "image/png")
|
||||
.body(payload)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
resp.status().as_u16(),
|
||||
413,
|
||||
"oversized upload must return 413"
|
||||
);
|
||||
{
|
||||
Ok(resp) => assert_eq!(
|
||||
resp.status().as_u16(),
|
||||
413,
|
||||
"oversized upload must be refused with 413"
|
||||
),
|
||||
Err(e) => assert!(
|
||||
e.is_request(),
|
||||
"the only acceptable error is the server hanging up mid-body, got {e:?}"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// `com.atproto.uploadBlob` rejects requests with no `Authorization`
|
||||
|
||||
Reference in New Issue
Block a user