dcapwell commented on code in PR #3689:
URL: https://github.com/apache/cassandra/pull/3689#discussion_r1847467296


##########
test/distributed/org/apache/cassandra/distributed/test/accord/AccordHostReplacementTest.java:
##########
@@ -68,25 +65,39 @@ public void hostReplace() throws IOException
             fixDistributedSchemas(cluster);
             init(cluster);
 
-            qt().withExamples(1).check(rs -> {
-                SchemaSpec schema = 
HarryHelper.schemaSpecBuilder(HarryHelper.KEYSPACE, "tbl")
-                                               
.transactionalMode(Generators.toHarry(Gens.pick(TRANSACTIONAL_MODES).map(Optional::of)))
-                                               .surjection()
-                                               .inflate(rs.nextLong());
-                ReplayingHistoryBuilder harry = new 
ReplayingHistoryBuilder(rs.nextLong(), 100, 1, new DefaultDataTracker(), new 
InJvmSut(cluster), schema, new TokenPlacementModel.SimpleReplicationFactor(3), 
SystemUnderTest.ConsistencyLevel.ALL);
-                cluster.schemaChange(format("CREATE KEYSPACE %s WITH 
replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};", 
HarryHelper.KEYSPACE));
-                cluster.schemaChange(schema.compile().cql());
+            withRandom(rng -> {
+                Generator<SchemaSpec> schemaGen = 
SchemaGenerators.schemaSpecGen(KEYSPACE, "host_replace", 1000,
+                                                                               
  SchemaSpec.Options.TRANSACTIONAL_MODE,
+                                                                               
  transactionalModeGen.generate(rng).toString());
+                SchemaSpec schema = schemaGen.generate(rng);
+                Generators.TrackingGenerator<Integer> pkGen = 
Generators.tracking(Generators.int32(0, 
Math.min(schema.valueGenerators.pkPopulation(), 1000)));
+
+                HistoryBuilder history = new 
HistoryBuilder(schema.valueGenerators);

Review Comment:
   can you switch this to the eager executor and not the lazy executor?



##########
test/distributed/org/apache/cassandra/distributed/test/log/MetadataChangeSimulationTest.java:
##########
@@ -73,12 +72,7 @@
 import org.apache.cassandra.tcm.transformations.TriggerSnapshot;
 
 import static 
org.apache.cassandra.distributed.test.log.PlacementSimulator.SimulatedPlacements;
-import static org.apache.cassandra.harry.sut.TokenPlacementModel.Node;
-import static 
org.apache.cassandra.harry.sut.TokenPlacementModel.NtsReplicationFactor;
-import static 
org.apache.cassandra.harry.sut.TokenPlacementModel.ReplicationFactor;
-import static 
org.apache.cassandra.harry.sut.TokenPlacementModel.SimpleReplicationFactor;
-import static org.apache.cassandra.harry.sut.TokenPlacementModel.nodeFactory;
-import static 
org.apache.cassandra.harry.sut.TokenPlacementModel.nodeFactoryHumanReadable;
+import static org.apache.cassandra.harry.model.TokenPlacementModel.*;

Review Comment:
   can you revert the `.*`?  goes against style guide



##########
test/distributed/org/apache/cassandra/distributed/test/log/FuzzTestBase.java:
##########
@@ -24,17 +24,39 @@
 
 import org.apache.cassandra.distributed.Cluster;
 import org.apache.cassandra.distributed.test.TestBaseImpl;
-import org.apache.cassandra.harry.HarryHelper;
+
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_ALLOW_SIMPLE_STRATEGY;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.CASSANDRA_MINIMUM_REPLICATION_FACTOR;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.DISABLE_TCACTIVE_OPENSSL;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.IO_NETTY_TRANSPORT_NONATIVE;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.LOG4J2_DISABLE_JMX;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.LOG4J2_DISABLE_JMX_LEGACY;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.LOG4J_SHUTDOWN_HOOK_ENABLED;
+import static 
org.apache.cassandra.config.CassandraRelevantProperties.ORG_APACHE_CASSANDRA_DISABLE_MBEAN_REGISTRATION;
 
 public class FuzzTestBase extends TestBaseImpl
 {
     @BeforeClass
     public static void beforeClass() throws Throwable
     {
         TestBaseImpl.beforeClass();
-        HarryHelper.init();
+        init();
     }
 
+    public static void init()
+    {
+        // setting both ways as changes between versions
+        LOG4J2_DISABLE_JMX.setBoolean(true);
+        LOG4J2_DISABLE_JMX_LEGACY.setBoolean(true);
+        LOG4J_SHUTDOWN_HOOK_ENABLED.setBoolean(false);
+        CASSANDRA_ALLOW_SIMPLE_STRATEGY.setBoolean(true); // makes easier to 
share OSS tests without RF limits

Review Comment:
   OSS tests?



##########
test/distributed/org/apache/cassandra/fuzz/sai/MultiNodeSAITest.java:
##########
@@ -18,18 +18,66 @@
 
 package org.apache.cassandra.fuzz.sai;
 
+import org.junit.Before;
 import org.junit.BeforeClass;
 
-public class MultiNodeSAITest extends MultiNodeSAITestBase
+import org.apache.cassandra.distributed.Cluster;
+import org.apache.cassandra.distributed.test.sai.SAIUtil;
+import org.apache.cassandra.harry.SchemaSpec;
+
+import static org.apache.cassandra.distributed.api.Feature.GOSSIP;
+import static org.apache.cassandra.distributed.api.Feature.NETWORK;
+
+public class MultiNodeSAITest extends SingleNodeSAITest
 {
     @BeforeClass
     public static void before() throws Throwable
     {
-        MultiNodeSAITestBase.before(false);
+        cluster = Cluster.build()
+                         .withNodes(2)
+                         // At lower fetch sizes, queries w/ hundreds or 
thousands of matches can take a very long time.
+                         .withConfig(defaultConfig().andThen(c -> 
c.set("range_request_timeout", "180s")
+                                                                   
.set("read_request_timeout", "180s")
+                                                                   
.set("native_transport_timeout", "180s")
+                                                                   
.set("slow_query_log_timeout", "180s")
+                                                                   
.with(GOSSIP).with(NETWORK)))
+                         .createWithoutStarting();
+        cluster.setUncaughtExceptionsFilter(t -> {
+            logger.error("Caught exception, reporting during shutdown. 
Ignoring.", t);
+            return true;
+        });

Review Comment:
   please revert this. This log is not correct, this is called w/e any 
exception is unhandled and bubbles up to the thread to handle.  We ignore all 
errors during shutdown, so only non-shutdown errors cause tests to fail... this 
lambda is saying "we found a bug... YOLO, lets ignore!"



##########
test/distributed/org/apache/cassandra/fuzz/topology/TopologyMixupTestBase.java:
##########
@@ -466,14 +465,13 @@ private static IntHashSet asSet(int[] array)
         return set;
     }
 
-    public interface SchemaSpec
+    public interface TestState

Review Comment:
   why this refactor?  This isn't the `TestState` and is confusing as we have 
`State`... please revert, it makes the diff larger, and adds confusion.  I am 
fine with `table()` and `keyspace()` if you wish to make that change, but 
please leave it `SchemaSpec` as this is *only* schema



##########
test/harry/main/README.md:
##########
@@ -1,647 +0,0 @@
-# Harry, a fuzz testing tool for Apache Cassandra

Review Comment:
   why are you removing?



##########
test/harry/main/org/apache/cassandra/harry/ColumnSpec.java:
##########
@@ -0,0 +1,605 @@
+/*
+ * 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.harry;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import accord.utils.Invariants;
+import org.apache.cassandra.cql3.ast.Symbol;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.BooleanType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.InetAddressType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.ShortType;
+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.UTF8Type;
+import org.apache.cassandra.db.marshal.UUIDType;
+import org.apache.cassandra.harry.gen.Generator;
+import org.apache.cassandra.harry.gen.Generators;
+import org.apache.cassandra.harry.gen.TypeAdapters;
+import org.apache.cassandra.utils.ByteArrayUtil;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.TimeUUID;
+
+// TODO: counters
+// TODO: UDTs
+// TODO: collections: frozen/unfrozen
+// TODO: empty / 0 / min / max values if present
+public class ColumnSpec<T>
+{
+    public final String name;
+    public final DataType<T> type;
+    public final Generator<T> gen;
+    public final Kind kind;
+
+    public ColumnSpec(String name,
+                      DataType<T> type,
+                      Generator<T> gen,
+                      Kind kind)
+    {
+
+        this.name = name;
+        this.type = Invariants.nonNull(type);
+        this.gen = Invariants.nonNull(gen);
+        this.kind = kind;
+    }
+
+    public String toCQL()
+    {
+        return String.format("%s %s%s",
+                             Symbol.maybeQuote(name),
+                             type,
+                             kind == Kind.STATIC ? " static" : "");
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ColumnSpec<?> that = (ColumnSpec<?>) o;
+        return Objects.equals(name, that.name) &&
+               Objects.equals(type.cqlName, that.type.cqlName) &&
+               kind == that.kind;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash(name, type.cqlName, kind);
+    }
+
+    public String name()
+    {
+        return name;
+    }
+
+    public boolean isReversed()
+    {
+        return type.isReversed();
+    }
+
+    public String toString()
+    {
+        return name + '(' + type.toString() + ")";
+    }
+
+    public Generator<T> gen()
+    {
+        return gen;
+    }
+
+    public static <T> ColumnSpec<T> pk(String name, DataType<T> type, 
Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.PARTITION_KEY);
+    }
+
+    public static <T> ColumnSpec<T> pk(String name, DataType<T> type)
+    {
+        return new ColumnSpec<>(name, type, (Generator<T>) 
TypeAdapters.forValues(type.asServerType()), Kind.PARTITION_KEY);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> ColumnSpec<T> ck(String name, DataType<T> type, 
Generator<T> gen, boolean isReversed)
+    {
+        return new ColumnSpec(name, isReversed ? 
ReversedType.getInstance(type) : type, gen, Kind.CLUSTERING);
+    }
+
+    public static <T> ColumnSpec<T> ck(String name, DataType<T> type, boolean 
isReversed)
+    {
+        return new ColumnSpec(name, isReversed ? 
ReversedType.getInstance(type) : type,
+                              TypeAdapters.forValues(type.asServerType()),
+                              Kind.CLUSTERING);
+    }
+
+
+    public static <T> ColumnSpec<T> regularColumn(String name, DataType<T> 
type, Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.REGULAR);
+    }
+
+    public static <T> ColumnSpec<T> regularColumn(String name, DataType<T> 
type)
+    {
+        return new ColumnSpec(name, type, 
TypeAdapters.forValues(type.asServerType()), Kind.REGULAR);
+    }
+
+    public static <T> ColumnSpec<T> staticColumn(String name, DataType<T> 
type, Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.STATIC);
+    }
+
+    public static <T> ColumnSpec<T> staticColumn(String name, DataType<T> type)
+    {
+        return new ColumnSpec(name, type, 
TypeAdapters.forValues(type.asServerType()), Kind.STATIC);
+    }
+
+    public enum Kind
+    {
+        CLUSTERING, REGULAR, STATIC, PARTITION_KEY
+    }
+
+    public static abstract class DataType<T>
+    {
+        protected final String cqlName;
+
+        protected DataType(String cqlName)
+        {
+            this.cqlName = cqlName;
+        }
+
+        public abstract /* unsigned */ long typeEntropy();
+
+        public boolean isReversed()
+        {
+            return false;
+        }
+
+        public abstract AbstractType<?> asServerType();
+
+        public final String toString()
+        {
+            return cqlName;
+        }
+
+        public final boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            DataType<?> dataType = (DataType<?>) o;
+            return Objects.equals(cqlName, dataType.cqlName);
+        }
+
+        public final int hashCode()
+        {
+            return Objects.hash(cqlName);
+        }
+
+        public Comparator<T> comparator()
+        {
+            return (o1, o2) -> ((Comparable) o1).compareTo(o2);
+        }
+    }
+
+    public static final DataType<Byte> int8Type = new DataType<Byte>("tinyint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << (8 * Byte.BYTES);
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return ByteType.instance;
+        }
+    };
+
+    public static final DataType<Short> int16Type = new 
DataType<Short>("smallint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << Byte.SIZE;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return ShortType.instance;
+        }
+    };
+
+    public static final DataType<Integer> int32Type = new 
DataType<Integer>("int")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << Integer.SIZE;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return Int32Type.instance;
+        }
+    };
+
+    public static final DataType<Long> int64Type = new DataType<Long>("bigint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << (8 * Long.BYTES - 1);
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return LongType.instance;
+        }
+    };
+
+    public static final DataType<Boolean> booleanType = new 
DataType<Boolean>("boolean")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 2;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return BooleanType.instance;
+        }
+    };
+
+    public static final DataType<Float> floatType = new 
DataType<Float>("float")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << (4 * Float.BYTES);
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return FloatType.instance;
+        }
+    };
+
+    public static final DataType<Double> doubleType = new 
DataType<Double>("double")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return DoubleType.instance;
+        }
+    };
+
+    public static final DataType<ByteBuffer> blobType = new 
DataType<ByteBuffer>("blob")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return BytesType.instance;
+        }
+
+        public Comparator<ByteBuffer> comparator()
+        {
+            return new Comparator<ByteBuffer>()
+            {
+                public int compare(ByteBuffer o1, ByteBuffer o2)
+                {
+                    return ByteBufferUtil.compareUnsigned(o1, o2);
+                }
+            };

Review Comment:
   ```suggestion
               return ByteBufferUtil::compareUnsigned;
   ```



##########
test/distributed/org/apache/cassandra/distributed/test/log/ModelState.java:
##########
@@ -28,8 +28,7 @@
 import java.util.Set;
 import java.util.TreeMap;
 
-import org.apache.cassandra.harry.sut.TokenPlacementModel;
-import org.apache.cassandra.harry.sut.TokenPlacementModel.DCReplicas;
+import static org.apache.cassandra.harry.model.TokenPlacementModel.*;

Review Comment:
   can you revert the `.*`?  goes against style guide



##########
test/distributed/org/apache/cassandra/distributed/test/log/OperationalEquivalenceTest.java:
##########
@@ -44,6 +43,8 @@
 import org.apache.cassandra.tcm.transformations.Register;
 import org.apache.cassandra.tcm.transformations.UnsafeJoin;
 
+import static org.apache.cassandra.harry.model.TokenPlacementModel.*;

Review Comment:
   can you revert the `.*`?  goes against style guide



##########
test/distributed/org/apache/cassandra/fuzz/harry/gen/DataGeneratorsTest.java:
##########
@@ -1,504 +0,0 @@
-/*
- * 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.fuzz.harry.gen;
-
-import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
-import org.apache.cassandra.harry.ddl.ColumnSpec;
-import org.apache.cassandra.harry.gen.Bijections;
-import org.apache.cassandra.harry.gen.Bytes;
-import org.apache.cassandra.harry.gen.DataGenerators;
-import org.apache.cassandra.harry.gen.EntropySource;
-import org.apache.cassandra.harry.gen.Generator;
-import org.apache.cassandra.harry.gen.StringBijection;
-
-public class DataGeneratorsTest

Review Comment:
   what test replaces this?



##########
test/distributed/org/apache/cassandra/distributed/test/log/PlacementSimulator.java:
##########
@@ -30,13 +30,9 @@
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
-import org.apache.cassandra.harry.sut.TokenPlacementModel.Replica;
 import org.junit.Assert;
 
-import static org.apache.cassandra.harry.sut.TokenPlacementModel.Node;
-import static org.apache.cassandra.harry.sut.TokenPlacementModel.Range;
-import static 
org.apache.cassandra.harry.sut.TokenPlacementModel.ReplicationFactor;
-import static org.apache.cassandra.harry.sut.TokenPlacementModel.toRanges;
+import static org.apache.cassandra.harry.model.TokenPlacementModel.*;

Review Comment:
   can you revert the `.*`?  goes against style guide



##########
test/distributed/org/apache/cassandra/distributed/upgrade/ClusterMetadataUpgradeHarryTest.java:
##########
@@ -1,151 +0,0 @@
-/*
- * 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.distributed.upgrade;
-
-import java.util.Collections;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-import com.google.common.util.concurrent.Uninterruptibles;
-
-import org.apache.cassandra.harry.HarryHelper;
-import org.apache.cassandra.harry.core.Configuration;
-import org.apache.cassandra.harry.ddl.SchemaSpec;
-
-import org.apache.cassandra.harry.runner.FlaggedRunner;
-import org.apache.cassandra.harry.sut.injvm.ClusterState;
-import org.apache.cassandra.harry.sut.injvm.ExistingClusterSUT;
-
-import org.apache.cassandra.harry.visitors.MutatingVisitor;
-import org.apache.cassandra.harry.visitors.QueryLogger;
-import org.apache.cassandra.harry.visitors.RandomPartitionValidator;
-
-import org.junit.Test;
-
-import org.apache.cassandra.distributed.Constants;
-import org.apache.cassandra.distributed.api.Feature;
-import org.apache.cassandra.schema.SchemaConstants;
-import org.apache.cassandra.utils.concurrent.CountDownLatch;
-
-
-import static java.util.Arrays.asList;
-import static 
org.apache.cassandra.concurrent.ExecutorFactory.Global.executorFactory;
-import static 
org.apache.cassandra.harry.core.Configuration.VisitorPoolConfiguration.pool;
-import static org.apache.cassandra.harry.ddl.ColumnSpec.asciiType;
-import static org.apache.cassandra.harry.ddl.ColumnSpec.int64Type;
-import static org.apache.cassandra.harry.ddl.ColumnSpec.ck;
-import static org.apache.cassandra.harry.ddl.ColumnSpec.pk;
-import static org.apache.cassandra.harry.ddl.ColumnSpec.regularColumn;
-import static org.apache.cassandra.harry.ddl.ColumnSpec.staticColumn;
-import static org.apache.cassandra.tcm.log.SystemKeyspaceStorage.NAME;
-import static org.junit.Assert.assertEquals;
-
-public class ClusterMetadataUpgradeHarryTest extends UpgradeTestBase

Review Comment:
   what test replaces this?



##########
test/distributed/org/apache/cassandra/fuzz/topology/HarryTopologyMixupTest.java:
##########
@@ -101,28 +108,73 @@ protected void destroyState(State<Spec> state, @Nullable 
Throwable cause)
         if (cause != null) return;
         if (((HarryState) state).numInserts > 0)
         {
-            // do one last read just to make sure we validate the data...
-            var harry = state.schemaSpec.harry;
-            harry.validateAll(harry.quiescentChecker());
+            for (Integer pkIdx : state.testState.pkGen.generated())
+                state.testState.history.selectPartition(pkIdx);
         }
     }
 
     private static BiFunction<RandomSource, Cluster, Spec> 
createSchemaSpec(AccordMode mode)
     {
         return (rs, cluster) -> {
-            long seed = rs.nextLong();
-            var schema = HarryHelper.schemaSpecBuilder("harry", 
"tbl").surjection().inflate(seed);
+            EntropySource rng = new JdkRandomEntropySource(rs.nextLong());
+            Generator<SchemaSpec> schemaGen;
+            org.apache.cassandra.harry.SchemaSpec schema;
             if (mode.kind != AccordMode.Kind.None)
-                schema = schema.withTransactionMode(mode.passthroughMode);
-            ReplayingHistoryBuilder harry = HarryHelper.dataGen(seed,
-                    mode.kind == AccordMode.Kind.Direct ? new 
AccordSut(cluster) : new InJvmSut(cluster),
-                    new TokenPlacementModel.SimpleReplicationFactor(3),
-                    SystemUnderTest.ConsistencyLevel.QUORUM,
-                    schema);
-            cluster.schemaChange(String.format("CREATE KEYSPACE %s WITH 
replication = {'class': 'SimpleStrategy', 'replication_factor' : 3};", 
HarryHelper.KEYSPACE));
-            cluster.schemaChange(schema.compile().cql());
+                schemaGen = SchemaGenerators.schemaSpecGen("harry", "table", 
1000,
+                                                           
SchemaSpec.Options.TRANSACTIONAL_MODE, mode.passthroughMode.toString());
+            else
+                schemaGen = SchemaGenerators.schemaSpecGen("harry", "table", 
1000);
+
+            schema = schemaGen.generate(rng);
+
+            HistoryBuilder harry = new 
ReplayingHistoryBuilder(schema.valueGenerators,
+                                                               hb -> 
InJvmDTestVisitExecutor.builder()
+                                                                               
             .nodeSelector(new InJvmDTestVisitExecutor.NodeSelector()
+                                                                               
             {
+                                                                               
                 private final AtomicLong cnt = new AtomicLong();
+
+                                                                               
                 @Override
+                                                                               
                 public int select(long lts)
+                                                                               
                 {
+                                                                               
                     for (int i = 0; i < 42; i++)
+                                                                               
                     {
+                                                                               
                         int selected = (int) (cnt.getAndIncrement() % 
cluster.size() + 1);
+                                                                               
                         if (!cluster.get(selected).isShutdown())
+                                                                               
                             return selected;
+                                                                               
                     }
+                                                                               
                     throw new IllegalStateException("Unable to find an alive 
instance");
+                                                                               
                 }
+                                                                               
             })
+                                                                               
             .retryPolicy(t -> {
+                                                                               
                 t = Throwables.getRootCause(t);
+                                                                               
                 if (!TIMEOUT_CHECKER.matches(t))
+                                                                               
                     return false;
+
+                                                                               
                 TxnId id;
+                                                                               
                 try
+                                                                               
                 {
+                                                                               
                     id = TxnId.parse(t.getMessage());
+                                                                               
                 }
+                                                                               
                 catch (Throwable t2)
+                                                                               
                 {
+                                                                               
                     return true;
+                                                                               
                 }
+                                                                               
                 try
+                                                                               
                 {
+                                                                               
                     var nodes = cluster.stream().filter(i -> 
!i.isShutdown()).mapToInt(i -> i.config().num()).toArray();

Review Comment:
   remove `var`, see ML for why



##########
test/distributed/org/apache/cassandra/distributed/test/log/SimulatedOperation.java:
##########
@@ -53,7 +53,7 @@
 
 import static org.apache.cassandra.dht.Murmur3Partitioner.LongToken;
 import static org.apache.cassandra.distributed.test.log.CMSTestBase.CMSSut;
-import static org.apache.cassandra.harry.sut.TokenPlacementModel.*;
+import static org.apache.cassandra.harry.model.TokenPlacementModel.*;

Review Comment:
   can you revert the `.*`?  goes against style guide



##########
test/distributed/org/apache/cassandra/fuzz/harry/gen/EntropySourceTest.java:
##########
@@ -1,158 +0,0 @@
-/*
- * 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.fuzz.harry.gen;
-
-import java.util.Random;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import org.apache.cassandra.harry.gen.EntropySource;
-import org.apache.cassandra.harry.gen.distribution.Distribution;
-import org.apache.cassandra.harry.gen.rng.PCGFastPure;
-import org.apache.cassandra.harry.gen.rng.PcgRSUFast;
-import org.apache.cassandra.harry.model.OpSelectors;
-
-public class EntropySourceTest

Review Comment:
   what test replaces this?



##########
test/distributed/org/apache/cassandra/fuzz/topology/HarryTopologyMixupTest.java:
##########
@@ -144,7 +196,7 @@ public PreCheckResult checkPreconditions(State<Spec> state)
     private static CommandGen<Spec> cqlOperations(Spec spec)
     {
         Command<State<Spec>, Void, ?> insert = new HarryCommand(state -> 
"Harry Insert" + state.commandNamePostfix(), state -> {
-            spec.harry.insert();
+            spec.history.insert();

Review Comment:
   personally I prefer `harry`.  `history` exists in the context of 
`TopologyMixup` and we don't actually build histories as we are eager... we 
execute right away... so `history` is kinda a confusing name



##########
test/distributed/org/apache/cassandra/fuzz/topology/TopologyMixupTestBase.java:
##########
@@ -94,13 +92,14 @@
 import static accord.utils.Property.ignoreCommand;
 import static accord.utils.Property.multistep;
 import static accord.utils.Property.stateful;
+import static org.apache.cassandra.harry.model.TokenPlacementModel.*;

Review Comment:
   can you revert the `.*`?  goes against style guide



##########
test/distributed/org/apache/cassandra/fuzz/topology/HarryTopologyMixupTest.java:
##########
@@ -164,90 +216,49 @@ private static CommandGen<Spec> cqlOperations(Spec spec)
 
     private static Command<State<Spec>, Void, ?> validateAll(State<Spec> state)
     {
-        Spec spec = state.schemaSpec;
-        var schema = spec.harry.schema();
-        boolean writeThroughAccord = schema.isWriteTimeFromAccord();
+        Spec spec = state.testState;
         List<Command<State<Spec>, Void, ?>> reads = new ArrayList<>();
-        Model model = spec.harry.quiescentChecker();
-        for (Long pd : new TreeSet<>(spec.harry.pds()))
+
+        for (Integer pkIdx : spec.pkGen.generated())
         {
-            reads.add(new HarryCommand(s -> "Harry Validate pd=" + pd  + 
state.commandNamePostfix(), s -> model.validate(Query.selectAllColumns(schema, 
pd, false))));
-            // as of this writing Accord does not support ORDER BY
-            if (!writeThroughAccord)
-                reads.add(new HarryCommand(s -> "Harry Reverse Validate pd=" + 
pd + state.commandNamePostfix(), s -> 
model.validate(Query.selectAllColumns(schema, pd, true))));

Review Comment:
   what happened to reverse select?



##########
test/distributed/org/apache/cassandra/fuzz/topology/HarryTopologyMixupTest.java:
##########
@@ -164,90 +216,49 @@ private static CommandGen<Spec> cqlOperations(Spec spec)
 
     private static Command<State<Spec>, Void, ?> validateAll(State<Spec> state)
     {
-        Spec spec = state.schemaSpec;
-        var schema = spec.harry.schema();
-        boolean writeThroughAccord = schema.isWriteTimeFromAccord();
+        Spec spec = state.testState;
         List<Command<State<Spec>, Void, ?>> reads = new ArrayList<>();
-        Model model = spec.harry.quiescentChecker();
-        for (Long pd : new TreeSet<>(spec.harry.pds()))
+
+        for (Integer pkIdx : spec.pkGen.generated())
         {
-            reads.add(new HarryCommand(s -> "Harry Validate pd=" + pd  + 
state.commandNamePostfix(), s -> model.validate(Query.selectAllColumns(schema, 
pd, false))));
-            // as of this writing Accord does not support ORDER BY
-            if (!writeThroughAccord)
-                reads.add(new HarryCommand(s -> "Harry Reverse Validate pd=" + 
pd + state.commandNamePostfix(), s -> 
model.validate(Query.selectAllColumns(schema, pd, true))));
+            long pd = spec.schema.valueGenerators.pkGen.descriptorAt(pkIdx);
+            reads.add(new HarryCommand(s -> String.format("Harry Validate 
pd=%d%s", pd, state.commandNamePostfix()), s -> 
spec.history.selectPartition(pkIdx)));
         }
         reads.add(new HarryCommand(s -> "Reset Harry Write State" + 
state.commandNamePostfix(), s -> ((HarryState) s).numInserts = 0));
         return Property.multistep(reads);
     }
 
-    private static class AccordSut extends InJvmSut

Review Comment:
   how are you replacing this behavior?  I don't see you wrapping CQL with 
`BEGIN TRANSACTION` which would be a huge regression for this patch.  Please 
make sure `HarryOnAccord` always wraps every CQL in `BEGIN TRANSACTION`



##########
test/harry/main/org/apache/cassandra/harry/SchemaSpec.java:
##########
@@ -0,0 +1,374 @@
+/*
+ * 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.harry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+
+import accord.utils.Invariants;
+import org.apache.cassandra.cql3.ast.Symbol;
+import org.apache.cassandra.harry.gen.Generator;
+import org.apache.cassandra.harry.gen.Generators;
+import org.apache.cassandra.harry.gen.ValueGenerators;
+import org.apache.cassandra.harry.util.IteratorsUtil;
+import org.apache.cassandra.service.consensus.TransactionalMode;
+import org.apache.cassandra.utils.ByteArrayUtil;
+
+import static 
org.apache.cassandra.harry.SchemaSpec.Options.SKIP_WRITE_TIMESTAMPS;
+import static org.apache.cassandra.harry.SchemaSpec.Options.TRANSACTIONAL_MODE;
+import static org.apache.cassandra.harry.gen.InvertibleGenerator.MAX_ENTROPY;
+
+public class SchemaSpec
+{
+    public final String keyspace;
+    public final String table;
+
+    public final List<ColumnSpec<?>> partitionKeys;
+    public final List<ColumnSpec<?>> clusteringKeys;
+    public final List<ColumnSpec<?>> regularColumns;
+    public final List<ColumnSpec<?>> staticColumns;
+
+    public final List<ColumnSpec<?>> allColumnInSelectOrder;
+    public final ValueGenerators valueGenerators;
+    public final Map<Options, Object> options;

Review Comment:
   we spoke about this, but I honestly think the options map makes this harder 
for people... you have to figure out what options exist (enum so not too hard) 
and what format they take... I strongly prefer the old way of having each 
"option" as its own field.



##########
test/harry/main/org/apache/cassandra/harry/ColumnSpec.java:
##########
@@ -0,0 +1,605 @@
+/*
+ * 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.harry;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import accord.utils.Invariants;
+import org.apache.cassandra.cql3.ast.Symbol;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.BooleanType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.InetAddressType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.ShortType;
+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.UTF8Type;
+import org.apache.cassandra.db.marshal.UUIDType;
+import org.apache.cassandra.harry.gen.Generator;
+import org.apache.cassandra.harry.gen.Generators;
+import org.apache.cassandra.harry.gen.TypeAdapters;
+import org.apache.cassandra.utils.ByteArrayUtil;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.TimeUUID;
+
+// TODO: counters
+// TODO: UDTs
+// TODO: collections: frozen/unfrozen
+// TODO: empty / 0 / min / max values if present
+public class ColumnSpec<T>
+{
+    public final String name;
+    public final DataType<T> type;
+    public final Generator<T> gen;
+    public final Kind kind;
+
+    public ColumnSpec(String name,
+                      DataType<T> type,
+                      Generator<T> gen,
+                      Kind kind)
+    {
+
+        this.name = name;
+        this.type = Invariants.nonNull(type);
+        this.gen = Invariants.nonNull(gen);
+        this.kind = kind;
+    }
+
+    public String toCQL()
+    {
+        return String.format("%s %s%s",
+                             Symbol.maybeQuote(name),
+                             type,
+                             kind == Kind.STATIC ? " static" : "");
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ColumnSpec<?> that = (ColumnSpec<?>) o;
+        return Objects.equals(name, that.name) &&
+               Objects.equals(type.cqlName, that.type.cqlName) &&
+               kind == that.kind;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash(name, type.cqlName, kind);
+    }
+
+    public String name()
+    {
+        return name;
+    }
+
+    public boolean isReversed()
+    {
+        return type.isReversed();
+    }
+
+    public String toString()
+    {
+        return name + '(' + type.toString() + ")";
+    }
+
+    public Generator<T> gen()
+    {
+        return gen;
+    }
+
+    public static <T> ColumnSpec<T> pk(String name, DataType<T> type, 
Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.PARTITION_KEY);
+    }
+
+    public static <T> ColumnSpec<T> pk(String name, DataType<T> type)
+    {
+        return new ColumnSpec<>(name, type, (Generator<T>) 
TypeAdapters.forValues(type.asServerType()), Kind.PARTITION_KEY);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> ColumnSpec<T> ck(String name, DataType<T> type, 
Generator<T> gen, boolean isReversed)
+    {
+        return new ColumnSpec(name, isReversed ? 
ReversedType.getInstance(type) : type, gen, Kind.CLUSTERING);
+    }
+
+    public static <T> ColumnSpec<T> ck(String name, DataType<T> type, boolean 
isReversed)
+    {
+        return new ColumnSpec(name, isReversed ? 
ReversedType.getInstance(type) : type,
+                              TypeAdapters.forValues(type.asServerType()),
+                              Kind.CLUSTERING);
+    }
+
+
+    public static <T> ColumnSpec<T> regularColumn(String name, DataType<T> 
type, Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.REGULAR);
+    }
+
+    public static <T> ColumnSpec<T> regularColumn(String name, DataType<T> 
type)
+    {
+        return new ColumnSpec(name, type, 
TypeAdapters.forValues(type.asServerType()), Kind.REGULAR);
+    }
+
+    public static <T> ColumnSpec<T> staticColumn(String name, DataType<T> 
type, Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.STATIC);
+    }
+
+    public static <T> ColumnSpec<T> staticColumn(String name, DataType<T> type)
+    {
+        return new ColumnSpec(name, type, 
TypeAdapters.forValues(type.asServerType()), Kind.STATIC);
+    }
+
+    public enum Kind
+    {
+        CLUSTERING, REGULAR, STATIC, PARTITION_KEY
+    }
+
+    public static abstract class DataType<T>
+    {
+        protected final String cqlName;
+
+        protected DataType(String cqlName)
+        {
+            this.cqlName = cqlName;
+        }
+
+        public abstract /* unsigned */ long typeEntropy();
+
+        public boolean isReversed()
+        {
+            return false;
+        }
+
+        public abstract AbstractType<?> asServerType();
+
+        public final String toString()
+        {
+            return cqlName;
+        }
+
+        public final boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            DataType<?> dataType = (DataType<?>) o;
+            return Objects.equals(cqlName, dataType.cqlName);
+        }
+
+        public final int hashCode()
+        {
+            return Objects.hash(cqlName);
+        }
+
+        public Comparator<T> comparator()
+        {
+            return (o1, o2) -> ((Comparable) o1).compareTo(o2);

Review Comment:
   can we remove?  I have found way too many bugs in `AbstractType` due to 
default implementations that make assumptions... someone extends this class and 
doesn't *know* it needs to think about `comparator` will have weird bugs to 
debug...
   
   we should make this abstract or ask for this in the constructor.
   
   We can even have the default named constructor require comparable and use 
`Comparator.naturalOrdering()`



##########
test/harry/main/org/apache/cassandra/harry/checker/TestHelper.java:
##########
@@ -0,0 +1,64 @@
+/*
+ * 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.harry.checker;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.distributed.test.ExecUtil;
+import org.apache.cassandra.harry.gen.EntropySource;
+import org.apache.cassandra.harry.gen.rng.JdkRandomEntropySource;
+
+public class TestHelper
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(TestHelper.class);
+
+    public static void withRandom(ModelChecker.ThrowingConsumer<EntropySource> 
rng)
+    {
+        withRandom(System.nanoTime(), rng);

Review Comment:
   FYI there is a `accord.utils.SeedProvider#nextSeed`.  This matches jdk's 
random seed behavior



##########
test/harry/main/org/apache/cassandra/harry/ColumnSpec.java:
##########
@@ -0,0 +1,605 @@
+/*
+ * 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.harry;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.UUID;
+
+import accord.utils.Invariants;
+import org.apache.cassandra.cql3.ast.Symbol;
+import org.apache.cassandra.db.marshal.AbstractType;
+import org.apache.cassandra.db.marshal.AsciiType;
+import org.apache.cassandra.db.marshal.BooleanType;
+import org.apache.cassandra.db.marshal.ByteType;
+import org.apache.cassandra.db.marshal.BytesType;
+import org.apache.cassandra.db.marshal.DecimalType;
+import org.apache.cassandra.db.marshal.DoubleType;
+import org.apache.cassandra.db.marshal.FloatType;
+import org.apache.cassandra.db.marshal.InetAddressType;
+import org.apache.cassandra.db.marshal.Int32Type;
+import org.apache.cassandra.db.marshal.IntegerType;
+import org.apache.cassandra.db.marshal.LongType;
+import org.apache.cassandra.db.marshal.ShortType;
+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.UTF8Type;
+import org.apache.cassandra.db.marshal.UUIDType;
+import org.apache.cassandra.harry.gen.Generator;
+import org.apache.cassandra.harry.gen.Generators;
+import org.apache.cassandra.harry.gen.TypeAdapters;
+import org.apache.cassandra.utils.ByteArrayUtil;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.TimeUUID;
+
+// TODO: counters
+// TODO: UDTs
+// TODO: collections: frozen/unfrozen
+// TODO: empty / 0 / min / max values if present
+public class ColumnSpec<T>
+{
+    public final String name;
+    public final DataType<T> type;
+    public final Generator<T> gen;
+    public final Kind kind;
+
+    public ColumnSpec(String name,
+                      DataType<T> type,
+                      Generator<T> gen,
+                      Kind kind)
+    {
+
+        this.name = name;
+        this.type = Invariants.nonNull(type);
+        this.gen = Invariants.nonNull(gen);
+        this.kind = kind;
+    }
+
+    public String toCQL()
+    {
+        return String.format("%s %s%s",
+                             Symbol.maybeQuote(name),
+                             type,
+                             kind == Kind.STATIC ? " static" : "");
+    }
+
+    @Override
+    public boolean equals(Object o)
+    {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        ColumnSpec<?> that = (ColumnSpec<?>) o;
+        return Objects.equals(name, that.name) &&
+               Objects.equals(type.cqlName, that.type.cqlName) &&
+               kind == that.kind;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        return Objects.hash(name, type.cqlName, kind);
+    }
+
+    public String name()
+    {
+        return name;
+    }
+
+    public boolean isReversed()
+    {
+        return type.isReversed();
+    }
+
+    public String toString()
+    {
+        return name + '(' + type.toString() + ")";
+    }
+
+    public Generator<T> gen()
+    {
+        return gen;
+    }
+
+    public static <T> ColumnSpec<T> pk(String name, DataType<T> type, 
Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.PARTITION_KEY);
+    }
+
+    public static <T> ColumnSpec<T> pk(String name, DataType<T> type)
+    {
+        return new ColumnSpec<>(name, type, (Generator<T>) 
TypeAdapters.forValues(type.asServerType()), Kind.PARTITION_KEY);
+    }
+
+    @SuppressWarnings("unchecked")
+    public static <T> ColumnSpec<T> ck(String name, DataType<T> type, 
Generator<T> gen, boolean isReversed)
+    {
+        return new ColumnSpec(name, isReversed ? 
ReversedType.getInstance(type) : type, gen, Kind.CLUSTERING);
+    }
+
+    public static <T> ColumnSpec<T> ck(String name, DataType<T> type, boolean 
isReversed)
+    {
+        return new ColumnSpec(name, isReversed ? 
ReversedType.getInstance(type) : type,
+                              TypeAdapters.forValues(type.asServerType()),
+                              Kind.CLUSTERING);
+    }
+
+
+    public static <T> ColumnSpec<T> regularColumn(String name, DataType<T> 
type, Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.REGULAR);
+    }
+
+    public static <T> ColumnSpec<T> regularColumn(String name, DataType<T> 
type)
+    {
+        return new ColumnSpec(name, type, 
TypeAdapters.forValues(type.asServerType()), Kind.REGULAR);
+    }
+
+    public static <T> ColumnSpec<T> staticColumn(String name, DataType<T> 
type, Generator<T> gen)
+    {
+        return new ColumnSpec<>(name, type, gen, Kind.STATIC);
+    }
+
+    public static <T> ColumnSpec<T> staticColumn(String name, DataType<T> type)
+    {
+        return new ColumnSpec(name, type, 
TypeAdapters.forValues(type.asServerType()), Kind.STATIC);
+    }
+
+    public enum Kind
+    {
+        CLUSTERING, REGULAR, STATIC, PARTITION_KEY
+    }
+
+    public static abstract class DataType<T>
+    {
+        protected final String cqlName;
+
+        protected DataType(String cqlName)
+        {
+            this.cqlName = cqlName;
+        }
+
+        public abstract /* unsigned */ long typeEntropy();
+
+        public boolean isReversed()
+        {
+            return false;
+        }
+
+        public abstract AbstractType<?> asServerType();
+
+        public final String toString()
+        {
+            return cqlName;
+        }
+
+        public final boolean equals(Object o)
+        {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            DataType<?> dataType = (DataType<?>) o;
+            return Objects.equals(cqlName, dataType.cqlName);
+        }
+
+        public final int hashCode()
+        {
+            return Objects.hash(cqlName);
+        }
+
+        public Comparator<T> comparator()
+        {
+            return (o1, o2) -> ((Comparable) o1).compareTo(o2);
+        }
+    }
+
+    public static final DataType<Byte> int8Type = new DataType<Byte>("tinyint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << (8 * Byte.BYTES);
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return ByteType.instance;
+        }
+    };
+
+    public static final DataType<Short> int16Type = new 
DataType<Short>("smallint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << Byte.SIZE;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return ShortType.instance;
+        }
+    };
+
+    public static final DataType<Integer> int32Type = new 
DataType<Integer>("int")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << Integer.SIZE;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return Int32Type.instance;
+        }
+    };
+
+    public static final DataType<Long> int64Type = new DataType<Long>("bigint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << (8 * Long.BYTES - 1);
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return LongType.instance;
+        }
+    };
+
+    public static final DataType<Boolean> booleanType = new 
DataType<Boolean>("boolean")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 2;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return BooleanType.instance;
+        }
+    };
+
+    public static final DataType<Float> floatType = new 
DataType<Float>("float")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << (4 * Float.BYTES);
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return FloatType.instance;
+        }
+    };
+
+    public static final DataType<Double> doubleType = new 
DataType<Double>("double")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return DoubleType.instance;
+        }
+    };
+
+    public static final DataType<ByteBuffer> blobType = new 
DataType<ByteBuffer>("blob")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return BytesType.instance;
+        }
+
+        public Comparator<ByteBuffer> comparator()
+        {
+            return new Comparator<ByteBuffer>()
+            {
+                public int compare(ByteBuffer o1, ByteBuffer o2)
+                {
+                    return ByteBufferUtil.compareUnsigned(o1, o2);
+                }
+            };
+        }
+    };
+
+    public static final DataType<String> asciiType = new 
DataType<String>("ascii")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return AsciiType.instance;
+        }
+    };
+
+    // utf8
+    public static final DataType<String> textType = new 
DataType<String>("text")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return UTF8Type.instance;
+        }
+
+        @Override
+        public Comparator<String> comparator()
+        {
+            return new Comparator<String>()
+            {
+                public int compare(String o1, String o2)
+                {
+                    return ByteArrayUtil.compareUnsigned(o1.getBytes(), 
o2.getBytes());
+                }
+            };
+        }
+    };
+
+    public static final DataType<UUID> uuidType = new DataType<UUID>("uuid")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return UUIDType.instance;
+        }
+
+        public Comparator<UUID> comparator()
+        {
+            // TODO: avoid serialization to match C* order
+            return new Comparator<UUID>()
+            {
+                public int compare(UUID o1, UUID o2)
+                {
+                    return 
UUIDType.instance.compare(UUIDType.instance.decompose(o1),
+                                                     
UUIDType.instance.decompose(o2));
+                }
+            };
+        }
+    };
+
+    public static final DataType<TimeUUID> timeUuidType = new 
DataType<TimeUUID>("timeuuid")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return TimeUUIDType.instance;
+        }
+    };
+
+    public static final DataType<Date> timestampType = new 
DataType<Date>("timestamp")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return TimestampType.instance;
+        }
+    };
+
+    public static final DataType<BigInteger> varintType = new 
DataType<BigInteger>("varint")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return IntegerType.instance;
+        }
+    };
+
+    public static final DataType<Long> timeType = new DataType<Long>("time")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return TimeType.instance;
+        }
+    };
+
+    public static final DataType<BigDecimal> decimalType = new 
DataType<BigDecimal>("decimal")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return DecimalType.instance;
+        }
+    };
+
+    public static final DataType<InetAddress> inetType = new 
DataType<InetAddress>("inet")
+    {
+        @Override
+        public /* unsigned */ long typeEntropy()
+        {
+            return 1L << 63;
+        }
+
+        @Override
+        public AbstractType<?> asServerType()
+        {
+            return InetAddressType.instance;
+        }
+
+        @Override
+        public Comparator<InetAddress> comparator()
+        {
+            return (o1, o2) -> {
+                byte[] b1 = o1.getAddress();
+                byte[] b2 = o2.getAddress();
+                return ByteArrayUtil.compareUnsigned(b1, b2);
+            };
+        }
+    };
+
+    public static final List<DataType<?>> types = new ArrayList<>()

Review Comment:
   immutable list?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to