2424import java .util .ArrayList ;
2525import java .util .Collections ;
2626import java .util .EnumMap ;
27+ import java .util .LinkedHashMap ;
2728import java .util .List ;
2829import java .util .Map ;
2930import java .util .TreeMap ;
3839import com .google .common .util .concurrent .AsyncFunction ;
3940import com .google .common .util .concurrent .FutureCallback ;
4041
42+ import org .slf4j .Logger ;
43+ import org .slf4j .LoggerFactory ;
44+
4145import org .apache .cassandra .auth .PasswordSaltSupplier ;
4246import org .apache .cassandra .concurrent .ExecutorFactory ;
4347import org .apache .cassandra .config .CassandraRelevantProperties ;
121125@ SuppressWarnings ("RedundantCast" )
122126public class ClusterSimulation <S extends Simulation > implements AutoCloseable
123127{
128+ private static final Logger logger = LoggerFactory .getLogger (ClusterSimulation .class );
129+
124130 static
125131 {
126132 CassandraRelevantProperties .TEST_STORAGE_COMPATIBILITY_MODE .setEnum (StorageCompatibilityMode .NONE );
@@ -220,6 +226,9 @@ public static abstract class Builder<S extends Simulation>
220226 protected String transactionalMode = "off" ;
221227 protected FutureActionSchedulerFactory futureActionSchedulerFactory ;
222228 protected PerVerbFutureActionSchedulersFactory perVerbFutureActionSchedulersFactory ;
229+ protected String memtableType = null ;
230+ protected String memtableAllocationType = null ;
231+ protected String sstableFormat = null ;
223232
224233 public Builder <S > failures (Failures failures )
225234 {
@@ -623,6 +632,24 @@ public TransactionalMode transactionalMode()
623632 return TransactionalMode .fromString (transactionalMode );
624633 }
625634
635+ public Builder <S > memtableType (String type )
636+ {
637+ this .memtableType = type ;
638+ return this ;
639+ }
640+
641+ public Builder <S > memtableAllocationType (String type )
642+ {
643+ this .memtableAllocationType = type ;
644+ return this ;
645+ }
646+
647+ public Builder <S > sstableFormat (String format )
648+ {
649+ this .sstableFormat = format ;
650+ return this ;
651+ }
652+
626653 public abstract ClusterSimulation <S > create (long seed ) throws IOException ;
627654 }
628655
@@ -767,8 +794,67 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
767794
768795 execution = new SimulatedExecution ();
769796
797+ // Track randomized configuration for consolidated logging
798+ Map <String , String > randomizedConfig = new LinkedHashMap <>();
799+ randomizedConfig .put ("nodes" , String .valueOf (numOfNodes ));
800+ randomizedConfig .put ("dcs" , String .valueOf (numOfDcs ));
801+
802+ // Log replication factors
803+ StringBuilder rfString = new StringBuilder ();
804+ for (int i = 0 ; i < numOfDcs ; ++i )
805+ {
806+ if (i > 0 )
807+ rfString .append ("," );
808+ rfString .append ("dc" ).append (i ).append (":" ).append (initialRf [i ]);
809+ }
810+ randomizedConfig .put ("replication_factors" , rfString .toString ());
811+
812+ // Randomize memtable type
813+ String memtableType ;
814+ if (builder .memtableType != null )
815+ {
816+ memtableType = builder .memtableType ;
817+ }
818+ else
819+ {
820+ String [] memtableTypes = {"TrieMemtable" , "SkipListMemtable" };
821+ memtableType = memtableTypes [random .uniform (0 , memtableTypes .length )];
822+ }
823+ randomizedConfig .put ("memtable" , memtableType );
824+
825+ // Randomize memtable allocation type (heap-based only to avoid InterruptibleChannel issues with offheap)
826+ String memtableAllocationType ;
827+ if (builder .memtableAllocationType != null )
828+ {
829+ memtableAllocationType = builder .memtableAllocationType ;
830+ }
831+ else
832+ {
833+ String [] allocationTypes = {
834+ "heap_buffers" , // Slab allocator (pooled memory)
835+ "unslabbed_heap_buffers" // Direct heap allocation (no pooling)
836+ };
837+ memtableAllocationType = allocationTypes [random .uniform (0 , allocationTypes .length )];
838+ }
839+ randomizedConfig .put ("memtable_allocation_type" , memtableAllocationType );
840+
841+ // Randomize SSTable format
842+ String sstableFormat ;
843+ if (builder .sstableFormat != null )
844+ {
845+ sstableFormat = builder .sstableFormat ;
846+ }
847+ else
848+ {
849+ String [] formats = {"big" , "bti" };
850+ sstableFormat = formats [random .uniform (0 , formats .length )];
851+ }
852+ randomizedConfig .put ("sstable_format" , sstableFormat );
853+
770854 KindOfSequence kindOfDriftSequence = Choices .uniform (KindOfSequence .values ()).choose (random );
771855 KindOfSequence kindOfDiscontinuitySequence = Choices .uniform (KindOfSequence .values ()).choose (random );
856+ randomizedConfig .put ("clock_drift_sequence" , kindOfDriftSequence .toString ());
857+ randomizedConfig .put ("clock_discontinuity_sequence" , kindOfDiscontinuitySequence .toString ());
772858 time = new SimulatedTime (numOfNodes , random , 1577836800000L /*Jan 1st UTC*/ , builder .clockDriftNanos , kindOfDriftSequence ,
773859 kindOfDiscontinuitySequence .period (builder .clockDiscontinuitIntervalNanos , random ),
774860 builder .timeListener );
@@ -800,7 +886,6 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
800886
801887 Failures failures = builder .failures ;
802888 ThreadAllocator threadAllocator = new ThreadAllocator (random , builder .threadCount , numOfNodes );
803-
804889 cluster = snitch .setup (Cluster .build (numOfNodes )
805890 .withRoot (fs .getPath ("/cassandra" ))
806891 .withSharedClasses (sharedClassPredicate )
@@ -812,7 +897,7 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
812897 .set ("cas_contention_timeout" , String .format ("%dms" , NANOSECONDS .toMillis (builder .contentionTimeoutNanos )))
813898 .set ("request_timeout" , String .format ("%dms" , NANOSECONDS .toMillis (builder .requestTimeoutNanos )))
814899 .set ("memtable_heap_space" , "1MiB" )
815- .set ("memtable_allocation_type" , builder .memoryListener != null ? "unslabbed_heap_buffers_logged" : "heap_buffers" )
900+ .set ("memtable_allocation_type" , builder .memoryListener != null ? "unslabbed_heap_buffers_logged" : memtableAllocationType )
816901 .set ("file_cache_size" , "16MiB" )
817902 .set ("use_deterministic_table_id" , true )
818903 .set ("accord.queue_submission_model" , "ASYNC" )
@@ -822,7 +907,22 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
822907 .set ("commitlog_compression" , new ParameterizedClass (LZ4Compressor .class .getName (), emptyMap ()))
823908 .set ("commitlog_sync" , "batch" )
824909 .set ("accord.journal.flush_mode" , "BATCH" )
825- .set ("accord.command_store_shard_count" , "4" );
910+ .set ("accord.command_store_shard_count" , "4" )
911+ .set ("sstable" , Map .of ("selected_format" , sstableFormat ));
912+
913+ if (memtableType .equals ("TrieMemtable" ))
914+ {
915+ config .set ("memtable" , Map .of (
916+ "configurations" , Map .of (
917+ "default" , Map .of ("class_name" , "TrieMemtable" ))));
918+ }
919+ else
920+ {
921+ config .set ("memtable" , Map .of (
922+ "configurations" , Map .of (
923+ "default" , Map .of ("class_name" , "SkipListMemtable" ))));
924+ }
925+
826926 // TODO: Add remove() to IInstanceConfig
827927 if (config instanceof InstanceConfig )
828928 {
@@ -921,13 +1021,22 @@ public void afterStartup(IInstance i)
9211021
9221022 scheduler = builder .schedulerFactory .create (random );
9231023 // TODO (required): we aren't passing paxos variant change parameter anymore
924- options = new ClusterActions .Options (builder .topologyChangeLimit , Choices .uniform (KindOfSequence .values ()).choose (random ).period (builder .topologyChangeIntervalNanos , random ),
1024+ KindOfSequence topologyChangeSequence = Choices .uniform (KindOfSequence .values ()).choose (random );
1025+ options = new ClusterActions .Options (builder .topologyChangeLimit , topologyChangeSequence .period (builder .topologyChangeIntervalNanos , random ),
9251026 Choices .random (random , builder .topologyChanges ),
9261027 builder .consensusChangeLimit , Choices .uniform (KindOfSequence .values ()).choose (random ).period (builder .consensusChangeIntervalNanos , random ),
9271028 Choices .random (random , builder .consensusChanges ),
9281029 minRf , initialRf , maxRf , null );
9291030 this .factory = factory ;
9301031
1032+ // Add remaining randomization tracking
1033+ if (futureActionScheduler instanceof SimulatedFutureActionScheduler )
1034+ randomizedConfig .put ("network_scheduler" , ((SimulatedFutureActionScheduler ) futureActionScheduler ).getKind ().toString ());
1035+ randomizedConfig .put ("runnable_scheduler" , scheduler .getClass ().getSimpleName ());
1036+ randomizedConfig .put ("topology_change_sequence" , topologyChangeSequence .toString ());
1037+
1038+ logger .warn ("Seed 0x{} - Randomized config: {}" , Long .toHexString (seed ), randomizedConfig );
1039+
9311040 // during cluster shutdown ignore all failures as there is no reason to track them
9321041 onPreShutdown .add (() -> {simulated .failures .ignoreFailures (); return null ;});
9331042 }
0 commit comments