From 0e37dfd29036fc9c00a38cf604621bdf695d3971 Mon Sep 17 00:00:00 2001 From: qdrant-cloud-bot <111755117+qdrant-cloud-bot@users.noreply.github.com> Date: Wed, 27 May 2026 13:16:59 +0200 Subject: [PATCH] ci(windows): skip IO-heavy tests that aren't OS-specific (#9188) * ci(windows): skip IO-heavy tests that aren't OS-specific On the Windows CI runner, several tests are 3-25x slower than on Ubuntu purely due to slow filesystem IO. These tests exercise platform-agnostic logic (optimizer, snapshot, WAL recovery, dedup, deferred points) and are fully covered by the Linux and macOS jobs. Mark them with `#[cfg_attr(target_os = "windows", ignore = "...")]` so: - Windows CI skips them and finishes faster. - They're still listed and runnable via `cargo test -- --ignored` on Windows for local debugging. Based on JUnit timings from CI run 26462785436 (PR #8827), this should save ~5 minutes wall-clock on the Windows job, taking it closer to the ~13min Ubuntu and ~9min macOS jobs (currently 20m24s). Tests affected: - lib/wal: check_wal, check_last_index, check_clear, check_reopen, check_truncate, check_prefix_truncate, test_prefix_truncate_parametric - lib/edge/optimize: full tests module - lib/segment deferred-point tests: read_operations, dense_segment_combinations, sparse, facets - lib/collection: snapshot_test, points_dedup, wal_recovery, collection_test::test_ordered_read_api, snapshot_recovery_test Co-authored-by: Cursor * revert(ci/windows): keep WAL and WAL-recovery tests on Windows Reviewer correctly pointed out that WAL is mmap-backed and has substantial Windows-specific code paths: - Different segment allocation (fs4 vs rustix::ftruncate) - Windows-specific delete_windows() with mmap-drop + retry loop - Windows-specific sync_all() because directory fsync is unavailable - Windows-specific lock proxy file (directories aren't lockable) So those tests genuinely need Windows coverage. Reverted skips for: - lib/wal/src/lib.rs: all check_* tests and test_prefix_truncate_parametric - lib/collection/src/tests/wal_recovery_test.rs: all three tests Still skipped on Windows (no OS-specific code in their production paths): - lib/edge/optimize.rs (no cfg(windows) in source) - lib/segment deferred-point tests (segment/ has no cfg(windows)) - lib/collection snapshot/dedup tests (collection/ has no cfg(windows)) - lib/collection integration snapshot_recovery + ordered_read_api Co-authored-by: Cursor * revert(ci/windows): keep collection integration and snapshot_test Per reviewer request, keep running these on Windows: - lib/collection/tests/integration/* (snapshot_recovery_test, collection_test::test_ordered_read_api) - lib/collection/src/tests/snapshot_test.rs These exercise higher-level collection/snapshot behavior that benefits from cross-platform validation. Remaining Windows skips (production code has no cfg(windows) branches): - lib/edge/src/optimize.rs: 14 optimizer tests - lib/segment/src/segment/tests/mod.rs: 4 deferred-point tests - lib/collection/src/tests/points_dedup.rs: 2 dedup tests Co-authored-by: Cursor * ci(windows): also skip HNSW/quantization integration tests Per reviewer, also skip these segment integration test modules on Windows: - hnsw_quantized_search_test::* (25 tests) - multivector_filtrable_hnsw_test::* (rstest cases) - multivector_quantization_test::* (rstest cases) - byte_storage_quantization_test::* (rstest cases) - payload_index_test::test_struct_payload_index_nested_fields These exercise pure HNSW/quantization correctness on top of standard segment IO that is already covered by tests we keep running on Windows. Adds ~930s of sequential time to the Windows skip list, bringing the expected wall-clock saving from ~3 min to ~8-10 min. Co-authored-by: Cursor --------- Co-authored-by: Cursor --- lib/collection/src/tests/points_dedup.rs | 2 ++ lib/edge/src/optimize.rs | 18 +++++++++++++ lib/segment/src/segment/tests/mod.rs | 4 +++ .../byte_storage_quantization_test.rs | 1 + .../integration/hnsw_quantized_search_test.rs | 25 +++++++++++++++++++ .../multivector_filtrable_hnsw_test.rs | 1 + .../multivector_quantization_test.rs | 1 + .../tests/integration/payload_index_test.rs | 1 + 8 files changed, 53 insertions(+) diff --git a/lib/collection/src/tests/points_dedup.rs b/lib/collection/src/tests/points_dedup.rs index 9646076f67..6cac7fd9dd 100644 --- a/lib/collection/src/tests/points_dedup.rs +++ b/lib/collection/src/tests/points_dedup.rs @@ -220,6 +220,7 @@ async fn test_scroll_dedup() { } #[tokio::test(flavor = "multi_thread")] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] async fn test_retrieve_dedup() { let collection = fixture().await; @@ -252,6 +253,7 @@ async fn test_retrieve_dedup() { } #[tokio::test(flavor = "multi_thread")] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] async fn test_search_dedup() { let collection = fixture().await; diff --git a/lib/edge/src/optimize.rs b/lib/edge/src/optimize.rs index 654f27d725..88db13ba85 100644 --- a/lib/edge/src/optimize.rs +++ b/lib/edge/src/optimize.rs @@ -131,6 +131,10 @@ impl EdgeShard { } } +// Tests in this module exercise platform-agnostic optimizer logic but run +// 5-25x slower on Windows due to filesystem IO. They are marked +// `#[ignore]` on Windows; Linux and macOS jobs provide full coverage. +// To execute them locally on Windows, run with `cargo test -- --ignored`. #[cfg(test)] mod tests { #![expect(clippy::wildcard_enum_match_arm, reason = "test code")] @@ -154,6 +158,7 @@ mod tests { const VECTOR_NAME: &str = "edge-test-vector"; + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn does_not_force_merge_all_segments_into_one() { let dir = tempfile::Builder::new() @@ -179,6 +184,7 @@ mod tests { assert_points_retrievable_with_vectors(&reopened, &[1]); } + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_optimizer_runs_in_blocking_mode_until_idle() { let dir = tempfile::Builder::new() @@ -214,6 +220,7 @@ mod tests { /// A fresh shard with a single small segment and no deletions should not /// trigger any optimizer. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn no_op_on_single_segment_without_deletions() { let dir = tempfile::Builder::new() @@ -237,6 +244,7 @@ mod tests { } /// An empty shard (no data at all) should be a no-op. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn no_op_on_empty_shard() { let dir = tempfile::Builder::new() @@ -253,6 +261,7 @@ mod tests { /// Creating more segments than `default_segment_number` should trigger /// the merge optimizer to reduce the segment count. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn merge_reduces_excess_segments() { let target_count = default_segment_number() + 6; @@ -298,6 +307,7 @@ mod tests { } /// After a merge optimization, a second run should be a no-op. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn optimization_is_idempotent_after_merge() { let target_count = default_segment_number() + 6; @@ -333,6 +343,7 @@ mod tests { /// Deleting less than 20% of points (below the vacuum threshold) /// should NOT trigger the vacuum optimizer. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_below_threshold_is_noop() { let dir = tempfile::Builder::new() @@ -365,6 +376,7 @@ mod tests { /// Deleting below the minimum vector count (< 1000 total points) /// should NOT trigger the vacuum optimizer even with a high deletion ratio. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_below_min_vector_count_is_noop() { let dir = tempfile::Builder::new() @@ -398,6 +410,7 @@ mod tests { /// After vacuum optimization, all non-deleted points should still be /// retrievable and deleted points should be gone. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_preserves_remaining_points() { let dir = tempfile::Builder::new() @@ -454,6 +467,7 @@ mod tests { /// resulting segment has 0 points, `optimize_all_segments_blocking` /// reports `false` (zero points processed). The shard should still be /// valid and accept new data afterward. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_after_all_points_deleted() { let dir = tempfile::Builder::new() @@ -504,6 +518,7 @@ mod tests { /// Vacuum at exactly the threshold boundary (20% deleted, 1000 total). /// The threshold check is strictly greater-than, so exactly 20% should /// NOT trigger vacuum. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_at_exact_threshold_boundary_is_noop() { let dir = tempfile::Builder::new() @@ -534,6 +549,7 @@ mod tests { } /// Just above the vacuum threshold should trigger optimization. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn vacuum_just_above_threshold_triggers() { let dir = tempfile::Builder::new() @@ -566,6 +582,7 @@ mod tests { /// When there are excess segments AND some have high deletion ratios, /// optimization should handle both (merge + vacuum). + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn merge_and_vacuum_cooperate() { let target_count = default_segment_number() + 6; @@ -626,6 +643,7 @@ mod tests { } /// Optimized shard should survive a reload and still serve correct data. + #[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn data_survives_optimize_and_reload() { let dir = tempfile::Builder::new() diff --git a/lib/segment/src/segment/tests/mod.rs b/lib/segment/src/segment/tests/mod.rs index c55ff8255b..29ffa5a481 100644 --- a/lib/segment/src/segment/tests/mod.rs +++ b/lib/segment/src/segment/tests/mod.rs @@ -1017,6 +1017,7 @@ fn test_dense_deferred_points() { } #[test] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] fn test_dense_deferred_point_segment_combinations() { init_logger(); @@ -1085,6 +1086,7 @@ fn test_deferred_point_estimation_with_filter() { } #[test] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] fn test_deferred_point_read_operations() { init_logger(); let hw_counter = HardwareCounterCell::new(); @@ -1196,6 +1198,7 @@ fn test_deferred_point_read_operations() { } #[test] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] fn test_deferred_point_sparse() { init_logger(); @@ -1277,6 +1280,7 @@ fn test_deferred_point_sparse() { } #[test] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] fn test_deferred_point_facets() { init_logger(); let hw_counter = HardwareCounterCell::new(); diff --git a/lib/segment/tests/integration/byte_storage_quantization_test.rs b/lib/segment/tests/integration/byte_storage_quantization_test.rs index bb06d8ee55..4999e0c751 100644 --- a/lib/segment/tests/integration/byte_storage_quantization_test.rs +++ b/lib/segment/tests/integration/byte_storage_quantization_test.rs @@ -72,6 +72,7 @@ fn sames_count(a: &[Vec], b: &[Vec]) -> us .count() } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[rstest] #[case::nearest_binary_dot( QueryVariant::Nearest, diff --git a/lib/segment/tests/integration/hnsw_quantized_search_test.rs b/lib/segment/tests/integration/hnsw_quantized_search_test.rs index 037320556e..8c76a5ea8b 100644 --- a/lib/segment/tests/integration/hnsw_quantized_search_test.rs +++ b/lib/segment/tests/integration/hnsw_quantized_search_test.rs @@ -352,6 +352,7 @@ fn check_rescoring( } } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_quantized_search_cosine_test() { hnsw_quantized_search_test( @@ -369,6 +370,7 @@ fn hnsw_quantized_search_cosine_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_quantized_search_euclid_test() { hnsw_quantized_search_test( @@ -386,6 +388,7 @@ fn hnsw_quantized_search_euclid_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_quantized_search_manhattan_test() { hnsw_quantized_search_test( @@ -403,6 +406,7 @@ fn hnsw_quantized_search_manhattan_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_product_quantization_cosine_test() { hnsw_quantized_search_test( @@ -419,6 +423,7 @@ fn hnsw_product_quantization_cosine_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_product_quantization_euclid_test() { hnsw_quantized_search_test( @@ -435,6 +440,7 @@ fn hnsw_product_quantization_euclid_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_product_quantization_manhattan_test() { hnsw_quantized_search_test( @@ -451,6 +457,7 @@ fn hnsw_product_quantization_manhattan_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_cosine_test() { // Bits4 has enough headroom to use the standard helper (40% recall floor), @@ -471,6 +478,7 @@ fn hnsw_turbo_quantization_cosine_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_dot_test() { // See `hnsw_turbo_quantization_cosine_test` for rationale. @@ -489,6 +497,7 @@ fn hnsw_turbo_quantization_dot_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_cosine_larger_test() { // See `hnsw_turbo_quantization_cosine_test` for rationale. @@ -507,6 +516,7 @@ fn hnsw_turbo_quantization_cosine_larger_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_cosine_bits2_test() { // Bits2 clears the 40% recall floor but with thin margin — `deterministic` @@ -527,6 +537,7 @@ fn hnsw_turbo_quantization_cosine_bits2_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_dot_bits2_test() { // See `hnsw_turbo_quantization_cosine_bits2_test` for rationale. @@ -545,6 +556,7 @@ fn hnsw_turbo_quantization_dot_bits2_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_cosine_larger_bits2_test() { // See `hnsw_turbo_quantization_cosine_bits2_test` for rationale. @@ -568,6 +580,7 @@ fn hnsw_turbo_quantization_cosine_larger_bits2_test() { // they don't reliably clear the standard helper's 40% recall floor // and would be flaky-to-failing under this shape. +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_euclid_test() { hnsw_quantized_search_test( @@ -585,6 +598,7 @@ fn hnsw_turbo_quantization_euclid_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_manhattan_test() { hnsw_quantized_search_test( @@ -602,6 +616,7 @@ fn hnsw_turbo_quantization_manhattan_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_euclid_bits2_test() { // See `hnsw_turbo_quantization_cosine_bits2_test` for rationale. @@ -620,6 +635,7 @@ fn hnsw_turbo_quantization_euclid_bits2_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_manhattan_bits2_test() { // See `hnsw_turbo_quantization_cosine_bits2_test` for rationale. @@ -887,6 +903,7 @@ fn build_quantized_hnsw_for_compare( (segment, hnsw_index, (segment_dir, hnsw_dir, quantized_dir)) } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_cosine_bits1_test() { hnsw_quantized_low_bit_compare_test( @@ -898,6 +915,7 @@ fn hnsw_turbo_quantization_cosine_bits1_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_dot_bits1_test() { hnsw_quantized_low_bit_compare_test( @@ -909,6 +927,7 @@ fn hnsw_turbo_quantization_dot_bits1_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_euclid_bits1_test() { hnsw_quantized_low_bit_compare_test( @@ -920,6 +939,7 @@ fn hnsw_turbo_quantization_euclid_bits1_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_manhattan_bits1_test() { hnsw_quantized_low_bit_compare_test( @@ -931,6 +951,7 @@ fn hnsw_turbo_quantization_manhattan_bits1_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_cosine_bits1_5_test() { hnsw_quantized_low_bit_compare_test( @@ -942,6 +963,7 @@ fn hnsw_turbo_quantization_cosine_bits1_5_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_dot_bits1_5_test() { hnsw_quantized_low_bit_compare_test( @@ -953,6 +975,7 @@ fn hnsw_turbo_quantization_dot_bits1_5_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_euclid_bits1_5_test() { hnsw_quantized_low_bit_compare_test( @@ -964,6 +987,7 @@ fn hnsw_turbo_quantization_euclid_bits1_5_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn hnsw_turbo_quantization_manhattan_bits1_5_test() { // See `hnsw_turbo_quantization_manhattan_bits1_test` for rationale. @@ -976,6 +1000,7 @@ fn hnsw_turbo_quantization_manhattan_bits1_5_test() { ); } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[test] fn test_build_hnsw_using_quantization() { let dir = Builder::new().prefix("segment_dir").tempdir().unwrap(); diff --git a/lib/segment/tests/integration/multivector_filtrable_hnsw_test.rs b/lib/segment/tests/integration/multivector_filtrable_hnsw_test.rs index acd6d4f7b1..0e3a87a7c6 100644 --- a/lib/segment/tests/integration/multivector_filtrable_hnsw_test.rs +++ b/lib/segment/tests/integration/multivector_filtrable_hnsw_test.rs @@ -26,6 +26,7 @@ use segment::vector_storage::VectorStorageRead; use tempfile::Builder; /// Check all cases with single vector per multi and several vectors per multi +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[rstest] #[case::nearest_eq(QueryVariant::Nearest, 1, 32, 5)] #[case::nearest_multi(QueryVariant::Nearest, 3, 64, 20)] diff --git a/lib/segment/tests/integration/multivector_quantization_test.rs b/lib/segment/tests/integration/multivector_quantization_test.rs index 8f4a3296c8..cee417f83d 100644 --- a/lib/segment/tests/integration/multivector_quantization_test.rs +++ b/lib/segment/tests/integration/multivector_quantization_test.rs @@ -63,6 +63,7 @@ fn sames_count(a: &[Vec], b: &[Vec]) -> us .count() } +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] #[rstest] #[case::nearest_binary_dot( QueryVariant::Nearest, diff --git a/lib/segment/tests/integration/payload_index_test.rs b/lib/segment/tests/integration/payload_index_test.rs index 45583847be..ee76198d43 100644 --- a/lib/segment/tests/integration/payload_index_test.rs +++ b/lib/segment/tests/integration/payload_index_test.rs @@ -1114,6 +1114,7 @@ fn test_struct_payload_geo_polygon_index(test_segments: &TestSegments) -> Result } #[test] +#[cfg_attr(target_os = "windows", ignore = "slow on Windows, not OS-specific")] fn test_struct_payload_index_nested_fields() { // Compare search with plain and struct indexes let dir1 = Builder::new().prefix("segment1_dir").tempdir().unwrap();