From e00532c042aa49307f71a2e655ad38adcc481f27 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 10 Jul 2026 09:19:31 +0300 Subject: [PATCH 1/2] Stabilize flaky TreeTransactionalLifetimeTest.createRemoveByStep The crash/restart branches of createRemoveByStepHelper reopen Persistit and return without waiting for recovery and timely-resource pruning to settle. Those run asynchronously after initialize(), so the next helper iteration can begin a transaction against not-yet-settled state, and residual tree-version state from the previous iteration's crash/restart leaks into the new transaction's step-based MVCC visibility -- a tree created at step 1 becomes visible at step 0. That produced an intermittent ComparisonFailure ("expected:<0[]...> but was:<0[:]...>") on CI (ubuntu-latest JDK 21); it does not reproduce on macOS. Quiesce the MVCC state after the restart by updating the active-transaction cache and pruning timely resources -- the same idiom the sibling tests in this class already use before asserting. --- .../TreeTransactionalLifetimeTest.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java b/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java index 2f9109dbc..3fde3522d 100644 --- a/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java +++ b/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java @@ -1,6 +1,8 @@ /** * Copyright 2013 Akiban Technologies, Inc. - * + * + * Portions Copyrighted 2026 3A Systems, LLC. + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -235,6 +237,19 @@ private void createRemoveByStepHelper(final String treeName, final boolean primo if (crash || restart) { _persistit = new Persistit(_config); _persistit.initialize(); + /* + * Recovery and the pruning of timely tree resources run + * asynchronously after initialize(). If the next helper iteration + * starts a transaction before that settles, residual tree-version + * state from this iteration's crash/restart can leak into its + * step-based MVCC visibility -- e.g. a tree created at step 1 shows + * up at step 0 -- producing an intermittent ComparisonFailure + * ("expected:<0[]...> but was:<0[:]...>") seen on CI. Settle the + * active-transaction cache and prune timely resources here, the same + * way the sibling tests quiesce MVCC state before asserting. + */ + _persistit.getTransactionIndex().updateActiveTransactionCache(); + _persistit.pruneTimelyResources(); } assertEquals("Expected contents at steps", expected2, computeCreateRemoveState(treeName, 1)); } From 28aebabd439070dbf09ca0c0170a2d97f9537407 Mon Sep 17 00:00:00 2001 From: Valera V Harseko Date: Fri, 10 Jul 2026 18:18:11 +0300 Subject: [PATCH 2/2] Reword quiesce comment: recovery is synchronous; the async actors are the ATC updater and CleanupManager pruning Per review on #257: initialize() applies recovered transactions inline, so blaming asynchronous recovery was wrong and would send a maintainer hunting a race that does not exist. The asynchronous actors that make the settle calls necessary are the TransactionIndex active-transaction-cache updater thread and the CleanupManager's timer-driven pruneTimelyResources(). Comment-only change. --- .../TreeTransactionalLifetimeTest.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java b/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java index 3fde3522d..55e3a49c2 100644 --- a/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java +++ b/persistit/core/src/test/java/com/persistit/TreeTransactionalLifetimeTest.java @@ -238,15 +238,17 @@ private void createRemoveByStepHelper(final String treeName, final boolean primo _persistit = new Persistit(_config); _persistit.initialize(); /* - * Recovery and the pruning of timely tree resources run - * asynchronously after initialize(). If the next helper iteration - * starts a transaction before that settles, residual tree-version - * state from this iteration's crash/restart can leak into its - * step-based MVCC visibility -- e.g. a tree created at step 1 shows - * up at step 0 -- producing an intermittent ComparisonFailure - * ("expected:<0[]...> but was:<0[:]...>") seen on CI. Settle the - * active-transaction cache and prune timely resources here, the same - * way the sibling tests quiesce MVCC state before asserting. + * Recovery itself completes synchronously inside initialize(), but + * two maintenance tasks are asynchronous after it: the + * TransactionIndex's active-transaction-cache updater thread and + * the CleanupManager's timer-driven pruneTimelyResources(). If the + * next helper iteration starts a transaction before those settle, + * residual tree-version state from this iteration's crash/restart + * can leak into its step-based MVCC visibility -- e.g. a tree + * created at step 1 shows up at step 0 -- producing an intermittent + * ComparisonFailure ("expected:<0[]...> but was:<0[:]...>") seen on + * CI. Force both here, the same way the sibling tests quiesce MVCC + * state before asserting. */ _persistit.getTransactionIndex().updateActiveTransactionCache(); _persistit.pruneTimelyResources();