This is an automated email from the ASF dual-hosted git repository. jonmeredith pushed a commit to branch trunk in repository https://gitbox.apache.org/repos/asf/cassandra.git
commit 32cfd477df72eab261eaf4a6f16aeb3a892b1e52 Merge: 17cb89208c 2ac701629b Author: Jon Meredith <jonmered...@apache.org> AuthorDate: Tue Apr 22 14:42:56 2025 -0600 Merge branch 'cassandra-4.1' into cassandra-5.0 CHANGES.txt | 1 + src/java/org/apache/cassandra/db/LivenessInfo.java | 11 ++- src/java/org/apache/cassandra/db/rows/Cells.java | 10 +++ test/unit/org/apache/cassandra/db/CellTest.java | 84 ++++++++++++++++++++++ .../org/apache/cassandra/db/LivenessInfoTest.java | 5 ++ .../org/apache/cassandra/db/rows/RowsTest.java | 54 ++++++++++++++ 6 files changed, 163 insertions(+), 2 deletions(-) diff --cc CHANGES.txt index 2e5aeed0b5,afb99c4d0b..462d3ed74f --- a/CHANGES.txt +++ b/CHANGES.txt @@@ -1,35 -1,5 +1,36 @@@ -4.1.9 +5.0.5 * Grant permission on keyspaces system_views and system_virtual_schema not possible (CASSANDRA-20171) + * Fix marking an SSTable as suspected and BufferPool leakage in case of a corrupted SSTable read during a compaction (CASSANDRA-20396) + * Introduce SSTableSimpleScanner for compaction (CASSANDRA-20092) + * Avoid purging deletions in RowFilter when reconciliation is required (CASSANDRA-20541) + * Fixed multiple single-node SAI query bugs relating to static columns (CASSANDRA-20338) + * Upgrade com.datastax.cassandra:cassandra-driver-core:3.11.5 to org.apache.cassandra:cassandra-driver-core:3.12.1 (CASSANDRA-17231) +Merged from 4.0: ++ * Updating a column with a new TTL but same expiration time is non-deterministic and causes repair mismatches. (CASSANDRA-20561) + * Avoid computing prepared statement size for unprepared batches (CASSANDRA-20556) + * Fix Dropwizard Meter causes timeouts when infrequently used (CASSANDRA-19332) + + +5.0.4 + * Update netty to 4.1.119.Final and netty-tcnative to 2.0.70.Final (CASSANDRA-20314) + * Serialization can lose complex deletions in a mutation with multiple collections in a row (CASSANDRA-20449) + * Improve error messages when initializing auth classes (CASSANDRA-20368) + * Prioritize legacy 2i over SAI for columns with multiple indexes (CASSANDRA-20334) + * Ensure only offline tools can build IntervalTrees without first/last key fields (CASSANDRA-20407) + * Improve disk access patterns during compaction and range reads (CASSANDRA-15452) + * Improve IntervalTree build throughput (CASSANDRA-19596) + * Avoid limit on RFP fetch in the case of an unresolved static row (CASSANDRA-20323) + * Include materialized views to the output of DESCRIBE TABLE statements (CASSANDRA-20365) + * Heap and GC jvm flags improvements (CASSANDRA-20296) + * Fix unparseable YAML in default cassandra.yaml when uncommented for downstream tooling (CASSANDRA-20359) + * Avoid fetching entire partitions on unresolved static rows in RFP when no static column predicates exist (CASSANDRA-20243) + * Avoid indexing empty values for non-literals and types that do not allow them (CASSANDRA-20313) + * Fix incorrect results of min / max in-built functions on clustering columns in descending order (CASSANDRA-20295) + * Avoid possible consistency violations for SAI intersection queries over repaired index matches and multiple non-indexed column matches (CASSANDRA-20189) + * Skip check for DirectIO when initializing tools (CASSANDRA-20289) + * Avoid under-skipping during intersections when an iterator has mixed STATIC and WIDE keys (CASSANDRA-20258) + * Correct the default behavior of compareTo() when comparing WIDE and STATIC PrimaryKeys (CASSANDRA-20238) +Merged from 4.1: * Fix mixed mode paxos ttl commit hang (CASSANDRA-20514) * Fix paxos mixed mode infinite loop (CASSANDRA-20493) * Optionally skip exception logging on invalid legacy protocol magic exception (CASSANDRA-19483) diff --cc test/unit/org/apache/cassandra/db/CellTest.java index 3a9e0d0dc0,b4cca19920..b9ab25fe50 --- a/test/unit/org/apache/cassandra/db/CellTest.java +++ b/test/unit/org/apache/cassandra/db/CellTest.java @@@ -48,10 -39,10 +48,12 @@@ import org.apache.cassandra.schema.Keys import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.serializers.MarshalException; import org.apache.cassandra.utils.ByteBufferUtil; + import org.apache.cassandra.utils.FBUtilities; +import org.assertj.core.api.Assertions; +import org.assertj.core.api.ThrowableAssert; import static java.util.Arrays.asList; + import static org.junit.Assert.assertEquals; public class CellTest { @@@ -282,11 -276,91 +286,91 @@@ Assert.assertEquals(-1, testExpiring("val", "b", 2, 1, null, "a", null, 1)); } + + public static void assertCellsEqual(Cell<?> cellA, Cell<?> cellB) + { + assertEquals(cellA.timestamp(), cellB.timestamp()); + assertEquals(cellA.ttl(), cellB.ttl()); + assertEquals(cellA.localDeletionTime(), cellB.localDeletionTime()); + assertEquals(cellA.buffer(), cellB.buffer()); + } + - static void checkCommutes(ColumnMetadata cmd, long timestamp, long tsDiff, int ttl, int ttlDiff, int nowInSeconds, int nowDiff) ++ static void checkCommutes(ColumnMetadata cmd, long timestamp, long tsDiff, int ttl, int ttlDiff, long nowInSeconds, int nowDiff) + { + long timestampA = timestamp; + long timestampB = timestampA + tsDiff; + int ttlA = ttl; + int ttlB = ttl + ttlDiff; - int nowInSecsA = nowInSeconds; - int nowInSecsB = nowInSecsA + nowDiff; ++ long nowInSecsA = nowInSeconds; ++ long nowInSecsB = nowInSecsA + nowDiff; + if (nowInSecsA < 0 || nowInSecsB < 0) + return; + + Cell<?> cellA = ttlA == 0 ? BufferCell.tombstone(cmd, timestampA, nowInSecsA) : + ttlA < 0 ? BufferCell.live(cmd, timestampA, TEST_VALUE) : + BufferCell.expiring(cmd, timestampA, ttlA, nowInSecsA, TEST_VALUE); + Cell<?> cellB = ttlB == 0 ? BufferCell.tombstone(cmd, timestampB, nowInSecsB) : + ttlB < 0 ? BufferCell.live(cmd, timestampB, TEST_VALUE) : + BufferCell.expiring(cmd, timestampB, ttlB, nowInSecsB, TEST_VALUE); + + Cell<?> cellAB = Cells.reconcile(cellA, cellB); + Cell<?> cellBA = Cells.reconcile(cellB, cellA); + + assertCellsEqual(cellAB, cellBA); + } + + @Test + public void checkSameValueDifferentLivenessCommutes() + { + ColumnMetadata cmd = fakeColumn("c", UTF8Type.instance); + long[] tsDiffs = new long[] {0L, + 1L, // microsecond + 1000L, // millisecond + 1000000L, // second + 60000000L}; // minute + int[] ttls = new int[] { -1, 0, 1, 3600, 24 * 3600, 7 * 24 * 3600, 60 * 24 * 3600, 366 * 24 * 3600 }; + int[] ttlDiffs = new int[] { 0, 1, 60, 3600, 24 * 3600, 7 * 24 * 3600, 60 * 24 * 3600, 366 * 24 * 3600 }; + - int nowInSeconds = FBUtilities.nowInSeconds(); ++ long nowInSeconds = FBUtilities.nowInSeconds(); + long timestamp = FBUtilities.timestampMicros(); + + for (long tsDiff: tsDiffs) + { + for (int ttl: ttls) + { + for (int ttlDiff : ttlDiffs) + { + for (Integer nowDiff : ttlDiffs) + checkCommutes(cmd, timestamp, tsDiff, ttl, ttlDiff, nowInSeconds, nowDiff); + } + } + } + } + + // Checks that reconciling a cell with a smaller TTL reconcile commutatively + // Similar to rewriting data retrieved with SELECT v, TTL(v), WRITETIMESTAMP(v) with + // INSERT SET v=? USING TTL ? AND TIMESTAMP ? + @Test + public void rewriteCellWithSmallerTTL() + { + ColumnMetadata cmd = fakeColumn("c", UTF8Type.instance); + int[] nowDiffs = new int[] { 0, 1, 60, 3600, 24 * 3600, 7 * 24 * 3600, 60 * 24 * 3600, 366 * 24 * 3600 }; + long timestamp = FBUtilities.timestampMicros(); - int nowInSeconds = FBUtilities.nowInSeconds(); ++ long nowInSeconds = FBUtilities.nowInSeconds(); + int ttl = 3600; + + for (Integer nowDiff : nowDiffs) + { + checkCommutes(cmd, timestamp, 0L, ttl, -nowDiff, nowInSeconds, nowDiff); + } + } + + class SimplePurger implements DeletionPurger { - private final int gcBefore; + private final long gcBefore; - public SimplePurger(int gcBefore) + public SimplePurger(long gcBefore) { this.gcBefore = gcBefore; } diff --cc test/unit/org/apache/cassandra/db/rows/RowsTest.java index a4436da88a,47e42b2542..865d33a8b6 --- a/test/unit/org/apache/cassandra/db/rows/RowsTest.java +++ b/test/unit/org/apache/cassandra/db/rows/RowsTest.java @@@ -526,6 -522,58 +528,58 @@@ public class RowsTes Assert.assertEquals(0, merged.columns().size()); } + - public static BufferCell expiringWithExpirationTime(ColumnMetadata column, long timestamp, int ttl, int localDeletionTime, ByteBuffer value) ++ public static BufferCell expiringWithExpirationTime(ColumnMetadata column, long timestamp, int ttl, long localDeletionTime, ByteBuffer value) + { + return expiringWithExpirationTime(column, timestamp, ttl, localDeletionTime, value, null); + } + - public static BufferCell expiringWithExpirationTime(ColumnMetadata column, long timestamp, int ttl, int localDeletionTime, ByteBuffer value, CellPath path) ++ public static BufferCell expiringWithExpirationTime(ColumnMetadata column, long timestamp, int ttl, long localDeletionTime, ByteBuffer value, CellPath path) + { + assert ttl != Cell.NO_TTL; + return new BufferCell(column, timestamp, ttl, localDeletionTime, value, path); + } + + @Test + public void mergeRowsWithSameExpiryDifferentTTLCommutesLiveness() + { - int now1 = FBUtilities.nowInSeconds(); ++ long now1 = FBUtilities.nowInSeconds(); + long ts1 = secondToTs(now1); - int ldt = now1 + 1000; ++ long ldt = now1 + 1000; + + Row.Builder r1Builder = BTreeRow.unsortedBuilder(); + r1Builder.newRow(c1); + LivenessInfo originalLiveness = LivenessInfo.withExpirationTime(ts1, 100, ldt); + r1Builder.addPrimaryKeyLivenessInfo(originalLiveness); + + Row.Builder r2Builder = BTreeRow.unsortedBuilder(); + r2Builder.newRow(c1); + LivenessInfo loweredTTL = LivenessInfo.withExpirationTime(ts1, 50, ldt); + r2Builder.addPrimaryKeyLivenessInfo(loweredTTL); + + Cell<?> r2v = expiringWithExpirationTime(v, ts1, 75, ldt, BB1); + Cell<?> r2m2 = expiringWithExpirationTime(m, ts1, 50, ldt, BB1, CellPath.create(BB2)); + Cell<?> r2m3 = expiringWithExpirationTime(m, ts1, 75, ldt, BB2, CellPath.create(BB3)); + Cell<?> r2m4 = expiringWithExpirationTime(m, ts1, 100, ldt, BB3, CellPath.create(BB4)); + List<Cell<?>> expectedCells = Lists.newArrayList(r2v, r2m2, r2m3, r2m4); + + expectedCells.forEach(r1Builder::addCell); + expectedCells.forEach(r2Builder::addCell); + + Row r1 = r1Builder.build(); + Row r2 = r2Builder.build(); + + Row r1r2 = Rows.merge(r1, r2); + Row r2r1 = Rows.merge(r2, r1); + + DiffListener mergedListener = new DiffListener(); + Rows.diff(mergedListener, r1r2, r2r1); + + mergedListener.liveness.forEach(pair -> Assert.assertEquals(pair.merged, pair.original)); + mergedListener.cells.forEach(pair -> assertCellsEqual(pair.merged, pair.original)); + } + + // Creates a dummy cell for a (regular) column for the provided name and without a cellPath. private static Cell<?> liveCell(ColumnMetadata name) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org For additional commands, e-mail: commits-h...@cassandra.apache.org