Skip to content

Commit de17b5f

Browse files
committed
Added option for random execution without duplicates
1 parent 8fa7a6f commit de17b5f

File tree

2 files changed

+86
-0
lines changed

2 files changed

+86
-0
lines changed

stage/stage.go

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ type Stage struct {
6161
// Use RandomlyExecuteUntil to specify a duration like "1h" or an integer as the number of queries should be executed
6262
// before exiting.
6363
RandomlyExecuteUntil *string `json:"randomly_execute_until,omitempty"`
64+
// If NoRandomDuplicates is set to true, queries will not be repeated during random execution
65+
// until all queries have been executed once. After that, the selection pool resets if more
66+
// executions are needed.
67+
NoRandomDuplicates *bool `json:"no_random_duplicates,omitempty"`
6468
// If not set, the default is 1. The default value is set when the stage is run.
6569
ColdRuns *int `json:"cold_runs,omitempty" validate:"omitempty,gte=0"`
6670
// If not set, the default is 0.
@@ -343,9 +347,17 @@ func (s *Stage) runRandomly(ctx context.Context) error {
343347
return nil
344348
}
345349
}
350+
346351
r := rand.New(rand.NewSource(s.States.RandSeed))
347352
s.States.RandSeedUsed = true
348353
log.Info().Int64("seed", s.States.RandSeed).Msg("random source seeded")
354+
355+
// If NoRandomDuplicates is enabled, use bag-based selection
356+
if s.NoRandomDuplicates != nil && *s.NoRandomDuplicates {
357+
return s.runRandomlyWithoutDuplicates(ctx, r, continueExecution)
358+
}
359+
360+
// Original random execution logic (with duplicates allowed)
349361
randIndexUpperBound := len(s.Queries) + len(s.QueryFiles)
350362
for i := 1; continueExecution(i); i++ {
351363
idx := r.Intn(randIndexUpperBound)
@@ -377,6 +389,74 @@ func (s *Stage) runRandomly(ctx context.Context) error {
377389
return nil
378390
}
379391

392+
func (s *Stage) runRandomlyWithoutDuplicates(ctx context.Context, r *rand.Rand, continueExecution func(int) bool) error {
393+
// Create initial bag of all query indices
394+
totalQueries := len(s.Queries) + len(s.QueryFiles)
395+
if totalQueries == 0 {
396+
log.Info().Msg("no queries available for random execution")
397+
return nil
398+
}
399+
400+
// Initialize the bag with all available query indices
401+
var bag []int
402+
for i := 0; i < totalQueries; i++ {
403+
bag = append(bag, i)
404+
}
405+
406+
log.Info().Int("total_queries", totalQueries).Bool("no_duplicates", true).Msg("starting random execution without duplicates")
407+
408+
for i := 1; continueExecution(i); i++ {
409+
// If bag is empty, refill it (reset)
410+
if len(bag) == 0 {
411+
log.Info().Int("execution_count", i-1).Msg("bag exhausted, refilling for next round")
412+
for j := 0; j < totalQueries; j++ {
413+
bag = append(bag, j)
414+
}
415+
}
416+
417+
// Skip executions if needed
418+
if i <= s.States.RandSkip {
419+
if i == s.States.RandSkip {
420+
log.Info().Msgf("skipped %d random selections", i)
421+
}
422+
continue
423+
}
424+
425+
// Randomly select an index from the bag
426+
bagIndex := r.Intn(len(bag))
427+
selectedIdx := bag[bagIndex]
428+
429+
// Remove the selected index from the bag (no replacement)
430+
bag = append(bag[:bagIndex], bag[bagIndex+1:]...)
431+
432+
log.Debug().Int("selected_idx", selectedIdx).Int("remaining_in_bag", len(bag)).Msg("selected query from bag")
433+
434+
// Execute the selected query
435+
if selectedIdx < len(s.Queries) {
436+
// Run query embedded in the json file
437+
pseudoFileName := fmt.Sprintf("rand_%d", i)
438+
if err := s.runQueries(ctx, s.Queries[selectedIdx:selectedIdx+1], &pseudoFileName, 0); err != nil {
439+
return err
440+
}
441+
} else {
442+
// Run query from file
443+
queryFileIdx := selectedIdx - len(s.Queries)
444+
queryFile := s.QueryFiles[queryFileIdx]
445+
fileAlias := queryFile
446+
if relPath, relErr := filepath.Rel(s.BaseDir, queryFile); relErr == nil {
447+
fileAlias = relPath
448+
}
449+
fileAlias = fmt.Sprintf("rand_%d_%s", i, fileAlias)
450+
if err := s.runQueryFile(ctx, queryFile, nil, &fileAlias); err != nil {
451+
return err
452+
}
453+
}
454+
}
455+
456+
log.Info().Msg("random execution without duplicates concluded.")
457+
return nil
458+
}
459+
380460
func (s *Stage) runShellScripts(ctx context.Context, shellScripts []string) error {
381461
for i, script := range shellScripts {
382462
cmd := exec.CommandContext(ctx, "/bin/sh", "-c", script)

stage/stage_utils.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,9 @@ func (s *Stage) setDefaults() {
194194
if s.RandomExecution == nil {
195195
s.RandomExecution = &falseValue
196196
}
197+
if s.NoRandomDuplicates == nil {
198+
s.NoRandomDuplicates = &falseValue
199+
}
197200
if s.AbortOnError == nil {
198201
s.AbortOnError = &falseValue
199202
}
@@ -238,6 +241,9 @@ func (s *Stage) propagateStates() {
238241
if nextStage.RandomlyExecuteUntil == nil {
239242
nextStage.RandomlyExecuteUntil = s.RandomlyExecuteUntil
240243
}
244+
if nextStage.NoRandomDuplicates == nil {
245+
nextStage.NoRandomDuplicates = s.NoRandomDuplicates
246+
}
241247
for k, v := range s.SessionParams {
242248
if v == nil {
243249
continue

0 commit comments

Comments
 (0)