dcapwell commented on code in PR #4905:
URL: https://github.com/apache/cassandra/pull/4905#discussion_r3501253903


##########
test/unit/org/apache/cassandra/index/sai/memory/VectorMemoryIndexTest.java:
##########
@@ -182,18 +198,489 @@ public void randomQueryTest() throws Exception
                 expectedVectorsReturned += expectedResults;
                 if (foundKeys.size() < expectedResults)
                     assertTrue("Expected at least " + expectedResults + " 
results but got " + foundKeys.size(),
-                               foundKeys.size() >= expectedResults * 
expectedRecall);
+                               foundKeys.size() >= expectedResults * 
RECALL_THRESHOLD);
             }
         }
 
         assertTrue("Expected at least " + expectedVectorsReturned + " results 
but got " + actualVectorsReturned,
-                   actualVectorsReturned >= expectedVectorsReturned * 
expectedRecall);
+                   actualVectorsReturned >= expectedVectorsReturned * 
RECALL_THRESHOLD);
+    }
+
+    /**
+     * Verifies that concurrent calls to add() do not corrupt the graph or 
lose data.
+     * <p>
+     * GraphIndexBuilder.addGraphNode() is designed for concurrent use: 
insertionsInProgress
+     * is a ConcurrentSkipListSet, and PoolingSupport gives each thread its 
own GraphSearcher
+     * and scratch arrays. This test validates the full stack from 
VectorMemoryIndex.index()
+     * through OnHeapGraph.add() through GraphIndexBuilder.addGraphNode().
+     * <p>
+     * Each thread owns a disjoint range of partition key integers so every 
insert is a
+     * pure add with no PK collisions, isolating add() from update() semantics.
+     * <p>
+     * After all writers complete, a full-ring search must return the vast 
majority of
+     * inserted keys with valid scores, confirming no data was lost or 
corrupted.
+     */
+    @Test
+    public void testConcurrentAddsProduceConsistentFinalState() throws 
Exception
+    {
+        Memtable memtable = Mockito.mock(Memtable.class);
+        memtableIndex = new VectorMemoryIndex(index, memtable);
+
+        int numThreads = Runtime.getRuntime().availableProcessors();
+        int vectorsPerThread = 2000;
+        int totalInserted = numThreads * vectorsPerThread;
+
+        ExecutorService executor = Executors.newFixedThreadPool(numThreads);
+        // CyclicBarrier ensures all threads begin inserting simultaneously,
+        // maximizing contention on GraphIndexBuilder and 
ConcurrentVectorValues.
+        CyclicBarrier barrier = new CyclicBarrier(numThreads);
+        List<Future<?>> futures = new ArrayList<>();
+
+        for (int t = 0; t < numThreads; t++)
+        {
+            final int threadId = t;
+            futures.add(executor.submit(() -> {
+                try
+                {
+                    barrier.await();
+                    for (int i = 0; i < vectorsPerThread; i++)
+                    {
+                        // Partition key is globally unique across all threads
+                        int pk = threadId * vectorsPerThread + i;
+                        addRow(pk, randomVectorFromThreadLocal());

Review Comment:
   I feel that `randomVectorFromThreadLocal` isn't the best option here.  We 
now allow `add` to overlap which means all protection from that is now lost.  
So we should also include *distinct*  PKs that haver the *same* vectors... In 
the common case all vectors are not equal so `OnHeapGraph.add()` almost never 
tests the branch where a conflict happens.
   
   what if we ran this test twice, but this logic is pluggable?  one run does 
random like you do and the other creates vectors like this
   
   ```
   [0.5, 05, ..., <i/vectorsPerThread>]
   ```
   
   with that encoding each thread *will* produce the same vectors, allowing 
this race to happen?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to