Introduce created_at column to system_distributed.compression_dictionaries

patch by Stefan Miklosovic; reviewed by Yifan Cai for CASSANDRA-21178
This commit is contained in:
Stefan Miklosovic 2026-02-17 18:56:14 +11:00
parent eee06be567
commit 0be5ffa8c8
No known key found for this signature in database
GPG Key ID: 32F35CB2F546D93E
7 changed files with 112 additions and 41 deletions

View File

@ -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)

View File

@ -22,6 +22,7 @@ import java.io.DataInput;
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 com.google.common.hash.Hashing;
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 @@ public interface CompressionDictionary
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 @@ public interface CompressionDictionary
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 @@ public interface CompressionDictionary
tableId,
new DictId(CompressionDictionary.Kind.valueOf(kindStr), dictId),
checksum,
size);
size,
createdAt);
}
catch (IllegalArgumentException ex)
{
@ -285,6 +297,7 @@ public interface CompressionDictionary
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 @@ public interface CompressionDictionary
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 @@ public interface CompressionDictionary
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 interface CompressionDictionary
* @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 @@ public interface CompressionDictionary
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 interface CompressionDictionary
this.dictId = dictId;
this.checksum = checksum;
this.size = size;
this.createdAt = createdAt;
}
@Override
@ -462,6 +492,7 @@ public interface CompressionDictionary
", dictId=" + dictId +
", checksum=" + checksum +
", size=" + size +
", createdAt=" + createdAt +
'}';
}
}

View File

@ -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.JsonCreator;
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<String[]>(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 class CompressionDictionaryDetailsTabularData
dictionary.dictId.kind.name(),
dictionary.checksum,
dictionary.size,
dictionary.createdAt.toString()
});
}
catch (OpenDataException e)
@ -166,6 +170,7 @@ public class CompressionDictionaryDetailsTabularData
dictionary.kind().name(),
dictionary.checksum(),
dictionary.rawDictionary().length,
dictionary.createdAt().toString(),
});
}
catch (OpenDataException e)
@ -196,7 +201,8 @@ public class CompressionDictionaryDetailsTabularData
dataObject.dict,
dataObject.kind,
dataObject.dictChecksum,
dataObject.dictLength
dataObject.dictLength,
dataObject.createdAt.toString()
});
}
catch (OpenDataException e)
@ -221,7 +227,8 @@ public class CompressionDictionaryDetailsTabularData
(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 class CompressionDictionaryDetailsTabularData
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 class CompressionDictionaryDetailsTabularData
@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 class CompressionDictionaryDetailsTabularData
this.kind = kind;
this.dictChecksum = dictChecksum;
this.dictLength = dictLength;
this.createdAt = createdAt;
validate();
}
@ -269,6 +279,7 @@ public class CompressionDictionaryDetailsTabularData
* <li>dictLength is bigger than 0</li>
* <li>dictLength has to be equal to dict's length</li>
* <li>dictChecksum has to be equal to checksum computed as part of this method</li>
* <li>creation date is not null</li>
* </ul>
*/
private void validate()
@ -278,15 +289,11 @@ public class CompressionDictionaryDetailsTabularData
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 @@ public class CompressionDictionaryDetailsTabularData
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)

View File

@ -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.Logger;
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<Integer, ZstdDictCompress> zstdDictCompressPerLevel = new ConcurrentHashMap<>();
private final AtomicReference<ZstdDictDecompress> dictDecompress = new AtomicReference<>();
private volatile Ref<ZstdCompressionDictionary> 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 class ZstdCompressionDictionary implements CompressionDictionary, SelfRef
return checksum;
}
@Override
public Instant createdAt()
{
return createdAt;
}
@Override
public int estimatedOccupiedMemoryBytes()
{

View File

@ -59,6 +59,7 @@ import org.apache.cassandra.locator.InetAddressAndPort;
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 @@ public final class 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 final class SystemDistributedKeyspace
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 final class SystemDistributedKeyspace
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 final class SystemDistributedKeyspace
@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 final class SystemDistributedKeyspace
@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 final class SystemDistributedKeyspace
@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 final class SystemDistributedKeyspace
@Nullable
public static List<LightweightCompressionDictionary> 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 final class SystemDistributedKeyspace
@Nullable
public static List<LightweightCompressionDictionary> 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);
}

View File

@ -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.junit.Test;
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 class CompressionDictionaryDataObjectTest
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 class CompressionDictionaryDataObjectTest
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 class CompressionDictionaryDataObjectTest
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 class CompressionDictionaryDataObjectTest
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 class CompressionDictionaryDataObjectTest
{
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 class CompressionDictionaryDataObjectTest
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<DataObjectModifier> action, String expectedExceptionMessage)
@ -130,6 +133,7 @@ public class CompressionDictionaryDataObjectTest
private static CompressionDictionaryDataObject createValidObject()
{
return new CompressionDictionaryDataObject("ks",
"tb",
TABLE_ID,
@ -139,7 +143,8 @@ public class CompressionDictionaryDataObjectTest
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 @@ public class CompressionDictionaryDataObjectTest
private String kind;
private int dictChecksum;
private int dictLength;
private Instant createdAt;
public DataObjectModifier(CompressionDictionaryDataObject from)
{
@ -163,11 +169,12 @@ public class CompressionDictionaryDataObjectTest
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 class CompressionDictionaryDataObjectTest
this.dictLength = dictLength;
return this;
}
private DataObjectModifier withCreatedAt(Instant createdAt)
{
this.createdAt = createdAt;
return this;
}
}
}

View File

@ -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 class ExportImportListCompressionDictionaryTest extends CQLTester
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 @@ public class ExportImportListCompressionDictionaryTest extends CQLTester
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<CompressionDictionaryDataObject, File> pair) throws Throwable
@ -183,7 +186,8 @@ public class ExportImportListCompressionDictionaryTest extends CQLTester
pair.left.dict,
pair.left.kind,
pair.left.dictChecksum,
pair.left.dictLength),
pair.left.dictLength,
pair.left.createdAt),
pair.right);
}