This is an automated email from the ASF dual-hosted git repository.
benedict pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git
The following commit(s) were added to refs/heads/trunk by this push:
new 92cbde1fde Introduce PseudoUtf8Type for virtual tables to search on
types that are otherwise treated as strings for pretty printing; Use this
facility to make Accord debug virtual tables more useful
92cbde1fde is described below
commit 92cbde1fdedcccf321a0e35c09fdca8d7435d9fb
Author: Benedict Elliott Smith <[email protected]>
AuthorDate: Thu Jul 10 13:33:11 2025 +0100
Introduce PseudoUtf8Type for virtual tables to search on types that are
otherwise treated as strings for pretty printing;
Use this facility to make Accord debug virtual tables more useful
patch by Benedict; reviewed by Alex Petrov for CASSANDRA-20755
---
src/java/org/apache/cassandra/cql3/CQL3Type.java | 9 ++-
.../marshal/{UTF8Type.java => PseudoUtf8Type.java} | 43 ++---------
.../apache/cassandra/db/marshal/TokenUtf8Type.java | 80 ++++++++++++++++++++
.../apache/cassandra/db/marshal/TxnIdUtf8Type.java | 78 +++++++++++++++++++
.../apache/cassandra/db/marshal/TypeParser.java | 6 ++
.../org/apache/cassandra/db/marshal/UTF8Type.java | 2 +-
.../cassandra/db/virtual/AccordDebugKeyspace.java | 87 ++++++++++------------
.../cassandra/serializers/UTF8Serializer.java | 2 +-
.../db/virtual/AccordDebugKeyspaceTest.java | 29 +++++++-
.../cassandra/utils/AbstractTypeGenerators.java | 5 +-
10 files changed, 252 insertions(+), 89 deletions(-)
diff --git a/src/java/org/apache/cassandra/cql3/CQL3Type.java
b/src/java/org/apache/cassandra/cql3/CQL3Type.java
index 6e2fdcc22e..aba76c0fa3 100644
--- a/src/java/org/apache/cassandra/cql3/CQL3Type.java
+++ b/src/java/org/apache/cassandra/cql3/CQL3Type.java
@@ -166,8 +166,13 @@ public interface CQL3Type
@Override
public String toCQLLiteral(ByteBuffer buffer)
{
- // *always* use the 'blob' syntax to express custom types in CQL
- return Native.BLOB.toCQLLiteral(buffer);
+ CQL3Type asCql3 = type.asCQL3Type();
+ if (asCql3 instanceof Custom)
+ {
+ // use the 'blob' syntax to express custom types in CQL if not
overridden
+ return Native.BLOB.toCQLLiteral(buffer);
+ }
+ return asCql3.toCQLLiteral(buffer);
}
@Override
diff --git a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
b/src/java/org/apache/cassandra/db/marshal/PseudoUtf8Type.java
similarity index 66%
copy from src/java/org/apache/cassandra/db/marshal/UTF8Type.java
copy to src/java/org/apache/cassandra/db/marshal/PseudoUtf8Type.java
index 5258321bee..6c3542808c 100644
--- a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
+++ b/src/java/org/apache/cassandra/db/marshal/PseudoUtf8Type.java
@@ -21,27 +21,19 @@ import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.StandardCharsets;
-import org.apache.cassandra.cql3.terms.Constants;
-
import org.apache.cassandra.cql3.CQL3Type;
+import org.apache.cassandra.cql3.terms.Constants;
import org.apache.cassandra.cql3.terms.Term;
-import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
import org.apache.cassandra.serializers.MarshalException;
-import org.apache.cassandra.serializers.TypeSerializer;
-import org.apache.cassandra.serializers.UTF8Serializer;
import org.apache.cassandra.transport.ProtocolVersion;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.JsonUtils;
-public class UTF8Type extends StringType
+public abstract class PseudoUtf8Type extends StringType
{
- public static final UTF8Type instance = new UTF8Type();
-
- private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new
DefaultArgumentDeserializer(instance);
-
- private static final ByteBuffer MASKED_VALUE = instance.decompose("****");
+ PseudoUtf8Type() {super(ComparisonType.CUSTOM);} // singleton
- UTF8Type() {super(ComparisonType.BYTE_ORDER);} // singleton
+ abstract String describe();
public ByteBuffer fromString(String source)
{
@@ -58,7 +50,7 @@ public class UTF8Type extends StringType
catch (ClassCastException exc)
{
throw new MarshalException(String.format(
- "Expected a UTF-8 string, but got a %s: %s",
parsed.getClass().getSimpleName(), parsed));
+ "Expected a %s string, but got a %s: %s", describe(),
parsed.getClass().getSimpleName(), parsed));
}
}
@@ -71,16 +63,15 @@ public class UTF8Type extends StringType
}
catch (CharacterCodingException exc)
{
- throw new AssertionError("UTF-8 value contained non-utf8
characters: ", exc);
+ throw new AssertionError(describe() + " value contained non-utf8
characters: ", exc);
}
}
@Override
public boolean isCompatibleWith(AbstractType<?> previous)
{
- // Anything that is ascii is also utf8, and they both use bytes
- // comparison
- return this == previous || previous == AsciiType.instance;
+ // Anything that is ascii is also utf8, and they both use bytes
comparison
+ return previous == AsciiType.instance || previous == UTF8Type.instance
|| previous instanceof PseudoUtf8Type;
}
@Override
@@ -88,22 +79,4 @@ public class UTF8Type extends StringType
{
return CQL3Type.Native.TEXT;
}
-
- @Override
- public TypeSerializer<String> getSerializer()
- {
- return UTF8Serializer.instance;
- }
-
- @Override
- public ArgumentDeserializer getArgumentDeserializer()
- {
- return ARGUMENT_DESERIALIZER;
- }
-
- @Override
- public ByteBuffer getMaskedValue()
- {
- return MASKED_VALUE;
- }
}
diff --git a/src/java/org/apache/cassandra/db/marshal/TokenUtf8Type.java
b/src/java/org/apache/cassandra/db/marshal/TokenUtf8Type.java
new file mode 100644
index 0000000000..caf8980910
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/marshal/TokenUtf8Type.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+package org.apache.cassandra.db.marshal;
+
+import java.nio.ByteBuffer;
+
+import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
+import org.apache.cassandra.dht.Token;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.UTF8Serializer;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+import static org.apache.cassandra.config.DatabaseDescriptor.getPartitioner;
+
+public class TokenUtf8Type extends PseudoUtf8Type
+{
+ public static final TokenUtf8Type instance = new TokenUtf8Type();
+ static final TypeSerializer<String> tokenSerializer = new UTF8Serializer()
+ {
+ @Override
+ public <V> void validate(V value, ValueAccessor<V> accessor) throws
MarshalException
+ {
+ super.validate(value, accessor);
+ String str = deserialize(value, accessor);
+ if (null == getPartitioner().getTokenFactory().fromString(str))
+ throw new MarshalException("Invalid Token: " + str);
+ }
+ };
+
+ private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new
DefaultArgumentDeserializer(instance);
+ private static final ByteBuffer MASKED_VALUE =
ByteBufferUtil.EMPTY_BYTE_BUFFER;
+
+ TokenUtf8Type() {} // singleton
+
+ String describe() { return "Token"; }
+
+ @Override
+ public TypeSerializer<String> getSerializer()
+ {
+ return tokenSerializer;
+ }
+
+ @Override
+ public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR
right, ValueAccessor<VR> accessorR)
+ {
+ String leftStr = UTF8Serializer.instance.deserialize(left, accessorL);
+ String rightStr = UTF8Serializer.instance.deserialize(right,
accessorR);
+ Token leftToken =
getPartitioner().getTokenFactory().fromString(leftStr);
+ Token rightToken =
getPartitioner().getTokenFactory().fromString(rightStr);
+ return leftToken.compareTo(rightToken);
+ }
+
+ @Override
+ public ArgumentDeserializer getArgumentDeserializer()
+ {
+ return ARGUMENT_DESERIALIZER;
+ }
+
+ @Override
+ public ByteBuffer getMaskedValue()
+ {
+ return MASKED_VALUE;
+ }
+}
diff --git a/src/java/org/apache/cassandra/db/marshal/TxnIdUtf8Type.java
b/src/java/org/apache/cassandra/db/marshal/TxnIdUtf8Type.java
new file mode 100644
index 0000000000..784969ead7
--- /dev/null
+++ b/src/java/org/apache/cassandra/db/marshal/TxnIdUtf8Type.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ */
+package org.apache.cassandra.db.marshal;
+
+import java.nio.ByteBuffer;
+
+import accord.primitives.TxnId;
+import org.apache.cassandra.cql3.functions.ArgumentDeserializer;
+import org.apache.cassandra.serializers.MarshalException;
+import org.apache.cassandra.serializers.TypeSerializer;
+import org.apache.cassandra.serializers.UTF8Serializer;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+public class TxnIdUtf8Type extends PseudoUtf8Type
+{
+ public static final TxnIdUtf8Type instance = new TxnIdUtf8Type();
+ static final TypeSerializer<String> txnIdSerializer = new UTF8Serializer()
+ {
+ @Override
+ public <V> void validate(V value, ValueAccessor<V> accessor) throws
MarshalException
+ {
+ super.validate(value, accessor);
+ String str = deserialize(value, accessor);
+ if (null == TxnId.tryParse(str))
+ throw new MarshalException("Invalid TxnId: " + str);
+ }
+ };
+
+ private static final ArgumentDeserializer ARGUMENT_DESERIALIZER = new
DefaultArgumentDeserializer(instance);
+ private static final ByteBuffer MASKED_VALUE =
ByteBufferUtil.EMPTY_BYTE_BUFFER;
+
+ TxnIdUtf8Type() {} // singleton
+
+ String describe() { return "TxnId"; }
+
+ @Override
+ public TypeSerializer<String> getSerializer()
+ {
+ return txnIdSerializer;
+ }
+
+ @Override
+ public <VL, VR> int compareCustom(VL left, ValueAccessor<VL> accessorL, VR
right, ValueAccessor<VR> accessorR)
+ {
+ String leftStr = UTF8Serializer.instance.deserialize(left, accessorL);
+ String rightStr = UTF8Serializer.instance.deserialize(right,
accessorR);
+ TxnId leftId = TxnId.parse(leftStr);
+ TxnId rightId = TxnId.parse(rightStr);
+ return leftId.compareTo(rightId);
+ }
+
+ @Override
+ public ArgumentDeserializer getArgumentDeserializer()
+ {
+ return ARGUMENT_DESERIALIZER;
+ }
+
+ @Override
+ public ByteBuffer getMaskedValue()
+ {
+ return MASKED_VALUE;
+ }
+}
diff --git a/src/java/org/apache/cassandra/db/marshal/TypeParser.java
b/src/java/org/apache/cassandra/db/marshal/TypeParser.java
index 87df2c3805..2fd1e9e053 100644
--- a/src/java/org/apache/cassandra/db/marshal/TypeParser.java
+++ b/src/java/org/apache/cassandra/db/marshal/TypeParser.java
@@ -35,6 +35,7 @@ import org.apache.cassandra.cql3.FieldIdentifier;
import org.apache.cassandra.dht.IPartitioner;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
+import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Pair;
@@ -465,6 +466,11 @@ public class TypeParser
{
String className = compareWith.contains(".") ? compareWith :
"org.apache.cassandra.db.marshal." + compareWith;
Class<? extends AbstractType<?>> typeClass =
FBUtilities.<AbstractType<?>>classForName(className, "abstract-type");
+ if (PseudoUtf8Type.class.isAssignableFrom(typeClass))
+ {
+ if (StorageService.instance.isDaemonSetupCompleted())
+ throw new ConfigurationException(typeClass.getName() + " is
reserved for internal functionality");
+ }
try
{
Method method = typeClass.getDeclaredMethod("getInstance",
TypeParser.class);
diff --git a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
index 5258321bee..babd6a52da 100644
--- a/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
+++ b/src/java/org/apache/cassandra/db/marshal/UTF8Type.java
@@ -80,7 +80,7 @@ public class UTF8Type extends StringType
{
// Anything that is ascii is also utf8, and they both use bytes
// comparison
- return this == previous || previous == AsciiType.instance;
+ return this == previous || previous == AsciiType.instance || previous
instanceof PseudoUtf8Type;
}
@Override
diff --git a/src/java/org/apache/cassandra/db/virtual/AccordDebugKeyspace.java
b/src/java/org/apache/cassandra/db/virtual/AccordDebugKeyspace.java
index 896c71c0ae..685e44a061 100644
--- a/src/java/org/apache/cassandra/db/virtual/AccordDebugKeyspace.java
+++ b/src/java/org/apache/cassandra/db/virtual/AccordDebugKeyspace.java
@@ -149,9 +149,8 @@ public class AccordDebugKeyspace extends VirtualKeyspace
"CREATE TABLE %s (\n" +
" keyspace_name text,\n" +
" table_name text,\n" +
- " token_sort blob,\n" +
- " token_start text,\n" +
- " token_end text,\n" +
+ " token_start 'TokenUtf8Type',\n" +
+ " token_end 'TokenUtf8Type',\n" +
" last_started_at bigint,\n" +
" cycle_started_at bigint,\n" +
" retries int,\n" +
@@ -161,10 +160,10 @@ public class AccordDebugKeyspace extends VirtualKeyspace
" waiting text,\n" +
" node_offset int,\n" +
" cycle_offset int,\n" +
- " activeIndex int,\n" +
- " nextIndex int,\n" +
- " nextToIndex int,\n" +
- " endIndex int,\n" +
+ " active_index int,\n" +
+ " next_index int,\n" +
+ " next_to_index int,\n" +
+ " end_index int,\n" +
" current_splits int,\n" +
" stopping boolean,\n" +
" stopped boolean,\n" +
@@ -182,8 +181,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
{
TableId tableId = (TableId)
view.shard().range.start().prefix();
TableMetadata tableMetadata = tableMetadata(tableId);
- ds.row(keyspace(tableMetadata), table(tableId, tableMetadata),
sortToken(view.shard().range.start()))
- .column("token_start",
printToken(view.shard().range.start()))
+ ds.row(keyspace(tableMetadata), table(tableId, tableMetadata),
printToken(view.shard().range.start()))
.column("token_end", printToken(view.shard().range.end()))
.column("last_started_at",
approxTime.translate().toMillisSinceEpoch(view.lastStartedAtMicros() * 1000))
.column("cycle_started_at",
approxTime.translate().toMillisSinceEpoch(view.cycleStartedAtMicros() * 1000))
@@ -194,10 +192,10 @@ public class AccordDebugKeyspace extends VirtualKeyspace
.column("waiting", Objects.toString(view.waiting()))
.column("node_offset", view.nodeOffset())
.column("cycle_offset", view.cycleOffset())
- .column("activeIndex", view.activeIndex())
- .column("nextIndex", view.nextIndex())
- .column("nextToIndex", view.toIndex())
- .column("endIndex", view.cycleLength())
+ .column("active_index", view.activeIndex())
+ .column("next_index", view.nextIndex())
+ .column("next_to_index", view.toIndex())
+ .column("end_index", view.cycleLength())
.column("current_splits", view.currentSplits())
.column("stopping", view.stopping())
.column("stopped", view.stopped())
@@ -216,12 +214,11 @@ public class AccordDebugKeyspace extends VirtualKeyspace
"CREATE TABLE %s (\n" +
" keyspace_name text,\n" +
" table_name text,\n" +
- " token_sort blob,\n" +
- " token_start text,\n" +
- " token_end text,\n" +
+ " token_start 'TokenUtf8Type',\n" +
+ " token_end 'TokenUtf8Type',\n" +
" majority_before text,\n" +
" universal_before text,\n" +
- " PRIMARY KEY (keyspace_name, table_name,
token_sort)" +
+ " PRIMARY KEY (keyspace_name, table_name,
token_start)" +
')', UTF8Type.instance));
}
@@ -233,8 +230,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
(entry, ds, start, end) -> {
TableId tableId = (TableId) start.prefix();
TableMetadata tableMetadata = tableMetadata(tableId);
- ds.row(keyspace(tableMetadata), table(tableId,
tableMetadata), sortToken(start))
- .column("token_start", printToken(start))
+ ds.row(keyspace(tableMetadata), table(tableId,
tableMetadata), printToken(start))
.column("token_end", printToken(end))
.column("majority_before",
entry.majorityBefore.toString())
.column("universal_before",
entry.universalBefore.toString());
@@ -297,12 +293,11 @@ public class AccordDebugKeyspace extends VirtualKeyspace
"CREATE TABLE %s (\n" +
" keyspace_name text,\n" +
" table_name text,\n" +
- " token_sort blob,\n" +
- " token_start text,\n" +
- " token_end text,\n" +
" command_store_id bigint,\n" +
+ " token_start 'TokenUtf8Type',\n" +
+ " token_end 'TokenUtf8Type',\n" +
" timestamp text,\n" +
- " PRIMARY KEY (keyspace_name, table_name, token_sort,
command_store_id)" +
+ " PRIMARY KEY (keyspace_name, table_name,
command_store_id, token_start)" +
')', UTF8Type.instance));
}
@@ -321,8 +316,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
maxConflicts.foldlWithBounds(
(timestamp, ds, start, end) -> {
- return ds.row(keyspace(tableMetadata), table(tableId,
tableMetadata), sortToken(start), commandStoreId)
- .column("token_start", printToken(start))
+ return ds.row(keyspace(tableMetadata), table(tableId,
tableMetadata), commandStoreId, printToken(start))
.column("token_end", printToken(end))
.column("timestamp", timestamp.toString())
;
@@ -434,7 +428,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
" keyspace_name text,\n" +
" table_name text,\n" +
" command_store_id int,\n" +
- " txn_id text,\n" +
+ " txn_id 'TxnIdUtf8Type',\n" +
// Timer + BaseTxnState
" contact_everyone boolean,\n" +
// WaitingState
@@ -504,22 +498,21 @@ public class AccordDebugKeyspace extends VirtualKeyspace
"CREATE TABLE %s (\n" +
" keyspace_name text,\n" +
" table_name text,\n" +
- " token_sort blob,\n" +
- " token_start text,\n" +
- " token_end text,\n" +
+ " token_start 'TokenUtf8Type',\n" +
+ " token_end 'TokenUtf8Type',\n" +
" command_store_id int,\n" +
" start_epoch bigint,\n" +
" end_epoch bigint,\n" +
- " gc_before text,\n" +
- " shard_applied text,\n" +
- " majority_applied text,\n" +
- " locally_applied text,\n" +
- " locally_synced text,\n" +
- " locally_redundant text,\n" +
- " locally_witnessed text,\n" +
- " pre_bootstrap text,\n" +
- " stale_until_at_least text,\n" +
- " PRIMARY KEY (keyspace_name, table_name, token_sort,
command_store_id)" +
+ " gc_before 'TxnIdUtf8Type',\n" +
+ " shard_applied 'TxnIdUtf8Type',\n" +
+ " majority_applied 'TxnIdUtf8Type',\n" +
+ " locally_applied 'TxnIdUtf8Type',\n" +
+ " locally_synced 'TxnIdUtf8Type',\n" +
+ " locally_redundant 'TxnIdUtf8Type',\n" +
+ " locally_witnessed 'TxnIdUtf8Type',\n" +
+ " pre_bootstrap 'TxnIdUtf8Type',\n" +
+ " stale_until_at_least 'TxnIdUtf8Type',\n" +
+ " PRIMARY KEY (keyspace_name, table_name,
command_store_id, token_start)" +
')', UTF8Type.instance));
}
@@ -538,8 +531,7 @@ public class AccordDebugKeyspace extends VirtualKeyspace
String table = table(tableId, tableMetadata);
commandStore.unsafeGetRedundantBefore().foldl(
(entry, ds) -> {
- ds.row(keyspace, table,
sortToken(entry.range.start()), commandStoreId)
- .column("token_start",
printToken(entry.range.start()))
+ ds.row(keyspace, table, commandStoreId,
printToken(entry.range.start()))
.column("token_end", printToken(entry.range.end()))
.column("start_epoch", entry.startEpoch)
.column("end_epoch", entry.endEpoch)
@@ -571,12 +563,11 @@ public class AccordDebugKeyspace extends VirtualKeyspace
"CREATE TABLE %s (\n" +
" keyspace_name text,\n" +
" table_name text,\n" +
- " token_sort blob,\n" +
- " token_start text,\n" +
- " token_end text,\n" +
" command_store_id int,\n" +
- " txn_id text,\n" +
- " PRIMARY KEY (keyspace_name, table_name, token_sort,
command_store_id)" +
+ " token_start 'TokenUtf8Type',\n" +
+ " token_end 'TokenUtf8Type',\n" +
+ " timestamp text,\n" +
+ " PRIMARY KEY (keyspace_name, table_name,
command_store_id, token_start)" +
')', UTF8Type.instance));
}
@@ -596,10 +587,10 @@ public class AccordDebugKeyspace extends VirtualKeyspace
String keyspace = keyspace(tableMetadata);
String table = table(tableId, tableMetadata);
rejectBefore.foldlWithBounds(
- (txnId, ds, start, end) -> ds.row(keyspace, table,
sortToken(start), commandStore.id())
+ (timestamp, ds, start, end) -> ds.row(keyspace, table,
sortToken(start), commandStore.id())
.column("token_start",
printToken(start))
.column("token_end",
printToken(end))
- .column("txn_id",
txnId.toString())
+ .column("timestamp",
timestamp.toString())
,
dataSet,
ignore -> false
diff --git a/src/java/org/apache/cassandra/serializers/UTF8Serializer.java
b/src/java/org/apache/cassandra/serializers/UTF8Serializer.java
index 38faf430b7..7b071e7131 100644
--- a/src/java/org/apache/cassandra/serializers/UTF8Serializer.java
+++ b/src/java/org/apache/cassandra/serializers/UTF8Serializer.java
@@ -25,7 +25,7 @@ public class UTF8Serializer extends AbstractTextSerializer
{
public static final UTF8Serializer instance = new UTF8Serializer();
- private UTF8Serializer()
+ protected UTF8Serializer()
{
super(StandardCharsets.UTF_8);
}
diff --git
a/test/unit/org/apache/cassandra/db/virtual/AccordDebugKeyspaceTest.java
b/test/unit/org/apache/cassandra/db/virtual/AccordDebugKeyspaceTest.java
index bb30e914b4..4d4067786a 100644
--- a/test/unit/org/apache/cassandra/db/virtual/AccordDebugKeyspaceTest.java
+++ b/test/unit/org/apache/cassandra/db/virtual/AccordDebugKeyspaceTest.java
@@ -33,8 +33,10 @@ import org.slf4j.LoggerFactory;
import accord.api.ProtocolModifiers;
import accord.messages.TxnRequest;
+import accord.primitives.Ranges;
import accord.primitives.Routable;
import accord.primitives.SaveStatus;
+import accord.primitives.Status;
import accord.primitives.Txn;
import accord.primitives.TxnId;
import accord.utils.async.AsyncChains;
@@ -42,12 +44,17 @@ import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.OptionaldPositiveInt;
import org.apache.cassandra.cql3.CQLTester;
import org.apache.cassandra.cql3.UntypedResultSet;
+import org.apache.cassandra.dht.Murmur3Partitioner.LongToken;
import org.apache.cassandra.locator.InetAddressAndPort;
import org.apache.cassandra.net.Message;
import org.apache.cassandra.net.MessagingService;
import org.apache.cassandra.net.Verb;
+import org.apache.cassandra.schema.Schema;
import org.apache.cassandra.schema.SchemaConstants;
+import org.apache.cassandra.schema.TableId;
import org.apache.cassandra.service.accord.AccordService;
+import org.apache.cassandra.service.accord.TokenRange;
+import org.apache.cassandra.service.accord.api.TokenKey;
import org.apache.cassandra.tcm.ClusterMetadata;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.concurrent.Condition;
@@ -99,6 +106,12 @@ public class AccordDebugKeyspaceTest extends CQLTester
private static final String QUERY_REDUNDANT_BEFORE =
String.format("SELECT * FROM %s.%s",
SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.REDUNDANT_BEFORE);
+ private static final String
QUERY_REDUNDANT_BEFORE_FILTER_MAJORITY_APPLIED_GEQ =
+ String.format("SELECT * FROM %s.%s WHERE majority_applied >= ?",
SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.REDUNDANT_BEFORE);
+
+ private static final String
QUERY_REDUNDANT_BEFORE_FILTER_SHARD_APPLIED_GEQ =
+ String.format("SELECT * FROM %s.%s WHERE shard_applied >= ?",
SchemaConstants.VIRTUAL_ACCORD_DEBUG, AccordDebugKeyspace.REDUNDANT_BEFORE);
+
@BeforeClass
public static void setUpClass()
{
@@ -171,11 +184,25 @@ public class AccordDebugKeyspaceTest extends CQLTester
}
@Test
- public void redundantBefore()
+ public void redundantBefore() throws ExecutionException,
InterruptedException
{
String tableName = createTable("CREATE TABLE %s (k int, c int, v int,
PRIMARY KEY (k, c)) WITH transactional_mode = 'full'");
var accord = accord();
+ TableId tableId = Schema.instance.getTableMetadata(KEYSPACE,
tableName).id;
+ TxnId syncId1 = new TxnId(100, 200, Txn.Kind.ExclusiveSyncPoint,
Routable.Domain.Range, accord.nodeId());
+ TxnId syncId2 = new TxnId(101, 300, Txn.Kind.ExclusiveSyncPoint,
Routable.Domain.Range, accord.nodeId());
+ Ranges ranges1 = Ranges.of(TokenRange.create(new TokenKey(tableId, new
LongToken(1)), new TokenKey(tableId, new LongToken(100))));
+ Ranges ranges2 = Ranges.of(TokenRange.create(new TokenKey(tableId, new
LongToken(100)), new TokenKey(tableId, new LongToken(200))));
+
AsyncChains.getBlocking(accord.node().commandStores().forEach(safeStore -> {
+ safeStore.commandStore().markShardDurable(safeStore, syncId1,
ranges1, Status.Durability.Universal);
+ safeStore.commandStore().markShardDurable(safeStore, syncId2,
ranges2, Status.Durability.Majority);
+ }));
+
Assertions.assertThat(execute(QUERY_REDUNDANT_BEFORE).size()).isGreaterThan(0);
+
Assertions.assertThat(execute(QUERY_REDUNDANT_BEFORE_FILTER_MAJORITY_APPLIED_GEQ,
syncId1.toString()).size()).isEqualTo(2);
+
Assertions.assertThat(execute(QUERY_REDUNDANT_BEFORE_FILTER_MAJORITY_APPLIED_GEQ,
syncId2.toString()).size()).isEqualTo(1);
+
Assertions.assertThat(execute(QUERY_REDUNDANT_BEFORE_FILTER_SHARD_APPLIED_GEQ,
syncId1.toString()).size()).isEqualTo(1);
+
Assertions.assertThat(execute(QUERY_REDUNDANT_BEFORE_FILTER_SHARD_APPLIED_GEQ,
syncId2.toString()).size()).isEqualTo(0);
}
@Test
diff --git a/test/unit/org/apache/cassandra/utils/AbstractTypeGenerators.java
b/test/unit/org/apache/cassandra/utils/AbstractTypeGenerators.java
index 6c85ad98fd..e6c904dd88 100644
--- a/test/unit/org/apache/cassandra/utils/AbstractTypeGenerators.java
+++ b/test/unit/org/apache/cassandra/utils/AbstractTypeGenerators.java
@@ -88,7 +88,9 @@ import org.apache.cassandra.db.marshal.StringType;
import org.apache.cassandra.db.marshal.TimeType;
import org.apache.cassandra.db.marshal.TimeUUIDType;
import org.apache.cassandra.db.marshal.TimestampType;
+import org.apache.cassandra.db.marshal.TokenUtf8Type;
import org.apache.cassandra.db.marshal.TupleType;
+import org.apache.cassandra.db.marshal.TxnIdUtf8Type;
import org.apache.cassandra.db.marshal.TypeParser;
import org.apache.cassandra.db.marshal.UTF8Type;
import org.apache.cassandra.db.marshal.UUIDType;
@@ -120,6 +122,8 @@ public final class AbstractTypeGenerators
.put((Class<? extends AbstractType<?>>) (Class<? extends
AbstractType>) ReversedType.class, "Implementation detail for cluster
ordering... its expected the caller will unwrap the clustering type to always
get access to the real type")
.put(DynamicCompositeType.FixedValueComparator.class, "Hack
type used for special ordering case, not a real/valid type")
.put(FrozenType.class, "Fake class only used during parsing...
the parsing creates this and the real type under it, then this gets swapped for
the real type")
+
.put(TxnIdUtf8Type.class, "Used only internally by accord
debug virtual tables - could be tested, but class initialisation order prevents
easy reuse of the relevant type generators")
+
.put(TokenUtf8Type.class, "Used only internally by accord
debug virtual tables - could be tested, but class initialisation order prevents
easy reuse of the relevant type generators")
.build();
/**
@@ -136,7 +140,6 @@ public final class AbstractTypeGenerators
return (String a, String b) ->
FastByteOperations.compareUnsigned(st.decompose(a), st.decompose(b));
}
-
private static final Map<AbstractType<?>, TypeSupport<?>>
PRIMITIVE_TYPE_DATA_GENS =
Stream.of(TypeSupport.of(BooleanType.instance, BOOLEAN_GEN),
TypeSupport.of(ByteType.instance,
SourceDSL.integers().between(0, Byte.MAX_VALUE * 2 +
1).map(Integer::byteValue)),
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]