2424import java .util .ArrayList ;
2525import java .util .Arrays ;
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 .concurrent .ExecutorFactory ;
46+ import org .apache .cassandra .config .CassandraRelevantProperties ;
4247import org .apache .cassandra .config .ParameterizedClass ;
4348import org .apache .cassandra .distributed .Cluster ;
4449import org .apache .cassandra .distributed .api .Feature ;
112117@ SuppressWarnings ("RedundantCast" )
113118public class ClusterSimulation <S extends Simulation > implements AutoCloseable
114119{
120+ private static final Logger logger = LoggerFactory .getLogger (ClusterSimulation .class );
121+
115122 public static final Class <?>[] SHARE = new Class []
116123 {
117124 AsyncFunction .class ,
@@ -188,6 +195,9 @@ public static abstract class Builder<S extends Simulation>
188195 protected HeapPool .Logged .Listener memoryListener ;
189196 protected SimulatedTime .Listener timeListener = (i1 , i2 ) -> {};
190197 protected LongConsumer onThreadLocalRandomCheck ;
198+ protected String memtableType = null ;
199+ protected String memtableAllocationType = null ;
200+ protected String sstableFormat = null ;
191201
192202 public Debug debug ()
193203 {
@@ -516,6 +526,24 @@ public Builder<S> onThreadLocalRandomCheck(LongConsumer runnable)
516526 return this ;
517527 }
518528
529+ public Builder <S > memtableType (String type )
530+ {
531+ this .memtableType = type ;
532+ return this ;
533+ }
534+
535+ public Builder <S > memtableAllocationType (String type )
536+ {
537+ this .memtableAllocationType = type ;
538+ return this ;
539+ }
540+
541+ public Builder <S > sstableFormat (String format )
542+ {
543+ this .sstableFormat = format ;
544+ return this ;
545+ }
546+
519547 public abstract ClusterSimulation <S > create (long seed ) throws IOException ;
520548 }
521549
@@ -654,8 +682,68 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
654682
655683 execution = new SimulatedExecution ();
656684
685+ // Track randomized configuration for consolidated logging
686+ Map <String , String > randomizedConfig = new LinkedHashMap <>();
687+ randomizedConfig .put ("nodes" , String .valueOf (numOfNodes ));
688+ randomizedConfig .put ("dcs" , String .valueOf (numOfDcs ));
689+
690+ // Log replication factors
691+ StringBuilder rfString = new StringBuilder ();
692+ for (int i = 0 ; i < numOfDcs ; ++i )
693+ {
694+ if (i > 0 )
695+ rfString .append ("," );
696+ rfString .append ("dc" ).append (i ).append (":" ).append (initialRf [i ]);
697+ }
698+ randomizedConfig .put ("replication_factors" , rfString .toString ());
699+
700+ // Randomize memtable type
701+ String memtableType ;
702+ if (builder .memtableType != null )
703+ {
704+ memtableType = builder .memtableType ;
705+ }
706+ else
707+ {
708+ String [] memtableTypes = {"TrieMemtable" , "SkipListMemtable" };
709+ memtableType = memtableTypes [random .uniform (0 , memtableTypes .length )];
710+ }
711+ randomizedConfig .put ("memtable" , memtableType );
712+
713+ // Randomize memtable allocation type (heap-based only to avoid InterruptibleChannel issues with offheap)
714+ String memtableAllocationType ;
715+ if (builder .memtableAllocationType != null )
716+ {
717+ memtableAllocationType = builder .memtableAllocationType ;
718+ }
719+ else
720+ {
721+ String [] allocationTypes = {
722+ "heap_buffers" , // Slab allocator (pooled memory)
723+ "unslabbed_heap_buffers" // Direct heap allocation (no pooling)
724+ };
725+ memtableAllocationType = allocationTypes [random .uniform (0 , allocationTypes .length )];
726+ }
727+ randomizedConfig .put ("memtable_allocation_type" , memtableAllocationType );
728+
729+ // Randomize SSTable format
730+ String sstableFormat ;
731+ if (builder .sstableFormat != null )
732+ {
733+ sstableFormat = builder .sstableFormat ;
734+ }
735+ else
736+ {
737+ String [] formats = {"big" , "bti" };
738+ sstableFormat = formats [random .uniform (0 , formats .length )];
739+ }
740+ randomizedConfig .put ("sstable_format" , sstableFormat );
741+ CassandraRelevantProperties .SSTABLE_FORMAT_DEFAULT .setString (sstableFormat );
742+
657743 KindOfSequence kindOfDriftSequence = Choices .uniform (KindOfSequence .values ()).choose (random );
658744 KindOfSequence kindOfDiscontinuitySequence = Choices .uniform (KindOfSequence .values ()).choose (random );
745+ randomizedConfig .put ("clock_drift_sequence" , kindOfDriftSequence .toString ());
746+ randomizedConfig .put ("clock_discontinuity_sequence" , kindOfDiscontinuitySequence .toString ());
659747 time = new SimulatedTime (numOfNodes , random , 1577836800000L /*Jan 1st UTC*/ , builder .clockDriftNanos , kindOfDriftSequence ,
660748 kindOfDiscontinuitySequence .period (builder .clockDiscontinuitIntervalNanos , random ),
661749 builder .timeListener );
@@ -672,6 +760,7 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
672760 ThreadAllocator threadAllocator = new ThreadAllocator (random , builder .threadCount , numOfNodes );
673761 List <String > allowedDiskAccessModes = Arrays .asList ("mmap" , "mmap_index_only" , "standard" );
674762 String disk_access_mode = allowedDiskAccessModes .get (random .uniform (0 , allowedDiskAccessModes .size () - 1 ));
763+ randomizedConfig .put ("disk_access_mode" , disk_access_mode );
675764 boolean commitlogCompressed = random .decide (.5f );
676765 cluster = snitch .setup (Cluster .build (numOfNodes )
677766 .withRoot (fs .getPath ("/cassandra" ))
@@ -683,13 +772,26 @@ public ClusterSimulation(RandomSource random, long seed, int uniqueNum,
683772 .set ("cas_contention_timeout" , String .format ("%dms" , NANOSECONDS .toMillis (builder .contentionTimeoutNanos )))
684773 .set ("request_timeout" , String .format ("%dms" , NANOSECONDS .toMillis (builder .requestTimeoutNanos )))
685774 .set ("memtable_heap_space" , "1MiB" )
686- .set ("memtable_allocation_type" , builder .memoryListener != null ? "unslabbed_heap_buffers_logged" : "heap_buffers" )
775+ .set ("memtable_allocation_type" , builder .memoryListener != null ? "unslabbed_heap_buffers_logged" : memtableAllocationType )
687776 .set ("file_cache_size" , "16MiB" )
688777 .set ("use_deterministic_table_id" , true )
689778 .set ("disk_access_mode" , disk_access_mode )
690779 .set ("failure_detector" , SimulatedFailureDetector .Instance .class .getName ())
691780 .set ("commitlog_sync" , "batch" );
692781
782+ if (memtableType .equals ("TrieMemtable" ))
783+ {
784+ config .set ("memtable" , Map .of (
785+ "configurations" , Map .of (
786+ "default" , Map .of ("class_name" , "TrieMemtable" ))));
787+ }
788+ else
789+ {
790+ config .set ("memtable" , Map .of (
791+ "configurations" , Map .of (
792+ "default" , Map .of ("class_name" , "SkipListMemtable" ))));
793+ }
794+
693795 // TODO: Add remove() to IInstanceConfig
694796 if (config instanceof InstanceConfig )
695797 {
@@ -779,10 +881,18 @@ public void afterStartup(IInstance i)
779881 simulated .register (futureActionScheduler );
780882
781883 RunnableActionScheduler scheduler = builder .schedulerFactory .create (random );
782- ClusterActions .Options options = new ClusterActions .Options (builder .topologyChangeLimit , Choices .uniform (KindOfSequence .values ()).choose (random ).period (builder .topologyChangeIntervalNanos , random ),
884+ KindOfSequence topologyChangeSequence = Choices .uniform (KindOfSequence .values ()).choose (random );
885+ ClusterActions .Options options = new ClusterActions .Options (builder .topologyChangeLimit , topologyChangeSequence .period (builder .topologyChangeIntervalNanos , random ),
783886 Choices .random (random , builder .topologyChanges ),
784887 minRf , initialRf , maxRf , null );
785888 simulation = factory .create (simulated , scheduler , cluster , options );
889+
890+ // Add remaining randomization tracking
891+ randomizedConfig .put ("network_scheduler" , futureActionScheduler .getKind ().toString ());
892+ randomizedConfig .put ("runnable_scheduler" , scheduler .getClass ().getSimpleName ());
893+ randomizedConfig .put ("topology_change_sequence" , topologyChangeSequence .toString ());
894+
895+ logger .warn ("Seed 0x{} - Randomized config: {}" , Long .toHexString (seed ), randomizedConfig );
786896 }
787897
788898 public synchronized void close () throws IOException
0 commit comments