From 1e0bdfbe2cf5f56bff198059f63e85f7e3526768 Mon Sep 17 00:00:00 2001 From: dawn <90008@klbr.net> Date: Mon, 5 Oct 2026 21:18:53 +0300 Subject: [PATCH] [backfill] bring back a repo a successful fetch shows is active keeping what changed during a fetch also kept a repo that was marked inactive before it, say after a missed reactivation and an operator resync, inactive for good, where BackfillFinished used to make it synced. a pds only hands over an active account's repo, so a backfill that got one now writes the account as active, unless the row's account changed during the fetch, which is newer than what the fetch saw. --- src/backfill/mod.rs | 17 +++++++++--- src/backfill/worker/task.rs | 52 +++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/backfill/mod.rs b/src/backfill/mod.rs index dd432eb..6053618 100644 --- a/src/backfill/mod.rs +++ b/src/backfill/mod.rs @@ -13,7 +13,7 @@ use jacquard_common::types::did::Did; use miette::IntoDiagnostic; use crate::db::{Db, keys}; -use crate::types::RepoState; +use crate::types::{RepoState, RepoStatus}; /// what writing a fetched repo came to pub(crate) enum Persisted { @@ -30,8 +30,7 @@ pub(crate) enum Persisted { /// the repo state a backfill writes, read under the repo lock its persist holds. `after` is /// `before` plus the doc resolved before the fetch and the fetched root, put onto the row as -/// it is now so a status change made during the fetch survives. that doc is older than any -/// identity written during the fetch, so such an identity stays too. `None` if the row is gone +/// it is now so a change made during the fetch survives. `None` if the row is gone pub(crate) fn backfilled_state( db: &Db, did: &Did, @@ -42,8 +41,20 @@ pub(crate) fn backfilled_state( return Ok(None); }; let current = crate::db::deser_repo_state(&bytes)?.into_static(); + // a pds only hands over an active account's repo, so one marked inactive before the + // fetch came back + if !after.active { + after.active = true; + after.status = RepoStatus::Desynchronized; + } + // the doc and the pds both answered when the fetch started, so an identity or account + // change written during it is newer and stays if current.identity() != before.identity() { after.set_identity(current.identity()); } + if (current.active, ¤t.status) != (before.active, &before.status) { + after.active = current.active; + after.status = current.status.clone(); + } Ok(Some(after.rebased(before, current))) } diff --git a/src/backfill/worker/task.rs b/src/backfill/worker/task.rs index 5263316..e87174d 100644 --- a/src/backfill/worker/task.rs +++ b/src/backfill/worker/task.rs @@ -1485,6 +1485,58 @@ mod tests { Ok(()) } + #[tokio::test] + async fn a_backfill_brings_back_a_repo_marked_inactive_before_it() -> miette::Result<()> { + let fixture = Fixture::new().await?; + // say a reactivation went by while hydrant was down + let mut seeded = rooted("3jzfcijpj2z2a")?; + seeded.active = false; + seeded.status = RepoStatus::Deactivated; + fixture.seed(&seeded, 7, &[])?; + let body = post("a"); + let car = build_car("3jzfcijpj2z2b", &[("a", body.as_slice())]).await?; + fixture.pds.serve_car(car.bytes).await; + + assert_eq!( + fixture.run(keys::pending_key(7)).await?, + TaskDisposition::Finished + ); + let after = fixture.repo()?; + assert!(after.active); + assert_eq!(after.status, RepoStatus::Desynchronized); + assert_eq!(fixture.record_rkeys()?, ["a"]); + Ok(()) + } + + #[tokio::test] + async fn a_status_written_during_the_fetch_beats_what_the_fetch_saw() -> miette::Result<()> { + let fixture = Fixture::new().await?; + let mut seeded = rooted("3jzfcijpj2z2a")?; + seeded.active = false; + seeded.status = RepoStatus::Deactivated; + fixture.seed(&seeded, 7, &[])?; + let body = post("a"); + let car = build_car("3jzfcijpj2z2b", &[("a", body.as_slice())]).await?; + fixture.pds.serve_car(car.bytes).await; + + let result = fixture + .run_changing(keys::pending_key(7), async { + let mut row = fixture.repo()?; + row.status = RepoStatus::Takendown; + fixture.put_repo(&row) + }) + .await?; + + assert!( + matches!(result, Ok(TaskDisposition::Finished)), + "{result:?}" + ); + let after = fixture.repo()?; + assert!(!after.active); + assert_eq!(after.status, RepoStatus::Takendown); + Ok(()) + } + #[tokio::test] async fn an_identity_written_during_the_fetch_outlives_the_backfills_doc() -> miette::Result<()> { -- 2.51.2