~ knotless Search/
↑↓ move↵ open⇧↵ all results

grain.social/grain

photography-focused photo sharing platform

grain.social
1 collaborator
0
2

The web proxy, deletes and the cross-post test land together, checked once

Merged
Merged chadtmiller.com asked to merge into feature/rust from grain.social/grain:batch/two

What changes

Three pulls into feature/rust, each approved by review and passing every check at its head with feature/rust at 92c202b merged in, merged together so the runner checks them once:

Merging this fast-forwards feature/rust over each pull's own commits.

One fix on the batch itself, as its review asked (d071e0b, 48472f4, 7d031dc): an account Jetstream says was deleted is purged only when something says it is gone. accounts::gone answers one of three things (crates/grain-serve/src/accounts.rs):

  • Gone: the host refuses getRepoStatus with RepoNotFound, or answers active: false with status: deleted, or PLC (or the did:web host) answers 404 or 410.
  • Active: the host answers active: true (the account moved there), or active: false with any other status, such as deactivated, suspended or taken down. Those can be undone, and the account is still at its host, so it is kept and logged.
  • Unknown: a transport error, a 5xx, a DID document that names no PDS, any other refusal, or a status with no active.

On Unknown, deletions gives each account its own task (settle), which asks again with doubling waits (Patience::default(): six asks, 30 s at first, capped at 10 min, about half an hour). Still Unknown after that, it logs an error and keeps everything.

Why

Every merge into feature/rust moves its base, and a pull merges only with checks against the base as it is; one at a time costs a runner cycle each.

How it was checked

git merged web and cross-post on its own. deletes conflicted only in the crate doc of crates/grain-serve/src/lib.rs, where web named pages and deletes named gallery and accounts. The resolution names all three. Nothing was built here; the checks run on the runner.

The fix: crates/grain-serve/tests/gone.rs runs a stand-in PLC directory on loopback that is also each account's host.

  • gone answers Active for an active, a deactivated and a suspended account.
  • It answers Gone for status deleted, RepoNotFound, a 410 and a 404.
  • It answers Unknown for PLC answering 503, the host answering 502, a host nobody listens on, a status with no active, and a document that names no PDS.
  • settle keeps an active account and a deactivated one, preferences and all; it purges one whose PLC fails once and then says deleted; it gives up on one PLC never answers, keeping its state.
  • deletions handles a tombstone and an unanswerable account at once.

tests/deletes.rs checks gone against the reference PDS: an account there is Active, and a DID its PLC never heard of is Gone.

What deletes' review left unread was read before batching:

  • Index::purge leaves the takedown table, labels and reports alone.
  • untrack deletes only the repo row, which holds no admin intent: takedowns are their own table.
  • deleteAccount refuses any did but the caller's.
  • The app path forgets an account only once its PDS holds no grain records.

Shortcomings

  • The waits live in memory. A restart part way through drops the accounts still waiting, and they are kept. An account whose PLC or host stays unreadable for the whole half hour is never purged by that event; a later deleted event, or an admin, has to do it.
  • knotless's deleted() (knotless-serve/src/accounts.rs) has the shape grain had: an identity that fails to resolve, or a status call that fails, counts as deleted (active != Some(true)), and so does any inactive status. It should get the same fix.

Review

New on the batch: the doc resolution in lib.rs, and the accounts::gone fix with tests/gone.rs.

🤖 Generated with Claude Code

chadtmiller.com added 25 commits
Asked for changes

This batches the web proxy, cross-post and deletes pulls onto feature/rust. The lib.rs crate doc resolution is correct: it names pages, gallery and accounts. The checks on this head are still queued or running, so I can't confirm that it passes.

One thing must change before this merges. The description admits that accounts::gone treats an identity that won't resolve, or a failed getRepoStatus, as gone. On that path deletions then calls deleted, which purges preferences, push tokens, mutes, story views and invites. Nothing rebuilds those. A transient PLC or network failure after a Jetstream deletion event is enough to lose them for an account that moved or still exists.

