Repository navigation
[CI] Flaky test sweep (Sep–Oct 2026): root causes and fix PRs #2783
Description
This issue coordinates a sweep of flaky CI test failures. Each workstream had a draft PR with a reduced CI matrix that repeated the failing job, so the flake could be reproduced in CI and the root cause found. Progress (reproducers, root causes, PR links, run links) is posted as comments here. The overall tracker is #2118.
Status (2026-10-14): root-causing is done for every workstream. What remains is review and merge of the open fix PRs listed under Remaining.
Data source: pr.yaml runs from 2026-09-05 to 2026-10-10 whose failed attempts later passed on a rerun, plus nightly test.yaml failures from 2026-09-26 to 2026-10-10.
Workstreams
| # | Flaky failure | Hits (5 weeks) | Where | Issue | Repro PR | Fix PR / status |
|---|---|---|---|---|---|---|
| A | CLUSTER_TEST SpectralClusteringTestF.Result/N (eigensolver / label mismatch) |
17 | Almost always 12.2.2, arm64, ubuntu22.04, a100 |
#2094, #1594 | #2788 (closed) | Open: #2794 and #2841 both fix this by changing the expanded-L2 clamp in l2_exp.cuh; only one should merge |
| B | Python test hangs on H100: cuvs test_cagra.py stalls, then cuvs_bench test_cli.py::test_run_command_creates_results hangs (CUVS_CAGRA_HNSWLIB_ANN_BENCH) |
7 PR + nightly 6-hour timeouts | h100 (oldest-deps rockylinux8, and nightly 12.9.2), once on a100 |
#2772, #2732 | #2784 (closed) | Open: #2829 |
| C | cuvs-lucene results missing from filtered/multi-segment searches (expected:<24> but was:<23>, Should return exactly 1 result expected:<1> but was:<0>, expected:<60> but was:<59>, Id: 1608 expected but not found) in TestMultiSegmentGPUFilterConcurrency, TestCuVSGaps.testVectorSearchWithFilterAndAlternatingDocuments, TestCuVSDeletedDocuments |
~20 | lucene tests, amd64 and arm64 | #2736 | #2785 (closed) | Open: #2799 |
| D | cuvs-lucene IllegalStateException: Index not found for field:... (TestCuVSGaps.testVectorSearchWithAlternatingDocuments, TestCuVSVectorsFormat.testRandomWithUpdatesAndGraph, TestCuVSRandomizedVectorSearch) and TestLucene99AcceleratedHNSWVectorsFormat.testRandomExceptions UNEXPECTED: exception type |
~10 | lucene tests, amd64 and arm64 | none | #2786 (closed) | Merged: #2795 (2026-10-13) |
| E | KmeansFitBatchedTestF.Result/4 centroid mismatch |
11 | h100 (13.3.0) C++ tests, incl. 2026-10-06 nightly |
#2657 | #2787 (closed) | Open: #2801 (deterministic k-means fit), #2804 (balanced k-means / IVF-PQ grouping) |
| F | NEIGHBORS_MG_TEST AnnMGTestF_float.AnnMG/5 aborts with a CUDA error in raft::copy (cudaMemcpyAsync) |
1 | 12.2.2, amd64, rockylinux8, l4, earliest-driver, oldest-deps |
none | #2796 (closed) | Open: #2807 |
| G | NEIGHBORS_ANN_IVF_RABITQ_TEST IvfRabitq/f32_f32_i64.build_host_input_search/1 (dim = 1) throws in the test body |
1 (nightly) | 13.3.0, arm64, ubuntu26.04, gh200 |
none | #2791 (closed) | Open: #2798 |
| H | Python IVF-PQ/IVF-Flat result flakes: test_ivf_pq.py::test_build_precomputed (2/1000 neighbors swapped), test_ivf_pq[inner_product-float32-True] (recall 0.696 vs 0.7), test_filtered_ivf_flat[0.5] (recall) |
3+ | A100 (amd64 and arm64) | #1445 (closed) | #2790 (closed) | Open: #2797 |
| I | STATS_TEST silhouetteScore.BatchedStreamPoolOrdering |
2 (2026-07-11, 2026-08-03) | arm64 a100 (12.2.2), gb300 (13.3.0 nightly) |
#2406 (closed) | #2789 (closed) | Already fixed on main by #2422; no PR needed |
| J | cuvs-java JVM crashes and flakes: HnswRandomizedIT forked VM aborted (exit 134), CagraRandomized*.testResultsTopKWithRandomValues |
1 + older | java tests, amd64 | #1048, #631, #1468 | #2792 (open draft) | Open: #2805 (reject single-row CAGRA build), #2830 (re-enable CagraRandomizedIT with a recall check) |
| K | IVF-RaBitQ search returns neighbors from the wrong list when a query is on a list centroid (e.g. a duplicated data row); found by G | n/a (found in review) | IVF-RaBitQ C++ | none | #2800 (closed) | Open: #2802 |
| L | NEIGHBORS_TEST NNTest/NNTest_{fp32,fp16}_fused* skipped then failed on GB300 (no cuTile cubin for sm_103); found that cuTile had been silently disabled in all conda builds since #2739 (bin2c lookup) |
every GB300 nightly until 2026-10-08 | 13.3.0, arm64, ubuntu26.04, gb300 |
#2660 (closed), #2759 (closed) | #2813 (closed) | Merged: #2820 (2026-10-13; configure now fails on CUDA 13.1+ when bin2c or cuda.tile is missing). Skip fixed by #2760. #2821 closed |
| M | Rust cargo test with default parallel test threads intermittently SIGSEGVs (e.g. ivf_flat); passes with --test-threads=1 |
0 in CI (0 SIGSEGVs in ~3,300 rust-build jobs since 2026-08-10) |
Rust tests (reporter: RTX 4000 8 GB, libcuvs 26.06) | #2233 | #2818 (open draft) | Not reproduced in 6,700 parallel iterations; no fix needed. Related bug found during the investigation: #2832 (open, cuvsRMMHostFree on a different thread) |
| N | NEIGHBORS_ANN_IVF_FLAT_TEST AnnIVFFlatTestF_{int8,uint8}.AnnIVFFlat/2 (dim=2, cosine): filtered search returns recall 0 (or duplicated indices) |
6 (2026-08-12 to 2026-09-19), 0 since | mostly 12.2.2, amd64, rockylinux8, v100, earliest-driver; once h100 |
none | #2815, bisect #2822/#2825/#2826 (closed) | Fixed on main by #2249. Open: #2828 (defensive zero-norm coarse guard) |
| O | cuvs-lucene TestCuVSRandomizedHNSWVectorSearch.testVectorSearch: Result returned was not in topK*3: 229 (CAGRA-built HNSW graph, searched on the CPU). Legitimate ANN miss, not a bug |
1 (found by S); 3/1586 in the repro loop | lucene tests, amd64 and arm64 | none | #2819 (closed) | Merged: #2824 (2026-10-14, test-only) |
| P | cuvs-lucene TestAcceleratedHNSWMultiLayerRoundTrip.testThreeLevelGraphCanBeReopenedAndSearched (BQ, dims=129): the entry-node vector must be returned for its own query. Test bug: the graph ordinal was used as the insertion id after a reordering forceMerge(1) |
1 (found by S); 112/39,320 BQ iterations in the repro loop | 13.3.0, amd64, rockylinux8, rtxpro6000 lucene tests |
none | #2827 (closed) | Merged: #2831 (2026-10-13, test-only) |
| Q | NEIGHBORS_ANN_CAGRA_HALF_UINT32_TEST AnnCagraMultiPartitionTestF16_U32.Search/0 (num_partitions=1, single-cta, L2): recall 0.966 vs 0.97. The test was under-provisioned; not a library bug |
1 (found by S); 0 in 60 days of nightly and PR reruns | 13.3.0, arm64, ubuntu26.04, gb300 |
none | #2838 (closed) | Open: #2839 (test-only) |
| R | C++ test jobs hitting the 6-hour limit: 86 of 89 were NEIGHBORS_DYNAMIC_BATCHING_TEST hanging after a NaN query; the other 3 were stalled artifact downloads |
89 (since 2026-09-01) | conda-cpp-tests, all configurations |
#2764 | none | Already fixed on main by #2729 (plus #2746 as a backstop); no PR needed |
| S | Combined stress run (done 2026-10-12 02:00 UTC): 25 passes, 3025 test-job executions on the PR+nightly matrices with all fixes; 3 new flakes found (O, P, Q) | n/a | full matrix | #2783 | #2793 (closed) | Done |
Remaining
Open fix PRs, and the issues each should close when it merges:
- A: Make the L2 expanded self-distance clamp relative to the norms #2794 or Fix L2 expanded distance clamp for cutlass and legacy fused backend #2841 (only one should merge), closes [CI] CLUSTER_TEST spectral flaky #2094 and [BUG] Possibly flaky C++ test:
SpectralClusteringTests/SpectralClusteringTestF.Result/5#1594. Make k-means fit deterministic #2801's CI stays red on A until one of them merges. - B: Do not treat pageable host memory as device memory on HMM systems #2829, closes [BUG] CAGRA hang on h100 oldest-deps CI (cuvs test_cagra, then cuvs_bench test_run_command_creates_results) #2772 and [CI] Hanging conda and wheel python tests #2732
- C: Derive the filtering rate in filtered multi-partition CAGRA search #2799, closes [CI] TestMultiSegmentGPUFilterConcurrency flaky #2736
- E: Make k-means fit deterministic #2801 and Make balanced k-means donor selection and IVF-PQ codebook grouping independent of scheduling #2804, closes [BUG] kmeans::fit is not run-to-run reproducible; KmeansFitBatchedTestF.Result/4 flakes ~50% on GB10 and should not gate CI #2657
- F: Fix CAGRA dataset descriptor race across threads sharing a handle #2807
- G: Handle empty lists when building IVF-RaBitQ indexes #2798
- H: Fix flaky IVF-PQ Python tests #2797
- J: Reject single-row datasets in CAGRA graph build #2805 and Re-enable CagraRandomizedIT with a recall-based check #2830, closes [BUG] cuvs-java com.nvidia.cuvs.CagraRandomizedTest.testResultsTopKWithRandomValues test fails intermittently #631, [BUG] Flaky java test
CagraRandomizedIT.testResultsTopKWithRandomValues#1048 and [BUG] [Java]CagraRandomizedIT.testResultsTopKWithRandomValuesfails randomly #1468. Then close repro PR [DO NOT MERGE] Reproduce flaky cuvs-java randomized tests #2792 - K: Fix IVF-RaBitQ search for queries on a list centroid #2802
- M: Allow cuvsRMMHostFree on a different thread than cuvsRMMHostAlloc #2832. Close [BUG] Rust: default parallel cargo test SIGSEGVs on GPU tests; passes with --test-threads=1 #2233 as not reproduced, and close repro PR [DO NOT MERGE] Reproduce flaky Rust parallel cargo test SIGSEGV #2818
- N: Guard IVF-Flat cosine coarse search against zero-norm centers #2828
- Q: Use itopk_size=128 in the core CAGRA multi-partition test sweep #2839
Other failures seen (not assigned)
NEIGHBORS_ANN_SCANN_TESTreconstruction error (6 hits, rtxpro6000): [CI] SCANN Test flaky #2549, fixed by Fix reconstruction test for ScANN #2743 (merged 2026-10-14).NNTest_*_fusedon GB300 nightly: deterministic, not flaky. Fixed by Fix GB300 cutile test skip #2760; [BUG] NNTest: GTEST_SKIP() in compute_1nn() doesn't skip the test, so cuTile-backend cases fail when cuTile is unavailable #2660 closed.AnnNNDescentTestUI8_U32(50 hits in September): fixed by Disable AnnNNDescentTestUI8_U32 until release/26.10 is complete #2655 / Fix NN-Descent early stopping #2708.- One-off hits (one each), now covered by workstreams F, G, H and J:
NEIGHBORS_MG_TESTAnnMG/5CUDA error on L4 oldest-deps (job);IvfRabitq build_host_input_searchon GH200 (job);test_ivf_pq[inner_product-float32-True]recall 0.696 on A100 wheels (job);test_ivf_pq.py::test_build_precomputedon arm64 A100 (job); JavaSurefireBooterForkExceptionVM crash (job). - Older flaky issues with no hits in this window: [CI] STATS_TEST silhouetteScore flaky #2406 (silhouetteScore, closed), [CI] NEIGHBORS_ALL_NEIGHBORS_TEST flaky #2137 (all-neighbors, closed), [BUG] Intermittent test failure:
test_ivf_flat.py::test_filtered_ivf_flat[0.5]#1445 (test_filtered_ivf_flat, closed), [BUG] Flaky java testCagraRandomizedIT.testResultsTopKWithRandomValues#1048 / [BUG] cuvs-java com.nvidia.cuvs.CagraRandomizedTest.testResultsTopKWithRandomValues test fails intermittently #631 (JavaCagraRandomized*, covered by J). - Infrastructure, not test flakes: codecov upload retries,
rapids-github-run-idfailing to find a nightly run, condaChecksumMismatchError, yum CUDA repo metadata downloads, sccache storage reads, and devcontainer builds.
Protocol and combined stress run (historical)
Protocol for agents
- One draft PR per workstream, based on
main. The first commit reduces.github/workflows/pr.yamlto the jobs and matrix entries that show the failure (usematrix_filter, and disable unrelated jobs), and repeats the failing job up to 4 times (or loops the test inside the job) so a flake is caught. - Trigger CI with
/ok to test <full 40-char sha>after every push. - Post a comment here when a draft PR is opened, when a reproducer is confirmed, when a root cause is found, and when a fix is verified. Link CI runs.
- CI-only changes are reverted before a fix PR is marked ready for review.
Combined stress run (workstream S)
A separate draft PR combined every fix that was ready, ran the full PR CI matrix, and kept rerunning the test jobs until 2026-10-12 02:00 UTC, to confirm the fixes and to find flakes not listed here.
- When a workstream had a fix ready, it pushed a fix-only branch to
bdice/cuvs: the fix commits on top ofupstream/main, with no CI or workflow changes and no diagnostics. It then posted a comment starting with**FIX READY:**giving the workstream letter, the branch name, the head SHA, and a one-line summary. - When the stress run found a failure not covered by an existing workstream, it posted a comment starting with
**NEW FLAKE:**with the test name, configuration, and job links, and a new workstream was assigned to it.
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fieldsNeeds Triage
Activity
bdice commentedon Oct 10, 2026
Workstream B (H100 CAGRA hang): Opened draft #2784. It runs only the H100 conda Python test configs (PR
12.2.2 rockylinux8 oldest-depsand nightly12.9.2 ubuntu24.04 latest-deps, 2 copies each) against nightly packages, loopstest_cagra.py(alternating with the preceding test files) andcuvs_benchtest_cli.py, and on a stall dumpspy-spy --native,gdball-thread backtraces,cuda-gdbkernel info, andnvidia-smiutilization.Log analysis so far: in 4 of 6 hung jobs,
test_cagra.pystalls at test 35 or 36 of the file (test_cagra_dataset_dtype_host_devicewithivf_pq,cosine,float16); the other 2 stall later, intest_cagra_build_from_dataset_handle. The bench hang is insidecagra::build(NN-descent) called fromhnsw::build.pytestignores the SIGINT fromtimeout, so it is stuck in native code.bdice commentedon Oct 10, 2026
Workstream C (lucene missing results): Opened draft PR #2785 (CI reduced to CUDA 13 cpp build -> java/lucene build -> 4 lucene test jobs, amd64+arm64). Triage of the logs so far: all
TestCuVSGaps.testVectorSearchWithFilterAndAlternatingDocuments/TestCuVSDeletedDocumentshits predate #2612 (exact fallback for selective filters, merged 2026-09-17); none since. The active one isTestMultiSegmentGPUFilterConcurrency.filteredSearchesTakeTheMultiPartitionGpuPath(24 vs 23, 5 hits since 09-26), where a filtered multi-partition CAGRA search returns < k hits and the query falls back to the per-segment path. Suspect:cagra::detail::search_multi_partitionnever derivesfiltering_ratefrom the partition bitsets, so MULTI_CTA itopk is not enlarged for selective filters the way single-partition search does it. CI run measures the shortfall rate vs itopk/algo and replays the failing seeds.bdice commentedon Oct 10, 2026
Workstream D (lucene index not found): Draft PR #2786 is open. CI is reduced to the CUDA 12 cpp/java/lucene jobs, with 3 copies of
conda-lucene-testson amd64 and arm64. Each copy runs the full suite, then repeatsTestCuVSGaps,TestCuVSRandomizedVectorSearch,TestCuVSVectorsFormatandTestCuVSDeletedDocumentsfor 40 minutes.Root cause (reproduced locally, every run):
CuVS2510GPUVectorsReaderskips loading the GPU indexes when it is opened withIOContext.Context.MERGE, socuvsIndicesstaysnull. Lucene'sReadersAndUpdates.getReaderForMerge(context)caches theSegmentReaderit opens for a merge in the writer's reader pool. If an NRT reader (writer.getReader()/DirectoryReader.open(writer)) is opened while that merge is still running,getReadOnlyClonereturns that pooled reader, or a clone that shares its core. Searching it then fails:GPUKnnFloatVectorQuery.rewriteseesgetCagraIndexForField == nulland falls back to the per-leaf path.CuVS2510GPUVectorsReader.search, which throwsIllegalStateException: Index not found for field:....This is a race between NRT reopen and a background merge, which is why the failure is intermittent and can't be reproduced by seed. It is also a real production bug, not only a test problem.
New test
TestSearchSegmentsOpenedForMergereproduces it every time: it blocks a forced merge inOneMerge.wrapForMerge, after the merge readers are open, then opens an NRT reader and searches. It fails with the same stack as CI (CuVS2510GPUVectorsReader.search:467<-GPUKnnFloatVectorQuery.approximateSearch).Fix (local, passing): a reader opened for a merge loads its GPU indexes on first search (thread-safe lazy load) instead of never loading them. The memory saving for merge-only readers is unchanged.
testRandomExceptionsUNEXPECTED: exception type: the 3 hits are all from before #2744 (merged 2026-10-07), which unwraps theInvocationTargetExceptioninLuceneProviderso the injected IOException surfaces correctly. I found no hits after #2744, so I'm treating it as already fixed.The cause is not shared with workstream C. It only produces exceptions, not missing hits.
bdice commentedon Oct 10, 2026
Workstream A (spectral clustering): Draft PR #2788 opened. CI is reduced to 4 copies of the arm64 / CUDA 12.2.2 / A100 C++ test job. Each copy runs
CLUSTER_TEST5 times,SpectralClustering*300 times in one process, andSpectralClusteringTestF.Result/7200 times in fresh processes, plus a diagnostic test that checks which stage is nondeterministic. All 17 historical failures areResult/7(ARI 0.58-0.64). Locally on an RTX 6000 Ada the kNN connectivity graph already differs between runs on identical input (3-4 variants, presumably from distance ties in kNN selection), but Lanczos always finds the 5-dim null space and ARI stays at 1.0.bdice commentedon Oct 10, 2026
Workstream E (kmeans batched fit): draft PR #2787 (CI reduced to
CLUSTER_TEST --gtest_filter=KmeansFitBatchedTests/* --gtest_repeat=100, 4x H100 / CUDA 13.3.0; first run: https://github.com/NVIDIA/cuvs/actions/runs/38063498592).Notes so far:
KmeansFitBatchedTestFhit in the window is fromrelease/26.10nightlies or frommainbefore Use newtop_1_nnprimitive in KMeans #2249 (2026-09-23). Use newtop_1_nnprimitive in KMeans #2249 removedASSERT_TRUE(centroids_match)from the float test onmain, somainhides the symptom but not the cause. The 2026-10-06 nightly failure wasResult/3(1.959 != 2.001 @13,2), notResult/4.24b0b170): 30 identical in-memorykmeans::fitcalls (same data,init=Array) give 30 different centroid sets, even withmax_iter=1. With the float centroid check restored,KmeansFitBatchedTestF.Result/5fails 5/20 locally.raft::linalg::reduce_rows_by_key(per-cluster row sums) andreduce_cols_by_key(per-cluster weights) do this, so the float summation order changes from run to run. That noise either flips the iteration at which the convergence check stops ([BUG] kmeans::fit is not run-to-run reproducible; KmeansFitBatchedTestF.Result/4 flakes ~50% on GB10 and should not gate CI #2657) or, when cuTile's TF32 assignment is used, moves borderline points.mapThenSumReduce(centroid shift, weight sum) also sums with atomics.cub::DeviceReducefor the shift and weight sum. Locally all fits are bitwise identical (1 distinct result out of 30, float and double, device and host-batched input), and the restored assertions pass 20/20. Kernel cost is within about 15% of the atomic version for large d, faster for small d with heavy contention, plus ~20 us fixed overhead per iteration on tiny inputs.bdice commentedon Oct 10, 2026
Workstream A (spectral clustering): Root cause found and reproduced locally. CI confirmation on A100 is still running.
The embedding is fine. The k-means assignment step is wrong. For a graph with 5 components, the normalized-Laplacian embedding puts each cluster at a single point, and all 5 points have the same squared norm (1/vol = 1/3800 ≈ 2.6e-4). The squared distance between them is 5.3e-4.
The L2-expanded epilogue in
cpp/src/distance/detail/distance_ops/l2_exp.cuhzeroes any distance withd^2 < 1e-6when||x||^2 == ||y||^2. That clamp is meant to remove self-distance round-off, but it uses an absolute tolerance. Hered^2= 2.8e-7, and many rows' norms are bitwise equal to another centroid's norm, so wrong centroids get distance 0 and win the argmin. k-means then returns ARI ≈ 0.6. Every mismatched row I checked has a norm bitwise equal to a wrong centroid's norm.Only the CUTLASS/SIMT fused 1-NN path has this clamp. The unfused path does not. With CUDA 12, the CUTLASS path is chosen for compute capability 8.x, so A100 is the only GPU in the PR matrix that hits it. The double-precision test passes because its clamp threshold is 1e-15.
Run-to-run flakiness comes from small rounding differences that decide whether norms collide. The kNN graph is not deterministic: on identical input I see 3-4 graph variants, presumably from distance ties.
Smallest reproducer:
pairwise_distance(L2Expanded)on0.01 * Ireturns 0 for every off-diagonal entry, where 2e-4 is expected. Fix in progress: make the clamp tolerance relative to the norms when the norms are below 1.bdice commentedon Oct 10, 2026
Workstream E (kmeans batched fit): reproduced in CI. Baseline run on
main+ diagnostics, 4x H100 / CUDA 13.3.0, 100 repetitions each: https://github.com/NVIDIA/cuvs/actions/runs/38063498592KmeansFitBatchedTest{F,D}.Reproducible/*test (3 identical fits from the same initial centroids, device and host input, compared bitwise): fails 400/400 for all 12 configs. Inertia and centroids differ on every run, both float and double. The run-to-run nondeterminism is present on H100.ASSERT_TRUE(centroids_match)restored for float,Result/*passed 400/400 on H100. On currentmain, H100 convergesResult/4in 4 iterations (11 or 14 before Use newtop_1_nnprimitive in KMeans #2249), so the noise does not currently change the stopping iteration there. It still does on other GPUs: locally on Ada,Result/5mismatched 5/20.Fix pushed to [DO NOT MERGE] Reproduce flaky KmeansFitBatched test #2787 (deterministic per-cluster sums); CI is running.
bdice commentedon Oct 10, 2026
Workstream D (lucene index not found): For workstream C: I found a seed that reproduces C's missing-results failure locally every time, with the D fix applied as well.
mvn test -Dtest=TestCuVSGaps -Dtests.seed=65F332205DAF280DfailstestVectorSearchWithAlternatingDocumentswithShould return exactly 56 results expected:<56> but was:<54>in 3 of 3 runs. Setup: RTX 6000 Ada, nightly libcuvs 26.12.00a108 (24b0b17, cuda12), and the cuvs-java jar from build run 38028687031. It is unrelated to the 'Index not found' bug.Local loop of the 4 affected classes before the D fix: 2/64 iterations failed. One was
Index not found(TestCuVSVectorsFormat.testRandomWithUpdatesAndGraph, seed 971AE5B7CFADB729; timing-dependent, so the seed does not reproduce it). The other was the C failure above.bdice commentedon Oct 10, 2026
Workstream C (lucene missing results): Reproducer confirmed in CI on #2785 (run https://github.com/NVIDIA/cuvs/actions/runs/38062855918, amd64 jobs 114249302243, 114249302320):
TestMultiSegmentGPUFilterConcurrency.filteredSearchesTakeTheMultiPartitionGpuPathfails 9/36 runs with the sameexpected:<24> but was:<23>(random seeds: 1/80). It's not deterministic per seed, because the CAGRA graph build isn't deterministic.cagra::searchmulti-partition overload (cpp/src/neighbors/cagra.cuh) never derivesfiltering_ratefrom the partition bitsets. Single-partition search counts the bitset and MULTI_CTA'sadjust_search_paramsthen enlarges itopk_size (here 32 -> ~352). Multi-partition leaves it at -1, so a selective filter runs with itopk 32, sometimes finds < k valid neighbors, and the Lucene query falls back to per-segment search. Fix: compute the rate per partition (max over partitions that accept >= 1 row). Pushing next with a C++ test.TestCuVSGaps.testVectorSearchWithAlternatingDocuments59 vs 53(seed BBE2A1CCC7772A7F) fails deterministically: it's a test bug. With that seed the index has only 53 docs with vectors, and topK=59. Fix flaky accelerated HNSW gaps test #2709 fixed the same issue inTestCuVSAcceleratedHNSWGaps. The olderTestCuVSGaps...FilterAndAlternatingDocuments/TestCuVSDeletedDocumentshits all predate Lucene: add exact search fallback for very selective filters #2612 (2026-09-17) and haven't recurred. No overlap with workstream D'sIndex not found.bdice commentedon Oct 10, 2026
Workstream A (spectral clustering): Reproducer confirmed in CI (run 38063875977, 4 copies of the arm64 / 12.2.2 / A100 job, unmodified library):
CLUSTER_TEST: 6/20 runs failed.SpectralClusteringTestF.Result/7in fresh processes: 126/800 failed (4, 12, 95, 15 per copy).SpectralClustering*with--gtest_repeat=300: 35Result/7failures out of 1200.Fix (relative self-distance clamp in
l2_exp.cuh, plus regression tests forpairwise_distanceandtop_1_nn) is pushed to #2788 and CI is running.bdice commentedon Oct 10, 2026
Workstream D (lucene index not found): CI reproducer confirmed without the fix (run 38063064947, 6 lucene test jobs: 3 on amd64 rtxpro6000, 3 on arm64 l4):
TestSearchSegmentsOpenedForMergefailed in 6/6 jobs withIndex not found for field:vector.Index not foundin 17 of 853 iterations (about 2%), spread across all four affected classes and both arches:TestCuVSVectorsFormat.testRandomWithUpdatesAndGraph,TestCuVSRandomizedVectorSearch.testVectorSearchandtestVectorSearchWithFilter, andTestCuVSGaps.testVectorSearchWithAlternatingDocuments.Should return exactly N resultsfailures (TestCuVSGaps).Pushed the fix (37c16e4) to #2786. Locally, the same loop went from 2/64 iterations failing before the fix to 0/106 after, and the full cuvs-lucene suite passes (375 tests).
bdice commentedon Oct 10, 2026
Workstream I (silhouetteScore): Opened draft #2789.
Triage: both known hits are
silhouetteScore.BatchedStreamPoolOrderingon arm64. One is PR job 86496273362 (2026-07-11, A100, CUDA 12.2.2), and the other is nightly job 91625948741 (2026-08-03, GB300, CUDA 13.3.0). The expected value is the same in both (-4.86e-4), butactualvaries (3.25e-3 and 8.4e-4).Both hits predate #2422 (merged 2026-08-17). Before that change, the batched path wrote the row-wise min of
b(n_rows x n_labels) in place tob[0..n_rows). Withn_labels=2, RAFT's thin coalesced reduction (512 rows per block) has blockkoverwrite inputs that blockk/2may not have read yet. That is an inter-block race, so it only shows up under GPU contention (ctest -j8). Working hypothesis: #2422 already fixed #2406.#2789 checks this on 4x arm64 A100. Each job loops
silhouetteScore.Batched*in 8 concurrent processes, both with a temporary env switch that restores the in-place reduction (negative control) and with currentmain. I'm also scanning the failedconda-cpp-testsjobs since 2026-08-01 for later hits.bdice commentedon Oct 10, 2026
Workstream H (Python IVF result flakes): Opened draft #2790. CI is reduced to the nightly-matrix A100 conda Python test configs (amd64 12.9.2, arm64 13.0.3, arm64 13.3.0) plus rtxpro6000 13.3.0, 2 copies each. It runs against nightly packages with this branch's test files, runs tie/recall diagnostics, and repeats the three tests 200 times each with pytest-repeat.
Local findings (RTX 6000 Ada, nightly cuvs 26.12.00a108):
test_build_precomputed: I reproduced the swap locally by using 10000 queries instead of 100 (2 of 10 runs swapped one pair). The regular and precomputed indexes have identical labels and PQ codes, and their distances are bitwise identical. Only the in-list storage order differs: about 2% of rows are at the same position, becauseprocess_and_fill_codesassigns slots withatomicAdd. Every swap I saw is two neighbors from the same list whose float32 distances are bitwise equal (for example0x1.6efa14p+1), even though their codes differ. The tie is resolved by storage position, so the test's exact index comparison is wrong when distances tie. Add a stable flag to the IVF-PQ and IVF-SQ search #2696'sstableflag breaks ties on the in-list candidate position, not the dataset id, so it would not make these two indexes agree.test_ivf_pq[inner_product]:generate_datauses the unseeded global numpy RNG. Over 1000 seeds, recall has mean 0.741, std 0.011 and min 0.703, versus about 0.91 for L2. On the same normalized problem with identical ground truth, IVF-PQ gets 0.92 recall with sqeuclidean and 0.74 with inner_product (pq_dim auto = 8 for dim 10). The IP score error is q·(r - r̂), while the L2 error scales with |q - x|, so the gap is expected. With the data seed fixed, recall still varies by about ±0.005 between runs because of build nondeterminism.test_filtered_ivf_flat[0.5]: Fix test params for filtered ivf_flat #1463 retuned n_lists and n_probes after [BUG] Intermittent test failure:test_ivf_flat.py::test_filtered_ivf_flat[0.5]#1445 was filed. Mean recall is about 0.96 and there have been no hits in the window. I'll confirm this in CI.78 remaining items
bdice commentedon Oct 11, 2026
Workstream S (stress run) summary #4 (2026-10-11 18:05 UTC), PR #2793
Current head
23d182e3, building now. I rebuilt the branch from24b0b170with:7dcc920a) plus the narrowed Make balanced k-means donor selection and IVF-PQ codebook grouping independent of scheduling #2804 (fe6517c6)9af2f6ba), which also includes the single-row fix696e4612(refactor)0a27e5fa)Since summary #3: attempts 4-7 of 38117245263, the full-fix build, were all clean, making 7/7 clean passes there (847/847 test jobs). The rebuild run 38149049954 failed deterministically in 19 jobs because of the first narrowed #2804 test (reported above, fixed in
fe6517c6). Its other 102 jobs passed.Cumulative: 16 passes, 1936 test-job executions. Excluding those 19 deterministic failures, 3 failed, all reported earlier (O, P, ScaNN #2549); there have been no new flakes since 03:00 UTC. Per flake (failed / executions with the fix):
CLUSTER_TESTjobs, 0/16 on arm64 12.2.2 A100CLUSTER_TEST, 0/48 on H100STATS_TESTjobsPlan until 02:00 UTC: one full run on
23d182e3, then test-only re-runs, merging any furtherFIX READYbranches.bdice commentedon Oct 11, 2026
Workstream E2 (k-means perf evidence): benchmarks are done, and #2801 and #2804 now have Performance/Accuracy sections in their descriptions. Method:
main, #2801 and narrowed #2804 built identically in one run, interleaved, 10 samples per config, on H100, A100, GH200, GB300, RTX PRO 6000 and L4 (#2803, https://github.com/NVIDIA/cuvs/actions/runs/38154672010).maindiffers run to run in 52 of 53 configs on A100). Per-iteration time is up to 30% faster for small k and large n. There is a regression at n≈100K with k≥100 or d=1024: +5–11% per iteration (0.05–0.1 ms). Nsight Systems attributes it to the fixed cost of the radix sort by label (124 → 195 µs per centroid update on A100). Centers are closer to a float64 fit (up to 10–100x). It does not touch the IVF build paths (IVF builds within ±2%).fe6517c6): IVF-Flat/PQ/SQ/RaBitQ/CAGRA builds are within -2.2%..+1.5% on H100 and -3.2%..+6.3% on A100 (only the 1M SQ/RaBitQ builds are above +3%, with overlapping ranges). Recall is unchanged. In a 64-fit microbenchmark of tiny balanced fits, H100 shows +12–16% in 2 of 5 configs; nsys shows more EM iterations (a different donor trajectory), not slower kernels.bdice commentedon Oct 11, 2026
NEW FLAKE: C++
NEIGHBORS_ANN_CAGRA_HALF_UINT32_TESTAnnCagraMultiPartitionTest/AnnCagraMultiPartitionTestF16_U32.Search/0, with{n_queries=100, dataset shape=10000x64, k=10, num_partitions=1, split=even, single-cta, itopk_size=64, metric=L2}:conda-cpp-tests (3, 4) / 13.3.0, 3.14, arm64, ubuntu26.04, gb300, latest-driver, latest-deps23d182e3, attempt 4, a test-only re-run; the same build passed this job in attempts 1-3)fe569825, which derivesfiltering_ratein multi-partition search) is in this build, but it only affects filtered searches, so I don't think it's involved. I couldn't find any earlier hit in the 5-week CI history logs or anywhere else in the stress run: the test has passed in about 10 other GB300 shard-3 executions and about 290 executions in other configs.bdice commentedon Oct 11, 2026
Workstream Q (CAGRA multi-partition fp16 recall): draft reproducer #2838.
AnnCagraMultiPartitionTest*case in nightlytest.yamlfailures (2026-08-10 to 2026-10-10, 179 failed test jobs) or in the minedpr.yamlfailed-then-rerun attempts. The only known hit is S's job.Search/0(single-cta, L2) 0.966,Search/3(multi-cta, L2) 0.976, while InnerProduct and Cosine are at 0.991 to 0.993. The test comment says that floors of about 0.974 were observed when 0.97 was chosen. So L2 runs close to the threshold.GenerateRoundingErrorFreeDatasetgives only 8 distinct values per dimension (resolution 4 for dim=64), so the data is coarse and uniform.Search/0-5(num_partitions=1) 250 times, runs a dataset-seed sweep, and runs an f32 comparison. Every repetition logs the multi-partition recall, the recall of a regular single-indexcagra::searchon the same index, the graph hash, and per-query misses, which together give the recall distribution.bdice commentedon Oct 11, 2026
Workstream Q (CAGRA multi-partition fp16 recall): I have the recall distribution. It's a tail event on a stable, unimodal distribution, not a bug. Run: 38177641942, with gb300 x2 and arm64 a100 x2. The jobs show as "failed" only because of the diagnostic script's exit code; no test case failed.
F16 Search/0(num_partitions=1, single-cta, L2, itopk=64), seed 1234:On GB300 the histogram is a single peak at about 0.979, with a left tail reaching 0.967 (the test fails at <= 0.966). The other L2 cases look the same:
Search/3(multi-cta, L2) has a GB300 mean of 0.981 and a min of 0.969. InnerProduct and Cosine sit at 0.993 to 0.996. f32Search/0on GB300 has a mean of 0.9886 and a min of 0.981.Hypotheses ruled out:
cagra::searchon the same index gave exactly the same recall in 800/800 runs. The index differences are only the order of tied distances; the coarse fp16 data has many exact ties. The single-cta search is also deterministic: repeating it on the same index gave identical results 800/800 times.max_iterations=20, with about 115k updates still happening in the last iteration. The old criterion, the new criterion andtermination_threshold=0produce the same distribution (mean 0.9786 / 0.9787 / 0.9786 on GB300).Why it shows up on GB300: the "fixed" dataset is not the same on every GPU.
raft::randomlaunches its RNG kernels with4 * multiProcessorCountblocks, so seed 1234 produces a different dataset on GB300 than on A100. The GB300 dataset happens to be harder (mean 0.9786 vs 0.9845). When I sweep the seed (600 runs per GPU), both GPUs average 0.984 with sd 0.004 and a min of 0.969.So the 0.97 threshold is about 3.5 sd below the GB300 mean. The test comment says 0.97 was picked from observed floors of about 0.974, which is too little headroom for a per-build sd of 0.003 to 0.004 plus a dataset that changes with the GPU's SM count. Next run: the same cases including
FilteredSearch(which uses the same 0.97), the full multi-partition suite for all four dtypes, anditopk_size64 vs 128. The goal is to fix the test by giving these cases enough search budget rather than lowering the bar.bdice commentedon Oct 11, 2026
Workstream Q (CAGRA multi-partition fp16 recall): I have the root cause and a candidate fix, which is being verified.
Root cause: the test is under-provisioned, not the library. In the core sweep the num_partitions=1 cases search one 10000-row index with
itopk_size=64. For the coarse fp16, int8 and uint8 datasets, recall at that budget sits around 0.98, and because the NN-descent graph differs from build to build it has a tail that reaches the 0.97 threshold. With num_partitions=8, each 1250-row partition contributes its own 64 candidates and recall is about 1.0. The 0.97 floor was a workaround for this gap.Measured in run 38180387035: gb300 and arm64 a100, 2 jobs each, about 11k test repetitions per job. Each row is one case, with the min/mean over all repetitions:
Search/0L2 single-cta, seed 1234, GB300Search/0, 200 dataset seeds per GPUFilteredSearch/0L2, seed 1234, GB300FilteredSearch/0, 200 dataset seeds per GPUSearch/0,FilteredSearch/0The seed sweep also reproduced a real failure:
FilteredSearch/0scored 0.966 on GB300. With itopk=128, the whole multi-partition suite for all four dtypes passed, and every case's minimum is at or above 0.994.Fix (test-only):
bdice:fix-cagra-mp-recall-itopk@587b72cc. It setsitopk_size=128in the core multi-partition sweep and raisesmin_recallfrom 0.97 to the single-index 0.985 convention. The test then tolerates build-to-build variation and is stricter against real merge/decode regressions. The k-spanning, dimensionality and max_queries-chunk blocks are unchanged, since their minimums are 0.985 or higher at their current budgets. Verification is running in #2838: the fix plus every multi-partition case for all four dtypes x40, plus num_partitions=1 seed sweeps, on gb300, a100, h100 and rtxpro6000 (CUDA 13.3). I'll post FIX READY when it passes.bdice commentedon Oct 12, 2026
FIX READY: Workstream Q (CAGRA multi-partition fp16 recall). Branch
bdice:fix-cagra-mp-recall-itopk, head587b72cc51f59d9144c8aadccd06818e064d839c, draft PR #2839. This is a test-only change: the coreAnnCagraMultiPartitionTestsweep usesitopk_size=128instead of 64, andmin_recallgoes from 0.97 to 0.985. With itopk=64, the num_partitions=1 cases with fp16/int8/uint8 data have recall around 0.98 and a tail below 0.97.Verified in run 38184518531 (#2838 @
eaa8e39a, on GB300, arm64 A100, H100 and RTX PRO 6000, CUDA 13.3). The run repeated every multi-partition case for all four dtypes 40 times, plus 150 repetitions of the num_partitions=1 cases on a different dataset each time. Results: 52,000 recall evaluations, 0 failures. The num_partitions=1 minimum was 0.993 against the new 0.985 floor; before the change the minimums were 0.966 to 0.968 against 0.97. The num_partitions=8 minimum was 0.999. The unchanged k-spanning, dim and chunk blocks had a minimum of 0.990.bdice commentedon Oct 12, 2026
Workstream S (stress run) final summary (2026-10-12 01:50 UTC), PR #2793 (left as a draft)
Totals: 25 passes of the union of the PR and nightly test matrices (121 test jobs per pass: 19 C++ configs × 4 shards, 17 conda Python, 16 wheel, 4 java, 4 lucene, 4 standalone C), for 3025 test-job executions. 3001 passed and 24 failed. 19 of those failures were the one deterministic test bug in the first narrowed #2804 head, fixed in
fe6517c6. There were no infrastructure failures. The passes were 7 full runs and 18 test-only re-runs through thestress-rerun-anchorjob.Final fix set (head
23d182e3, run 38160969192, 9 passes, 1087/1089 test jobs passed):390f97b0696e4612fe56982587ca43987dcc920aand Make balanced k-means donor selection and IVF-PQ codebook grouping independent of scheduling #2804fe6517c6733caab2f7c813d3ccb70f139af2f6bac799bd985af73320and18855627(cuTile re-enabled)ae0f9c2f37f4477e0a27e5faI's fix (#2422) is on
main. Q (587b72cc, posted 00:43) is not included, because a rebuild could not finish before the deadline.Per-flake results with the fix included (failed / executions):
CLUSTER_TESTjobs, 0/25 on arm64 12.2.2 A100. Before the fix, the A100 reproducer failed 6/20 fullCLUSTER_TESTruns; ~17 CI hits in 5 weeks.CLUSTER_TESTjobs, 0/75 on H100NEIGHBORS_MG_TESTjobsNEIGHBORS_ANN_IVF_RABITQ_TESTjobsSTATS_TESTjobsNew flakes found by the stress run (all now have workstreams):
TestCuVSRandomizedHNSWVectorSearch.testVectorSearch(comment), fixed by Use a recall-based check in TestCuVSRandomizedHNSWVectorSearch #2824.TestAcceleratedHNSWMultiLayerRoundTripBQ entry-node self-match (comment), fixed by Query the entry node's own vector in TestAcceleratedHNSWMultiLayerRoundTrip #2831.AnnCagraMultiPartitionTestF16_U32.Search/0recall 0.966 < 0.97 on GB300 (comment). Fix Use itopk_size=128 in the core CAGRA multi-partition test sweep #2839 was not stress-tested.Remaining failures:
ScaNN/f32_i64.build/12with max reconstruction error 1.4301 vs 1.425 (job, job). The catalog only listed rtxpro6000 hits. Fix reconstruction test for ScANN #2743 is the open fix.Integration notes:
test_float_int64_t.cuincludes only in the older E head; the current heads merge cleanly.5af73320.fe6517c6.bdice commentedon Oct 12, 2026
Workstream R (C++ test 6-hour timeouts): already fixed on main by #2729 (plus #2746 as a backstop). No new PR.
I scanned every
conda-cpp-testsjob inpr.yamland nightlytest.yamlruns since 2026-09-01, all attempts included, and found 89 jobs that ran ≥ 5.5 h. For each one I took the lastStart N:line from the log.NEIGHBORS_DYNAMIC_BATCHING_TEST(ctest #28, shard 4/4)cuvs_conda_cpp_libcuvs_*(ctest never started)successat 5.97 h)The dynamic-batching hang. Every GPU and CUDA combination was hit: V100, L4 (amd64 and arm64), A100 (arm64), H100 (including ubuntu26.04 / CUDA 13.3), and GH200. Every shard-4 job of the 2026-10-06 nightly was stuck in this test for about 5h40m. Sample jobs:
Start 28: NEIGHBORS_DYNAMIC_BATCHING_TESTat 06:58:14 and was cancelled at 12:38:37.Timeline on main:
future.get(). The last hung job's run was created at 18:53Z on 10-06, before Fix dynamic batching hangs and CAGRA multi-kernel search on NaN queries #2729 merged.timeout ... 100mto the ctest invocation inci/test_cpp.sh. It explains why none of these logs showtimeout: sending signal: the wrapper did not exist yet. It was not a timeout that failed to fire.After #2729: no C++ test job has run ≥ 5.5 h (all runs through 2026-10-12 were scanned). The test passes in about 68 s (10-12 nightly, L4), and the stress run had 0 hits in 3,025 test jobs.
Relation to other workstreams:
error writing zip archive … connection timed out / reset by peer, then rapids-retry) are GitHub artifact-storage infrastructure problems. Add 100 minute timeout to C++ ctest runs #2746's timeout does not cover the download step. That is an optional follow-up for CI owners; I made no change.Per-job table and logs are kept locally (
scratchpad/wsR/lastcase.tsv).Fix bin2c lookup so conda builds enable cuTile (#2820)
Load cuvs-lucene GPU indexes on first search for readers opened for a…
Query the entry node's own vector in TestAcceleratedHNSWMultiLayerRou…
bdice commentedon Oct 14, 2026
Status update for 2026-10-14
Merged on 10-13: #2820 (cuTile configure check), #2795 (Lucene, D) and #2831 (Lucene multi-layer BQ test, P).
Flake A (spectral clustering, #2094) has two open fixes. #2841 (@aamijar) and #2794 both change the expanded-L2 epilogues in
l2_exp.cuh, so only one should merge:x == y.Either one fixes the
Result/7failures on arm64 A100 CUDA 12.2 and turns #2801 green.CI scan, 10-12 12:00Z to 10-14: no new flaky tests.
libcuvs-examplesrebuildslibcuvs#2848.test_clifailure) hit Clamp negative expanded L2 before sqrt in sparse distances #2738 and [FEA] int8 and uint8 k-means fit instantiations #2758. Do not treat pageable host memory as device memory on HMM systems #2829 fixes it.libcuvs-examplesrebuildslibcuvs#2848. Fix reconstruction test for ScANN #2743 fixes it.CI watcher: finished after 16 checks. The 15 open fix PRs are green except #2801, which is still failing only on flake A.
Use a recall-based check in TestCuVSRandomizedHNSWVectorSearch (#2824)
[-][CI] Flaky test sweep: coordination for root-causing current CI flakes[/-][+][CI] Flaky test sweep (Sep–Oct 2026): root causes and fix PRs[/+]