diff --git a/persistit/core/src/main/java/com/persistit/IOTaskRunnable.java b/persistit/core/src/main/java/com/persistit/IOTaskRunnable.java index a1d0ba57c..7230cc993 100644 --- a/persistit/core/src/main/java/com/persistit/IOTaskRunnable.java +++ b/persistit/core/src/main/java/com/persistit/IOTaskRunnable.java @@ -14,6 +14,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * Portions Copyrighted 2026 3A Systems, LLC */ package com.persistit; @@ -64,7 +65,7 @@ Thread getThread() { synchronized void kick() { if (!_notified) { _notified = true; - notify(); + notifyAll(); } } diff --git a/persistit/core/src/test/java/com/persistit/WarmupTest.java b/persistit/core/src/test/java/com/persistit/WarmupTest.java index 1d0141aae..67666e0a8 100644 --- a/persistit/core/src/test/java/com/persistit/WarmupTest.java +++ b/persistit/core/src/test/java/com/persistit/WarmupTest.java @@ -20,9 +20,10 @@ import org.junit.Test; +import java.util.HashSet; import java.util.Properties; +import java.util.Set; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; public class WarmupTest extends PersistitUnitTestCase { @@ -44,24 +45,48 @@ public void testWarmup() throws Exception { ex.clear().append(i).store(); } - final Buffer[] buff = new Buffer[100]; - for (int i = 0; i < pool.getBufferCount(); ++i) { - buff[i] = pool.getBufferCopy(i); - } + /* + * Warmup (bufferinventory + bufferpreload) guarantees that the pages + * resident in the pool at shutdown are read back into the pool on restart. + * It does not guarantee that a page returns to the same buffer slot: + * preloadBufferInventory() sorts the recorded pages by read order and + * reallocates buffers via the clock algorithm, so the slot index is not + * preserved. Compare the set of resident pages, not per-slot correspondence + * (the latter only happened to match and made the test flaky). + */ + final Set before = residentPages(pool); + assertTrue("Test setup should leave pages resident in the pool", !before.isEmpty()); ex = null; _persistit.close(); _persistit = new Persistit(_config); - ex = _persistit.getExchange("persistit", "WarmupTest", false); - pool = ex.getBufferPool(); - - for (int i = 0; i < pool.getBufferCount(); ++i) { - final Buffer bufferCopy = pool.getBufferCopy(i); - assertEquals(bufferCopy.getPageAddress(), buff[i].getPageAddress()); - assertEquals(bufferCopy.getPageType(), buff[i].getPageType()); - assertEquals(bufferCopy.getBufferSize(), buff[i].getBufferSize()); + pool = _persistit.getVolume("persistit").getStructure().getPool(); + + final Set after = residentPages(pool); + final Set missing = new HashSet(before); + missing.removeAll(after); + assertTrue("Warmup should preload every previously-resident page; missing=" + missing, + missing.isEmpty()); + } + + /** + * Collects an identity ({@code volume:page:type:size}) for every valid, + * recordable buffer currently resident in the pool. Mirrors the filtering in + * {@link BufferPool#recordBufferInventory}, which skips temporary and lock + * volumes, so the returned set is exactly what warmup is expected to reload. + */ + private static Set residentPages(final BufferPool pool) { + final Set pages = new HashSet(); + for (int i = 0; i < pool.getBufferCount(); i++) { + final Buffer b = pool.getBufferCopy(i); + final Volume volume = b.getVolume(); + if (b.isValid() && volume != null && !volume.isTemporary() && !volume.isLockVolume()) { + pages.add(volume.getName() + ':' + b.getPageAddress() + ':' + b.getPageType() + ':' + + b.getBufferSize()); + } } + return pages; } @Test