diff --git a/CHANGES.txt b/CHANGES.txt index 2a39a3cb4053..0f61587cd198 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 5.1 + * Introduce created_at column to system_distributed.compression_dictionaries (CASSANDRA-21178) * Be able to detect and remove orphaned compression dictionaries (CASSANDRA-21157) * Fix BigTableVerifier to only read a data file during extended verification (CASSANDRA-21150) * Reduce memory allocation during transformation of BatchStatement to Mutation (CASSANDRA-21141) diff --git a/src/java/org/apache/cassandra/db/compression/CompressionDictionary.java b/src/java/org/apache/cassandra/db/compression/CompressionDictionary.java index 30f37a0b5568..b947bb3a19cb 100644 --- a/src/java/org/apache/cassandra/db/compression/CompressionDictionary.java +++ b/src/java/org/apache/cassandra/db/compression/CompressionDictionary.java @@ -22,6 +22,7 @@ import java.io.DataOutput; import java.io.EOFException; import java.io.IOException; +import java.time.Instant; import java.util.Objects; import javax.annotation.Nullable; @@ -34,6 +35,7 @@ import org.apache.cassandra.cql3.UntypedResultSet; import org.apache.cassandra.io.compress.ICompressor; import org.apache.cassandra.io.compress.ZstdDictionaryCompressor; +import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.concurrent.Ref; /** @@ -119,6 +121,14 @@ default Kind kind() return dictId().kind; } + /** + * Returns a date of creation of this dictionary. By creation, we mean when + * the compression object was instantiated. + * + * @return when was this dictionary created. + */ + Instant createdAt(); + /** * Returns a reference from lazily initialized reference counter. * @@ -262,6 +272,7 @@ static LightweightCompressionDictionary createFromRowLightweight(UntypedResultSe String keyspaceName = row.getString("keyspace_name"); String tableName = row.getString("table_name"); String tableId = row.getString("table_id"); + Instant createdAt = row.getTimestamp("created_at").toInstant(); try { @@ -270,7 +281,8 @@ static LightweightCompressionDictionary createFromRowLightweight(UntypedResultSe tableId, new DictId(CompressionDictionary.Kind.valueOf(kindStr), dictId), checksum, - size); + size, + createdAt); } catch (IllegalArgumentException ex) { @@ -285,6 +297,7 @@ static CompressionDictionary createFromRow(UntypedResultSet.Row row) byte[] dict = row.getByteArray("dict"); int storedLength = row.getInt("dict_length"); int storedChecksum = row.getInt("dict_checksum"); + Instant createdAt = row.getTimestamp("created_at").toInstant(); try { @@ -305,7 +318,7 @@ static CompressionDictionary createFromRow(UntypedResultSet.Row row) kindStr, dictId, storedChecksum, calculatedChecksum)); } - return kind.createDictionary(new DictId(kind, dictId), row.getByteArray("dict"), storedChecksum); + return kind.createDictionary(new DictId(kind, dictId), row.getByteArray("dict"), storedChecksum, createdAt); } catch (IllegalArgumentException ex) { @@ -330,9 +343,9 @@ enum Kind ZSTD { @Override - public CompressionDictionary createDictionary(DictId dictId, byte[] dict, int checksum) + public CompressionDictionary createDictionary(DictId dictId, byte[] dict, int checksum, Instant createdAt) { - return new ZstdCompressionDictionary(dictId, dict, checksum); + return new ZstdCompressionDictionary(dictId, dict, checksum, createdAt); } @Override @@ -364,7 +377,21 @@ public ICompressionDictionaryTrainer createTrainer(String keyspaceName, * @param checksum checksum of this dictionary * @return a compression dictionary instance */ - public abstract CompressionDictionary createDictionary(CompressionDictionary.DictId dictId, byte[] dict, int checksum); + public CompressionDictionary createDictionary(CompressionDictionary.DictId dictId, byte[] dict, int checksum) + { + return createDictionary(dictId, dict, checksum, FBUtilities.now()); + } + + /** + * Creates a compression dictionary instance for this kind + * + * @param dictId the dictionary identifier + * @param dict the raw dictionary bytes + * @param checksum checksum of this dictionary + * @param createdAt creation date of to-be-constructed dictionary + * @return a compression dictionary instance + */ + public abstract CompressionDictionary createDictionary(CompressionDictionary.DictId dictId, byte[] dict, int checksum, Instant createdAt); /** * Creates a dictionary compressor for this kind @@ -436,13 +463,15 @@ class LightweightCompressionDictionary public final DictId dictId; public final int checksum; public final int size; + public final Instant createdAt; public LightweightCompressionDictionary(String keyspaceName, String tableName, String tableId, DictId dictId, int checksum, - int size) + int size, + Instant createdAt) { this.keyspaceName = keyspaceName; this.tableName = tableName; @@ -450,6 +479,7 @@ public LightweightCompressionDictionary(String keyspaceName, this.dictId = dictId; this.checksum = checksum; this.size = size; + this.createdAt = createdAt; } @Override @@ -462,6 +492,7 @@ public String toString() ", dictId=" + dictId + ", checksum=" + checksum + ", size=" + size + + ", createdAt=" + createdAt + '}'; } } diff --git a/src/java/org/apache/cassandra/db/compression/CompressionDictionaryDetailsTabularData.java b/src/java/org/apache/cassandra/db/compression/CompressionDictionaryDetailsTabularData.java index 588bb418d341..8b55abbb482c 100644 --- a/src/java/org/apache/cassandra/db/compression/CompressionDictionaryDetailsTabularData.java +++ b/src/java/org/apache/cassandra/db/compression/CompressionDictionaryDetailsTabularData.java @@ -18,6 +18,7 @@ package org.apache.cassandra.db.compression; +import java.time.Instant; import java.util.Arrays; import javax.management.openmbean.ArrayType; @@ -33,7 +34,6 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.cassandra.db.compression.CompressionDictionary.LightweightCompressionDictionary; -import org.apache.cassandra.io.util.FileUtils; import static java.lang.String.format; @@ -63,7 +63,7 @@ public class CompressionDictionaryDetailsTabularData public static final String KIND_NAME = "Kind"; public static final String CHECKSUM_NAME = "Checksum"; public static final String SIZE_NAME = "Size"; - + public static final String CREATED_AT_NAME = "CreatedAt"; private static final String[] ITEM_NAMES = new String[]{ KEYSPACE_NAME, TABLE_NAME, @@ -72,7 +72,8 @@ public class CompressionDictionaryDetailsTabularData DICT_NAME, KIND_NAME, CHECKSUM_NAME, - SIZE_NAME }; + SIZE_NAME, + CREATED_AT_NAME }; private static final String[] ITEM_DESCS = new String[]{ "keyspace", "table", @@ -81,7 +82,8 @@ public class CompressionDictionaryDetailsTabularData "dictionary_bytes", "kind", "checksum", - "size" }; + "size", + "created_at" }; private static final String TYPE_NAME = "DictionaryDetails"; private static final String ROW_DESC = "DictionaryDetails"; @@ -100,7 +102,8 @@ public class CompressionDictionaryDetailsTabularData new ArrayType(SimpleType.BYTE, true), // dict bytes SimpleType.STRING, // kind SimpleType.INTEGER, // checksum - SimpleType.INTEGER }; // size of dict bytes + SimpleType.INTEGER, // size of dict bytes + SimpleType.STRING }; // created_at COMPOSITE_TYPE = new CompositeType(TYPE_NAME, ROW_DESC, ITEM_NAMES, ITEM_DESCS, ITEM_TYPES); TABULAR_TYPE = new TabularType(TYPE_NAME, ROW_DESC, COMPOSITE_TYPE, ITEM_NAMES); @@ -133,6 +136,7 @@ public static CompositeData fromLightweightCompressionDictionary(LightweightComp dictionary.dictId.kind.name(), dictionary.checksum, dictionary.size, + dictionary.createdAt.toString() }); } catch (OpenDataException e) @@ -166,6 +170,7 @@ public static CompositeData fromCompressionDictionary(String keyspace, String ta dictionary.kind().name(), dictionary.checksum(), dictionary.rawDictionary().length, + dictionary.createdAt().toString(), }); } catch (OpenDataException e) @@ -196,7 +201,8 @@ public static CompositeData fromCompressionDictionaryDataObject(CompressionDicti dataObject.dict, dataObject.kind, dataObject.dictChecksum, - dataObject.dictLength + dataObject.dictLength, + dataObject.createdAt.toString() }); } catch (OpenDataException e) @@ -221,7 +227,8 @@ public static CompressionDictionaryDataObject fromCompositeData(CompositeData co (byte[]) compositeData.get(CompressionDictionaryDetailsTabularData.DICT_NAME), (String) compositeData.get(CompressionDictionaryDetailsTabularData.KIND_NAME), (Integer) compositeData.get(CompressionDictionaryDetailsTabularData.CHECKSUM_NAME), - (Integer) compositeData.get(CompressionDictionaryDetailsTabularData.SIZE_NAME)); + (Integer) compositeData.get(CompressionDictionaryDetailsTabularData.SIZE_NAME), + Instant.parse((String) compositeData.get(CompressionDictionaryDetailsTabularData.CREATED_AT_NAME))); } public static class CompressionDictionaryDataObject @@ -234,6 +241,7 @@ public static class CompressionDictionaryDataObject public final String kind; public final int dictChecksum; public final int dictLength; + public final Instant createdAt; @JsonCreator public CompressionDictionaryDataObject(@JsonProperty("keyspace") String keyspace, @@ -243,7 +251,8 @@ public CompressionDictionaryDataObject(@JsonProperty("keyspace") String keyspace @JsonProperty("dict") byte[] dict, @JsonProperty("kind") String kind, @JsonProperty("dictChecksum") int dictChecksum, - @JsonProperty("dictLength") int dictLength) + @JsonProperty("dictLength") int dictLength, + @JsonProperty("createdAt") Instant createdAt) { this.keyspace = keyspace; this.table = table; @@ -253,6 +262,7 @@ public CompressionDictionaryDataObject(@JsonProperty("keyspace") String keyspace this.kind = kind; this.dictChecksum = dictChecksum; this.dictLength = dictLength; + this.createdAt = createdAt; validate(); } @@ -269,6 +279,7 @@ public CompressionDictionaryDataObject(@JsonProperty("keyspace") String keyspace *
  • dictLength is bigger than 0
  • *
  • dictLength has to be equal to dict's length
  • *
  • dictChecksum has to be equal to checksum computed as part of this method
  • + *
  • creation date is not null
  • * */ private void validate() @@ -278,15 +289,11 @@ private void validate() if (table == null) throw new IllegalArgumentException("Table not specified."); if (tableId == null) - throw new IllegalArgumentException("Table id not specified"); + throw new IllegalArgumentException("Table id not specified."); if (dictId <= 0) throw new IllegalArgumentException("Provided dictionary id must be positive but it is '" + dictId + "'."); if (dict == null || dict.length == 0) throw new IllegalArgumentException("Provided dictionary byte array is null or empty."); - if (dict.length > FileUtils.ONE_MIB) - throw new IllegalArgumentException("Imported dictionary can not be larger than " + - FileUtils.ONE_MIB + " bytes, but it is " + - dict.length + " bytes."); if (kind == null) throw new IllegalArgumentException("Provided kind is null."); @@ -305,6 +312,8 @@ private void validate() throw new IllegalArgumentException("Size has to be strictly positive number, it is '" + dictLength + "'."); if (dict.length != dictLength) throw new IllegalArgumentException("The length of the provided dictionary array (" + dict.length + ") is not equal to provided length value (" + dictLength + ")."); + if (createdAt == null) + throw new IllegalArgumentException("The creation date not specified."); int checksumOfDictionaryToImport = CompressionDictionary.calculateChecksum((byte) dictionaryKind.ordinal(), dictId, dict); if (checksumOfDictionaryToImport != dictChecksum) diff --git a/src/java/org/apache/cassandra/db/compression/ZstdCompressionDictionary.java b/src/java/org/apache/cassandra/db/compression/ZstdCompressionDictionary.java index b39583a328a8..a83d1f49106e 100644 --- a/src/java/org/apache/cassandra/db/compression/ZstdCompressionDictionary.java +++ b/src/java/org/apache/cassandra/db/compression/ZstdCompressionDictionary.java @@ -18,6 +18,7 @@ package org.apache.cassandra.db.compression; +import java.time.Instant; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicReference; @@ -30,6 +31,7 @@ import org.slf4j.LoggerFactory; import org.apache.cassandra.io.compress.ZstdCompressorBase; +import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.concurrent.Ref; import org.apache.cassandra.utils.concurrent.RefCounted; import org.apache.cassandra.utils.concurrent.SelfRefCounted; @@ -45,21 +47,24 @@ public class ZstdCompressionDictionary implements CompressionDictionary, SelfRef private final ConcurrentHashMap zstdDictCompressPerLevel = new ConcurrentHashMap<>(); private final AtomicReference dictDecompress = new AtomicReference<>(); private volatile Ref selfRef; + private final Instant createdAt; @VisibleForTesting public ZstdCompressionDictionary(DictId dictId, byte[] rawDictionary) { this(dictId, rawDictionary, - CompressionDictionary.calculateChecksum((byte) dictId.kind.ordinal(), dictId.id, rawDictionary)); + CompressionDictionary.calculateChecksum((byte) dictId.kind.ordinal(), dictId.id, rawDictionary), + FBUtilities.now()); } - public ZstdCompressionDictionary(DictId dictId, byte[] rawDictionary, int checksum) + public ZstdCompressionDictionary(DictId dictId, byte[] rawDictionary, int checksum, Instant createdAt) { this.dictId = dictId; this.rawDictionary = rawDictionary; this.checksum = checksum; this.selfRef = null; + this.createdAt = createdAt; } @Override @@ -86,6 +91,12 @@ public int checksum() return checksum; } + @Override + public Instant createdAt() + { + return createdAt; + } + @Override public int estimatedOccupiedMemoryBytes() { diff --git a/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java b/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java index 31141b6852f9..15f8ca6fbee5 100644 --- a/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java +++ b/src/java/org/apache/cassandra/schema/SystemDistributedKeyspace.java @@ -59,6 +59,7 @@ import org.apache.cassandra.repair.CommonRange; import org.apache.cassandra.repair.messages.RepairOption; import org.apache.cassandra.tcm.ClusterMetadata; +import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.utils.TimeUUID; @@ -206,6 +207,7 @@ private SystemDistributedKeyspace() "dict blob," + "dict_length int," + "dict_checksum int," + + "created_at timestamp," + "PRIMARY KEY ((keyspace_name, table_name), table_id, dict_id)) " + "WITH CLUSTERING ORDER BY (table_id DESC, dict_id DESC)"; // in order to retrieve the latest dictionary; the contract is the newer the dictionary the larger the dict_id @@ -426,7 +428,7 @@ public static void storeCompressionDictionary(String keyspaceName, CompressionDictionary dictionary) { byte[] dict = dictionary.rawDictionary(); - String query = "INSERT INTO %s.%s (keyspace_name, table_name, table_id, kind, dict_id, dict, dict_length, dict_checksum) VALUES ('%s', '%s', '%s', '%s', %s, ?, %s, %s)"; + String query = "INSERT INTO %s.%s (keyspace_name, table_name, table_id, kind, dict_id, dict, dict_length, dict_checksum, created_at) VALUES ('%s', '%s', '%s', '%s', %s, ?, %s, %s, ?)"; String fmtQuery = format(query, SchemaConstants.DISTRIBUTED_KEYSPACE_NAME, COMPRESSION_DICTIONARIES, @@ -439,7 +441,7 @@ public static void storeCompressionDictionary(String keyspaceName, dictionary.checksum()); noThrow(fmtQuery, () -> QueryProcessor.process(fmtQuery, ConsistencyLevel.ONE, - Collections.singletonList(ByteBuffer.wrap(dict)))); + List.of(ByteBuffer.wrap(dict), ByteBufferUtil.bytes(dictionary.createdAt().toEpochMilli())))); } /** @@ -454,7 +456,7 @@ public static void storeCompressionDictionary(String keyspaceName, @Nullable public static CompressionDictionary retrieveLatestCompressionDictionary(String keyspaceName, String tableName, String tableId) { - String query = "SELECT kind, dict_id, dict, dict_length, dict_checksum FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s' LIMIT 1"; + String query = "SELECT kind, dict_id, dict, dict_length, dict_checksum, created_at FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s' LIMIT 1"; String fmtQuery = format(query, SchemaConstants.DISTRIBUTED_KEYSPACE_NAME, COMPRESSION_DICTIONARIES, keyspaceName, tableName, tableId); try { @@ -479,7 +481,7 @@ public static CompressionDictionary retrieveLatestCompressionDictionary(String k @Nullable public static LightweightCompressionDictionary retrieveLightweightLatestCompressionDictionary(String keyspaceName, String tableName, String tableId) { - String query = "SELECT keyspace_name, table_name, table_id, kind, dict_id, dict_checksum, dict_length FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s' LIMIT 1"; + String query = "SELECT keyspace_name, table_name, table_id, kind, dict_id, dict_checksum, dict_length, created_at FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s' LIMIT 1"; String fmtQuery = format(query, SchemaConstants.DISTRIBUTED_KEYSPACE_NAME, COMPRESSION_DICTIONARIES, keyspaceName, tableName, tableId); try { @@ -505,7 +507,7 @@ public static LightweightCompressionDictionary retrieveLightweightLatestCompress @Nullable public static CompressionDictionary retrieveCompressionDictionary(String keyspaceName, String tableName, String tableId, long dictionaryId) { - String query = "SELECT kind, dict_id, dict, dict_length, dict_checksum FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s' AND dict_id=%s"; + String query = "SELECT kind, dict_id, dict, dict_length, dict_checksum, created_at FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s' AND dict_id=%s"; String fmtQuery = format(query, SchemaConstants.DISTRIBUTED_KEYSPACE_NAME, COMPRESSION_DICTIONARIES, keyspaceName, tableName, tableId, dictionaryId); try { @@ -529,7 +531,7 @@ public static CompressionDictionary retrieveCompressionDictionary(String keyspac @Nullable public static List retrieveLightweightCompressionDictionaries(String keyspaceName, String tableName, String tableId) { - String query = "SELECT keyspace_name, table_name, table_id, kind, dict_id, dict_length, dict_checksum FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s'"; + String query = "SELECT keyspace_name, table_name, table_id, kind, dict_id, dict_length, dict_checksum, created_at FROM %s.%s WHERE keyspace_name='%s' AND table_name='%s' AND table_id='%s'"; String fmtQuery = format(query, SchemaConstants.DISTRIBUTED_KEYSPACE_NAME, COMPRESSION_DICTIONARIES, keyspaceName, tableName, tableId); return retrieveLightweightCompressionDictionariesInternal(fmtQuery); } @@ -543,7 +545,7 @@ public static List retrieveLightweightCompress @Nullable public static List retrieveLightweightCompressionDictionaries() { - String query = "SELECT keyspace_name, table_name, table_id, kind, dict_id, dict_length, dict_checksum FROM %s.%s"; + String query = "SELECT keyspace_name, table_name, table_id, kind, dict_id, dict_length, dict_checksum, created_at FROM %s.%s"; String fmtQuery = format(query, SchemaConstants.DISTRIBUTED_KEYSPACE_NAME, COMPRESSION_DICTIONARIES); return retrieveLightweightCompressionDictionariesInternal(fmtQuery); } diff --git a/test/unit/org/apache/cassandra/db/compression/CompressionDictionaryDataObjectTest.java b/test/unit/org/apache/cassandra/db/compression/CompressionDictionaryDataObjectTest.java index 268c8222bd81..ecb3e35853c6 100644 --- a/test/unit/org/apache/cassandra/db/compression/CompressionDictionaryDataObjectTest.java +++ b/test/unit/org/apache/cassandra/db/compression/CompressionDictionaryDataObjectTest.java @@ -18,6 +18,7 @@ package org.apache.cassandra.db.compression; +import java.time.Instant; import java.util.UUID; import java.util.function.Consumer; @@ -27,7 +28,6 @@ import org.apache.cassandra.db.compression.CompressionDictionary.LightweightCompressionDictionary; import org.apache.cassandra.db.compression.CompressionDictionaryDetailsTabularData.CompressionDictionaryDataObject; -import org.apache.cassandra.io.util.FileUtils; import org.apache.cassandra.utils.CompressionDictionaryHelper; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -40,6 +40,7 @@ public class CompressionDictionaryDataObjectTest private static final String KEYSPACE = "ks"; private static final String TABLE = "tb"; private static final String TABLE_ID = UUID.randomUUID().toString(); + private static final Instant now = Instant.now(); private static final CompressionDictionary COMPRESSION_DICTIONARY = CompressionDictionaryHelper.INSTANCE.trainDictionary(KEYSPACE, TABLE); private static final CompressionDictionaryDataObject VALID_OBJECT = createValidObject(); @@ -56,6 +57,7 @@ public void testConversionOfCompressionDictionaryDataObjectToCompositeDataAndBac assertEquals(VALID_OBJECT.kind, dataObject.kind); assertEquals(VALID_OBJECT.dictChecksum, dataObject.dictChecksum); assertEquals(VALID_OBJECT.dictLength, dataObject.dictLength); + assertEquals(VALID_OBJECT.createdAt, dataObject.createdAt); } @Test @@ -71,6 +73,7 @@ public void testConversionOfCompressionDictionaryToDataObject() assertEquals(COMPRESSION_DICTIONARY.kind().name(), dataObject.kind); assertEquals(COMPRESSION_DICTIONARY.checksum(), dataObject.dictChecksum); assertEquals(COMPRESSION_DICTIONARY.rawDictionary().length, dataObject.dictLength); + assertEquals(COMPRESSION_DICTIONARY.createdAt(), dataObject.createdAt); } @Test @@ -81,7 +84,8 @@ public void testConversionOfLightweightDictionaryToCompositeData() TABLE_ID, COMPRESSION_DICTIONARY.dictId(), COMPRESSION_DICTIONARY.checksum(), - COMPRESSION_DICTIONARY.rawDictionary().length); + COMPRESSION_DICTIONARY.rawDictionary().length, + now); CompositeData compositeData = CompressionDictionaryDetailsTabularData.fromLightweightCompressionDictionary(lightweight); @@ -93,6 +97,7 @@ public void testConversionOfLightweightDictionaryToCompositeData() assertEquals(COMPRESSION_DICTIONARY.dictId().kind.name(), compositeData.get(CompressionDictionaryDetailsTabularData.KIND_NAME)); assertEquals(COMPRESSION_DICTIONARY.checksum(), compositeData.get(CompressionDictionaryDetailsTabularData.CHECKSUM_NAME)); assertEquals(COMPRESSION_DICTIONARY.rawDictionary().length, compositeData.get(CompressionDictionaryDetailsTabularData.SIZE_NAME)); + assertEquals(now.toString(), compositeData.get(CompressionDictionaryDetailsTabularData.CREATED_AT_NAME)); } @Test @@ -100,13 +105,10 @@ public void testValidation() { assertInvalid(modifier -> modifier.withKeyspace(null), "Keyspace not specified."); assertInvalid(modifier -> modifier.withTable(null), "Table not specified."); - assertInvalid(modifier -> modifier.withTableId(null), "Table id not specified"); + assertInvalid(modifier -> modifier.withTableId(null), "Table id not specified."); assertInvalid(modifier -> modifier.withDictId(-1), "Provided dictionary id must be positive but it is '-1'."); assertInvalid(modifier -> modifier.withDict(null), "Provided dictionary byte array is null or empty."); - assertInvalid(modifier -> modifier.withDict(new byte[0]), "Provided dictionary byte array is null or empty."); - assertInvalid(modifier -> modifier.withDict(new byte[((int) FileUtils.ONE_MIB) + 1]), - "Imported dictionary can not be larger than 1048576 bytes, but it is 1048577 bytes."); - assertInvalid(modifier -> modifier.withKind(null), "Provided kind is null."); + assertInvalid(modifier -> modifier.withDict(new byte[0]), "Provided dictionary byte array is null or empty.");assertInvalid(modifier -> modifier.withKind(null), "Provided kind is null."); assertInvalid(modifier -> modifier.withKind("NONSENSE"), "There is no such dictionary kind like 'NONSENSE'. Available kinds: [ZSTD]"); assertInvalid(modifier -> modifier.withDictLength(0), "Size has to be strictly positive number, it is '0'."); assertInvalid(modifier -> modifier.withDictLength(-10), "Size has to be strictly positive number, it is '-10'."); @@ -116,6 +118,7 @@ public void testValidation() assertInvalid(builder -> builder.withDictChecksum(VALID_OBJECT.dictChecksum + 1), "Computed checksum of dictionary to import (" + VALID_OBJECT.dictChecksum + ") is different from checksum specified on input (" + (VALID_OBJECT.dictChecksum + 1) + ")."); + assertInvalid(modifier -> modifier.withCreatedAt(null), "The creation date not specified."); } private void assertInvalid(Consumer action, String expectedExceptionMessage) @@ -130,6 +133,7 @@ private void assertInvalid(Consumer action, String expectedE private static CompressionDictionaryDataObject createValidObject() { + return new CompressionDictionaryDataObject("ks", "tb", TABLE_ID, @@ -139,7 +143,8 @@ private static CompressionDictionaryDataObject createValidObject() CompressionDictionary.calculateChecksum((byte) CompressionDictionary.Kind.ZSTD.ordinal(), 123, COMPRESSION_DICTIONARY.rawDictionary()), - COMPRESSION_DICTIONARY.rawDictionary().length); + COMPRESSION_DICTIONARY.rawDictionary().length, + now); } private static class DataObjectModifier @@ -152,6 +157,7 @@ private static class DataObjectModifier private String kind; private int dictChecksum; private int dictLength; + private Instant createdAt; public DataObjectModifier(CompressionDictionaryDataObject from) { @@ -163,11 +169,12 @@ public DataObjectModifier(CompressionDictionaryDataObject from) withKind(from.kind); withDictChecksum(from.dictChecksum); withDictLength(from.dictLength); + withCreatedAt(from.createdAt); } public CompressionDictionaryDataObject build() { - return new CompressionDictionaryDataObject(keyspace, table, tableId, dictId, dict, kind, dictChecksum, dictLength); + return new CompressionDictionaryDataObject(keyspace, table, tableId, dictId, dict, kind, dictChecksum, dictLength, createdAt); } public DataObjectModifier withKeyspace(String keyspace) @@ -217,5 +224,11 @@ public DataObjectModifier withDictLength(int dictLength) this.dictLength = dictLength; return this; } + + private DataObjectModifier withCreatedAt(Instant createdAt) + { + this.createdAt = createdAt; + return this; + } } } diff --git a/test/unit/org/apache/cassandra/tools/nodetool/ExportImportListCompressionDictionaryTest.java b/test/unit/org/apache/cassandra/tools/nodetool/ExportImportListCompressionDictionaryTest.java index f831b3470357..204cf115e762 100644 --- a/test/unit/org/apache/cassandra/tools/nodetool/ExportImportListCompressionDictionaryTest.java +++ b/test/unit/org/apache/cassandra/tools/nodetool/ExportImportListCompressionDictionaryTest.java @@ -19,6 +19,7 @@ package org.apache.cassandra.tools.nodetool; import java.nio.file.Files; +import java.time.Instant; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -92,7 +93,8 @@ public void testInvalidKeyspaceTable() throws Throwable pair.left.dict, pair.left.kind, pair.left.dictChecksum, - pair.left.dictLength), pair.right); + pair.left.dictLength, + Instant.now()), pair.right); ToolResult result = invokeNodetool("compressiondictionary", "import", pair.right.absolutePath()); assertTrue(result.getStderr().contains("Unable to import dictionary JSON: Table abc.def does not exist or does not support dictionary compression")); @@ -168,10 +170,11 @@ private void list(CompressionDictionaryDataObject dataObject, String table) ToolResult result = invokeNodetool("compressiondictionary", "list", keyspace(), table); result.assertOnExitCode(); assertTrue(result.getStdout() - .contains(format("%s %s %s %s %s %s", + .contains(format("%s %s %s %s %s %s %s", keyspace(), table, dataObject.dictId, dataObject.kind, dataObject.dictChecksum, - dataObject.dictLength))); + dataObject.dictLength, + dataObject.createdAt.toString()))); } private void rewriteTable(String table, Pair pair) throws Throwable @@ -183,7 +186,8 @@ private void rewriteTable(String table, Pair