Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions benchmarks/aft-search/slice-descriptors/land-flake-batch-3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"slice_class": "non_ranking",
"targeted_mechanism": "none",
"kind": "harness",
"fixtures": [
"harness-goldens"
],
"slices": [
"flake-batch-3"
],
"note": "Test-only: timing and race fixes for merge-queue flakes (generous positive waits, waits on real signals). No product search behavior changed and no changed path matches RANKING_FENCE_PREFIXES."
}
14 changes: 14 additions & 0 deletions crates/aft/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12076,6 +12076,20 @@ impl AppContext {
true
}

/// Test hook: hold back query-triggered trigram reloads for the current
/// configure generation, as the retry cooldown in
/// `search_index_query_reload_allowed` does. A test can then observe a
/// root whose saved trigram cache is valid but not resident, without its
/// own search request starting a reload whose completion races the reply.
#[doc(hidden)]
pub fn defer_query_search_index_reloads_for_test(&self) {
const HOLD: Duration = Duration::from_secs(60 * 60);
let generation = self.configure_generation();
let mut state = self.search_index_disconnect_reschedule.lock();
let replacements = if state.0 == generation { state.1 } else { 0 };
*state = (generation, replacements, Some(Instant::now() + HOLD));
}

pub(crate) fn note_search_index_load_succeeded(&self) {
let generation = self.configure_generation();
let mut state = self.search_index_disconnect_reschedule.lock();
Expand Down
19 changes: 19 additions & 0 deletions crates/aft/tests/integration/aft_search_contract_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3462,6 +3462,12 @@ fn issue_826_lane_footer_agrees_with_a_degraded_answer() {
/// The trigram lane is not resident but its saved cache is valid. Planning
/// treats that lane as still loading and answers from a filesystem scan; the
/// footer used to call it ready beside `Search status: fully degraded`.
///
/// The search request would otherwise start reloading the saved cache itself.
/// That reload usually finishes inside the request, but on a loaded machine it
/// can still be running when the footer is written, which then truthfully
/// says `trigram: building`. The test holds the reload back so the footer
/// always describes a valid cache that is not resident.
#[test]
fn issue_826_trigram_footer_agrees_with_a_scan_answer() {
let _git_env = crate::test_helpers::hermetic_git_env_guard();
Expand All @@ -3473,12 +3479,25 @@ fn issue_826_trigram_footer_agrees_with_a_scan_answer() {
let canonical = std::fs::canonicalize(project.path()).expect("canonical root");
ctx.set_canonical_cache_root(canonical.clone());
ctx.memoized_artifact_cache_key(&canonical);
ctx.defer_query_search_index_reloads_for_test();
install_ready_semantic_lane(&ctx, project.path());

let (rendered, response) =
render_search(handle_semantic_search(&request("needle_symbol"), &ctx));
handle.join().expect("embedding server thread");

assert!(
ctx.search_index_rx()
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_none()
&& ctx
.search_index()
.read()
.unwrap_or_else(std::sync::PoisonError::into_inner)
.is_none(),
"the request must not have started loading the saved trigram cache"
);
assert_eq!(response["success"], true, "{response}");
assert_eq!(response["fully_degraded"], true, "{response}");
assert!(
Expand Down
4 changes: 3 additions & 1 deletion crates/aft/tests/integration/lsp_diagnostics_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -909,8 +909,10 @@ fn move_file_reports_deleted_source_and_created_destination_for_config_file() {
let json = serde_json::to_value(&response).expect("response serializes");
assert_eq!(json["success"], true, "move failed: {json}");

// Both notifications arrive asynchronously from the move; a loaded runner
// can deliver the second one well after 2 s, so wait generously for it.
let mut events = drain_watched_file_events_from_ctx(&ctx);
let deadline = Instant::now() + Duration::from_secs(2);
let deadline = Instant::now() + Duration::from_secs(30);
while events.len() < 2 && Instant::now() < deadline {
thread::sleep(Duration::from_millis(25));
events.extend(drain_watched_file_events_from_ctx(&ctx));
Expand Down
Loading