From 1b0fdbf1f5beec943f9732ed236e51a20ecb75c0 Mon Sep 17 00:00:00 2001 From: callumalpass Date: Sat, 3 Oct 2026 10:39:59 +1000 Subject: [PATCH] Accept the recovery worker's projection when it supersedes the test driver writes_with_a_current_projection_reject_duplicate_unique_values installs a type pack, which schedules recovery; its projection worker can supersede or lease the generation the test drives. The driver retried only provider_database_retryable, so CI failed with projection_generation_not_building, including on PRs that change nothing here (e.g. #502). Handle the hand-off as the provider's own bootstrap does: accept a projection the worker made current, otherwise start a fresh generation. --- .../tests/hosted_write_validation.rs | 51 ++++++++++++------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/crates/connect-hosted-provider/tests/hosted_write_validation.rs b/crates/connect-hosted-provider/tests/hosted_write_validation.rs index 25baa0403..003f12a20 100644 --- a/crates/connect-hosted-provider/tests/hosted_write_validation.rs +++ b/crates/connect-hosted-provider/tests/hosted_write_validation.rs @@ -269,28 +269,41 @@ async fn projection_is_current(fixture: &FileLifecycleFixture) -> bool { } async fn complete_projection(fixture: &FileLifecycleFixture) { - let generation = fixture - .provider - .start_projection_generation(fixture.collection_id) - .await - .unwrap(); - for _ in 0..64 { - let batch = match fixture + // Type-pack installation also schedules recovery, and its projection worker + // races this driver: it may supersede or lease the generation started here. + // As the provider's own bootstrap does, treat that as a hand-off: accept the + // projection the worker made current, or start a fresh generation. + for _ in 0..8 { + let generation = fixture .provider - .advance_projection_generation(fixture.collection_id, generation.generation_id) + .start_projection_generation(fixture.collection_id) .await - { - Ok(batch) => batch, - // Type-pack installation also schedules recovery. Its projection - // worker can race this driver; retry only the declared DB conflict. - Err(error) if error.code == "provider_database_retryable" => { - tokio::time::sleep(std::time::Duration::from_millis(100)).await; - continue; + .unwrap(); + for _ in 0..64 { + match fixture + .provider + .advance_projection_generation(fixture.collection_id, generation.generation_id) + .await + { + Ok(batch) if batch.generation.status == "complete" => return, + Ok(_) => {} + Err(error) if error.code == "provider_database_retryable" => { + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + } + Err(error) + if matches!( + error.code.as_str(), + "projection_lease_unavailable" | "projection_generation_not_building" + ) => + { + if projection_is_current(fixture).await { + return; + } + tokio::time::sleep(std::time::Duration::from_millis(100)).await; + break; + } + Err(error) => panic!("projection advance failed: {error:?}"), } - Err(error) => panic!("projection advance failed: {error:?}"), - }; - if batch.generation.status == "complete" { - return; } } panic!("the projection generation did not complete");