diff --git a/engine/table/src/main/java/io/deephaven/engine/table/impl/asofjoin/RightIncrementalAsOfJoinStateManagerTypedBase.java b/engine/table/src/main/java/io/deephaven/engine/table/impl/asofjoin/RightIncrementalAsOfJoinStateManagerTypedBase.java index 9bd02ca47ca..7830ed374dd 100644 --- a/engine/table/src/main/java/io/deephaven/engine/table/impl/asofjoin/RightIncrementalAsOfJoinStateManagerTypedBase.java +++ b/engine/table/src/main/java/io/deephaven/engine/table/impl/asofjoin/RightIncrementalAsOfJoinStateManagerTypedBase.java @@ -129,7 +129,7 @@ protected long makeCookie(ImmutableLongArraySource cookieSource, int slot) { protected void migrateCookie(long cookie, int destinationLocation) { if (cookie >= cookieGeneration && cookie - cookieGeneration < nextCookie) { - hashSlots.set(cookie, destinationLocation | mainInsertMask); + hashSlots.set(cookie - cookieGeneration, destinationLocation | mainInsertMask); mainCookieSource.set(destinationLocation, cookie); } } diff --git a/engine/table/src/test/java/io/deephaven/engine/table/impl/QueryTableAjTest.java b/engine/table/src/test/java/io/deephaven/engine/table/impl/QueryTableAjTest.java index 419cddcce6b..aff123c4487 100644 --- a/engine/table/src/test/java/io/deephaven/engine/table/impl/QueryTableAjTest.java +++ b/engine/table/src/test/java/io/deephaven/engine/table/impl/QueryTableAjTest.java @@ -1425,7 +1425,7 @@ protected Table e() { * Reproduction of the error from DHC issue #3080. */ @Test - public void testIds3080() { + public void testDHC3080() { try (final SafeCloseable ignored = LivenessScopeStack.open()) { final int seed = 0; final Random random = new Random(seed); @@ -1458,4 +1458,53 @@ public void testIds3080() { true, true); } } + + /** + * Reproduction of the error from DHC issue #4700. The root cause is that the cookies were not being migrated + * properly during a partial rehash. This repro creates small initial tables, then generates large updates that + * force a partial rehash and migration. + */ + @Test + public void testDHC4700() { + try (final SafeCloseable ignored = LivenessScopeStack.open()) { + final int seed = 0; + final Random random = new Random(seed); + + final ColumnInfo[] leftColumnInfo; + final ColumnInfo[] rightColumnInfo; + + // Small initial tables. + final int leftSize = 2; + final int rightSize = 2; + final QueryTable leftTable = getTable(true, leftSize, random, + leftColumnInfo = initColumnInfos(new String[] {"Bucket", "LeftStamp", "LeftSentinel"}, + new StringGenerator(100_000), + new IntGenerator(0, 100_000), + new IntGenerator(10_000_000, 10_010_000))); + final QueryTable rightTable = getTable(true, rightSize, random, + rightColumnInfo = initColumnInfos(new String[] {"Bucket", "RightStamp", "RightSentinel"}, + new StringGenerator(100_000), + new SortedIntGenerator(0, 100_000), + new IntGenerator(20_000_000, 20_010_000))); + + final Table result = AsOfJoinHelper.asOfJoin(QueryTableJoinTest.SMALL_LEFT_CONTROL, leftTable, + (QueryTable) rightTable.reverse(), + MatchPairFactory.getExpressions("Bucket", "LeftStamp=RightStamp"), + MatchPairFactory.getExpressions("RightStamp", "RightSentinel"), SortingOrder.Descending, true); + + final ControlledUpdateGraph updateGraph = ExecutionContext.getContext().getUpdateGraph().cast(); + updateGraph.runWithinUnitTestCycle(() -> { + // Large updates to force a partial rehash. + GenerateTableUpdates.generateShiftAwareTableUpdates(GenerateTableUpdates.DEFAULT_PROFILE, 100_000, + random, leftTable, leftColumnInfo); + GenerateTableUpdates.generateShiftAwareTableUpdates(GenerateTableUpdates.DEFAULT_PROFILE, 100_000, + random, rightTable, rightColumnInfo); + }); + + // Compare results of the bucketed output. + checkAjResults(result.partitionBy("Bucket"), leftTable.partitionBy("Bucket"), + rightTable.partitionBy("Bucket"), + true, true); + } + } }