I think it's no longer reproducible because I added a synchronized around
the test logging method. It should be resolved now?

On Tue, Apr 14, 2026, 8:55 PM Dawid Weiss <[email protected]> wrote:

>
> I can't reproduce this, even when looping, but your analysis seems
> correct, neoremind. I filed this -
>
> https://github.com/apache/lucene/pull/15957
>
> Dawid
>
> On Wed, Apr 8, 2026 at 7:17 AM xu <[email protected]> wrote:
>
>> I think the merge itself isn't actually getting skipped, it's a
>> thread-safety issue in the test.
>> The test collects InfoStream messages into a ArrayList<String>, but with
>> ConcurrentMergeScheduler, the "merged new segment" message gets written by
>> the merge background thread. One thread writes to a non-thread-safe list,
>> the other test thread reads, oops, visibility problem in JMM. To address,
>> simply change:
>>
>> List<String> infoStream = new ArrayList<>();
>>
>> to:
>>
>> List<String> infoStream = Collections.synchronizedList(new ArrayList<>());
>>
>> I can reproduce this by putting the test in a for-loop running 10k times,
>> it shows roughly 0.3% chance of failure.
>>
>> To verify the theory, I quickly create a JCStress test (a dummy one
>> though, but it illustrates the point). It shows the write from one thread
>> is completely or partially invisible to the reader thread:
>>
>> @JCStressTest
>> @State
>> @Outcome(id = "1, 42",  expect = Expect.ACCEPTABLE, desc = "Fully visible
>> write.")
>> @Outcome(id = "0, -1",  expect = Expect.ACCEPTABLE, desc = "Write not
>> visible at all.")
>> @Outcome(              expect = Expect.ACCEPTABLE_INTERESTING, desc =
>> "Partial / stale visibility.")
>> public class ArrayListVisibilityTest {
>>
>>     final List<Integer> list = new ArrayList<>();
>>
>>     @Actor
>>     public void writer() {
>>         list.add(42);
>>     }
>>
>>     @Actor
>>     public void reader(II_Result r) {
>>         int size = list.size();
>>         r.r1 = size;
>>         try {
>>             r.r2 = size > 0 ? list.get(0) : -1;
>>         } catch (Exception e) {
>>             // ArrayIndexOutOfBoundsException is possible under races
>>             r.r2 = -2;
>>         }
>>     }
>> }
>>
>> ```
>> RESULT              SAMPLES     FREQ       EXPECT  DESCRIPTION
>> 0, -1         1,838,722,183   67.78%   Acceptable  Write not visible at
>> all.
>> 1, -2               106,178   <0.01%  Interesting  Partial / stale
>> visibility.
>> 1, 42           873,770,813   32.21%   Acceptable  Fully visible write.
>> ```
>> In practice, a lock (or a memory barrier) would guarantee visibility, in
>> essence, the merge thread's CPU flushes writes to store buffer, and the
>> test thread's CPU is able to snoop the invalid to get the freshly added
>> message (at least this should be how things work on x86).
>>
>> Thanks,
>> neoremind
>>
>> Michael Sokolov <[email protected]> 于2026年4月6日周一 09:52写道:
>>
>>> I just added this TestInfoStream.testMergeSmallIndex test so I thought
>>> I'd try to reproduce, but it didn't repro for me.  The failure
>>> indicates that no segment merge was logged. I'm not sure how it could
>>> get skipped though.
>>>
>>> On Sun, Apr 5, 2026 at 1:57 PM Policeman Jenkins Server via builds
>>> <[email protected]> wrote:
>>> >
>>> > Build: https://jenkins.thetaphi.de/job/Lucene-MMAPv2-Linux/5230/
>>> > Java: 64bit/hotspot/jdk-27-ea+14 -XX:-UseCompressedOops -XX:+UseZGC
>>> >
>>> > 3 tests failed.
>>> > FAILED:  org.apache.lucene.index.TestInfoStream.testMergeSmallIndex
>>> >
>>> > Error Message:
>>> > java.lang.AssertionError: expected:<1> but was:<0>
>>> >
>>> > Stack Trace:
>>> > java.lang.AssertionError: expected:<1> but was:<0>
>>> >         at
>>> __randomizedtesting.SeedInfo.seed([F77FD74A101B3B0B:45303098296E3A19]:0)
>>> >         at org.junit.Assert.fail(Assert.java:89)
>>> >         at org.junit.Assert.failNotEquals(Assert.java:835)
>>> >         at org.junit.Assert.assertEquals(Assert.java:647)
>>> >         at org.junit.Assert.assertEquals(Assert.java:633)
>>> >         at
>>> org.apache.lucene.index.TestInfoStream.testMergeSmallIndex(TestInfoStream.java:151)
>>> >         at
>>> java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
>>> >         at java.base/java.lang.reflect.Method.invoke(Method.java:565)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner.invoke(RandomizedRunner.java:1763)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$8.evaluate(RandomizedRunner.java:946)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$9.evaluate(RandomizedRunner.java:982)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$10.evaluate(RandomizedRunner.java:996)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:48)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:45)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
>>> >         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl.forkTimeoutingTask(ThreadLeakControl.java:843)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$3.evaluate(ThreadLeakControl.java:490)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner.runSingleTest(RandomizedRunner.java:955)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$5.evaluate(RandomizedRunner.java:840)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$6.evaluate(RandomizedRunner.java:891)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$7.evaluate(RandomizedRunner.java:902)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:38)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:52)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:47)
>>> >         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl.lambda$forkTimeoutingTask$0(ThreadLeakControl.java:850)
>>> >         at java.base/java.lang.Thread.run(Thread.java:1527)
>>> >
>>> >
>>> > FAILED:
>>> org.apache.lucene.tests.store.TestMockDirectoryWrapper.testIsLoaded
>>> >
>>> > Error Message:
>>> > java.lang.AssertionError
>>> >
>>> > Stack Trace:
>>> > java.lang.AssertionError
>>> >         at
>>> __randomizedtesting.SeedInfo.seed([F77FD74A101B3B0B:D95E8D3E0CAB8C7C]:0)
>>> >         at org.junit.Assert.fail(Assert.java:87)
>>> >         at org.junit.Assert.assertTrue(Assert.java:42)
>>> >         at org.junit.Assert.assertTrue(Assert.java:53)
>>> >         at
>>> org.apache.lucene.tests.store.BaseDirectoryTestCase.testIsLoaded(BaseDirectoryTestCase.java:1663)
>>> >         at
>>> org.apache.lucene.tests.store.BaseDirectoryTestCase.testIsLoaded(BaseDirectoryTestCase.java:1623)
>>> >         at
>>> java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
>>> >         at java.base/java.lang.reflect.Method.invoke(Method.java:565)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner.invoke(RandomizedRunner.java:1763)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$8.evaluate(RandomizedRunner.java:946)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$9.evaluate(RandomizedRunner.java:982)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$10.evaluate(RandomizedRunner.java:996)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:48)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:45)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
>>> >         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl.forkTimeoutingTask(ThreadLeakControl.java:843)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$3.evaluate(ThreadLeakControl.java:490)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner.runSingleTest(RandomizedRunner.java:955)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$5.evaluate(RandomizedRunner.java:840)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$6.evaluate(RandomizedRunner.java:891)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$7.evaluate(RandomizedRunner.java:902)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:38)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:52)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:47)
>>> >         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl.lambda$forkTimeoutingTask$0(ThreadLeakControl.java:850)
>>> >         at java.base/java.lang.Thread.run(Thread.java:1527)
>>> >
>>> >
>>> > FAILED:
>>> org.apache.lucene.tests.store.TestSerializedIOCountingDirectory.testIsLoaded
>>> >
>>> > Error Message:
>>> > java.lang.AssertionError
>>> >
>>> > Stack Trace:
>>> > java.lang.AssertionError
>>> >         at
>>> __randomizedtesting.SeedInfo.seed([F77FD74A101B3B0B:D95E8D3E0CAB8C7C]:0)
>>> >         at org.junit.Assert.fail(Assert.java:87)
>>> >         at org.junit.Assert.assertTrue(Assert.java:42)
>>> >         at org.junit.Assert.assertTrue(Assert.java:53)
>>> >         at
>>> org.apache.lucene.tests.store.BaseDirectoryTestCase.testIsLoaded(BaseDirectoryTestCase.java:1663)
>>> >         at
>>> org.apache.lucene.tests.store.BaseDirectoryTestCase.testIsLoaded(BaseDirectoryTestCase.java:1623)
>>> >         at
>>> java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
>>> >         at java.base/java.lang.reflect.Method.invoke(Method.java:565)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner.invoke(RandomizedRunner.java:1763)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$8.evaluate(RandomizedRunner.java:946)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$9.evaluate(RandomizedRunner.java:982)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$10.evaluate(RandomizedRunner.java:996)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleSetupTeardownChained$1.evaluate(TestRuleSetupTeardownChained.java:48)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleThreadAndTestName$1.evaluate(TestRuleThreadAndTestName.java:45)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
>>> >         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl.forkTimeoutingTask(ThreadLeakControl.java:843)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$3.evaluate(ThreadLeakControl.java:490)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner.runSingleTest(RandomizedRunner.java:955)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$5.evaluate(RandomizedRunner.java:840)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$6.evaluate(RandomizedRunner.java:891)
>>> >         at
>>> com.carrotsearch.randomizedtesting.RandomizedRunner$7.evaluate(RandomizedRunner.java:902)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleStoreClassName$1.evaluate(TestRuleStoreClassName.java:38)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.NoShadowingOrOverridesOnMethodsRule$1.evaluate(NoShadowingOrOverridesOnMethodsRule.java:40)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleAssertionsRequired$1.evaluate(TestRuleAssertionsRequired.java:52)
>>> >         at
>>> org.apache.lucene.tests.util.AbstractBeforeAfterRule$1.evaluate(AbstractBeforeAfterRule.java:43)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleMarkFailure$1.evaluate(TestRuleMarkFailure.java:44)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreAfterMaxFailures$1.evaluate(TestRuleIgnoreAfterMaxFailures.java:60)
>>> >         at
>>> org.apache.lucene.tests.util.TestRuleIgnoreTestSuites$1.evaluate(TestRuleIgnoreTestSuites.java:47)
>>> >         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
>>> >         at
>>> com.carrotsearch.randomizedtesting.rules.StatementAdapter.evaluate(StatementAdapter.java:36)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl$StatementRunner.run(ThreadLeakControl.java:390)
>>> >         at
>>> com.carrotsearch.randomizedtesting.ThreadLeakControl.lambda$forkTimeoutingTask$0(ThreadLeakControl.java:850)
>>> >         at java.base/java.lang.Thread.run(Thread.java:1527)
>>> >
>>> > ---------------------------------------------------------------------
>>> > To unsubscribe, e-mail: [email protected]
>>> > For additional commands, e-mail: [email protected]
>>>
>>> ---------------------------------------------------------------------
>>> To unsubscribe, e-mail: [email protected]
>>> For additional commands, e-mail: [email protected]
>>>
>>>
>>
>> --
>>
>> Best regards,
>>
>> Xu
>>
>

Reply via email to