I read accounts.rs:83-91 and the code does what the description says. This is data loss that can't be undone, and the fix is small. Deferring it to a later pull means a known destructive bug sits on feature/rust in the meantime. Fix it here, or leave the deletions path out of this batch.

The fix is to make gone three-valued, for example Gone, Active and Unknown, with only an explicit inactive or deleted status counting as gone. On Unknown, deletions should retry later and not purge. Please add a test for the failing-lookup case.

crates/grain-serve/src/accounts.rslines 83 to 91Outdated
pub async fn gone(ctx: &Ctx, did: &str) -> bool {
let Ok(pds) = grain::identity::resolve_pds(ctx, did).await else {
return true;
};
let xrpc = Xrpc::new(ctx.guarded.clone(), &pds);
let asked = [("did", did)];
let status = xrpc.query::<Value>("com.atproto.sync.getRepoStatus", &asked, None);
let active = status.await.ok().and_then(|s| s["active"].as_bool());
active != Some(true)
review.knotless.dev

A failed resolve_pds or getRepoStatus returns gone here, and deletions then purges state that nothing rebuilds. Treat "could not ask" as unknown and retry later. Only an explicit active: false or a deleted status should count as gone.

chadtmiller.com

Fixed on this batch at 48472f4 (d071e0b, then the names). accounts::gone now answers Asked::Gone, Active or Unknown. Only the host's active: false or RepoNotFound, or PLC (or the did:web host) answering 410 or 404, is Gone. A transport error, a 5xx, a document naming no PDS, any other refusal, or a status without active is Unknown. deletions gives each account its own task (settle), asking again with doubling waits (Patience::default(): six asks, 30 s at first, capped at 10 min) and, still Unknown, logs and keeps everything. Tests: crates/grain-serve/tests/gone.rs (a stand-in PLC that is each account's host: every answer, a PLC that fails once then answers, one that never does, and deletions over both), and tests/deletes.rs against the reference PDS. knotless's own deleted() has the lenient shape, so this one is grain's, and worth carrying back.

Approved

My earlier round asked for accounts::gone to be three-valued, for deletions to retry on unknown, and for a test of the failing-lookup case. All three are done at 48472f4.

  • gone: Asked::Gone now needs an explicit sign: active: false, RepoNotFound, or a 404/410 from the identity lookup. A transport error, a 5xx, a document naming no PDS, any other refusal, or a status without active is Unknown. I read crates/grain-serve/src/accounts.rs.
  • settle and deletions: deletions gives each account its own settle task. settle asks again with doubling waits, six asks at most. It purges only on Gone and keeps everything on Active or a final Unknown. One slow account doesn't hold up the others.
  • Tests: the author says tests/gone.rs and tests/deletes.rs cover the failing, flaky and never-answering lookups. I didn't open them.

Checks: at this head lexicons passes and check passes. rust, harness and test were still queued or running when I looked. Don't merge until they pass.

Approved

This batches the web proxy, cross-post test and deletes pulls onto feature/rust. The one new commit since my approval at 48472f4 is 7d031dc, and I read it.

It narrows Asked::Gone further. gone in crates/grain-serve/src/accounts.rs now returns Gone only for active: false with status: deleted, RepoNotFound, or a 404/410 from the identity lookup. A deactivated, suspended or taken-down account counts as Active and is kept, with a log line. Those states can be undone, so keeping the account is right. This is the safer side of the earlier data-loss concern, and settle and deletions are unchanged.

I didn't open the tests. The author says tests/gone.rs covers the deactivated and suspended cases, and I'm relying on that.

Checks: at this head lexicons and check were not yet reported as passing. check and harness are running, and rust, test and lexicons were still queued when I looked. Don't merge until all of them pass.

Merged as 7d031dc
Part of a stack of 2
  1. The web proxy, deletes and the cross-post test land together, checked once Landed as 7d031dc
  2. main