Repository: grpc/grpc-go
File: interop/soak_tests.go (Go)

Code before:
		fmt.Fprintf(os.Stderr, "soakIterations must be evenly divisible by soakNumWThreads\n")
	}
	startTime := time.Now()
	var wg sync.WaitGroup
	soakWorkerResults := make([]SoakWorkerResults, soakConfig.NumWorkers)
	for i := 0; i < soakConfig.NumWorkers; i++ {
		wg.Add(1)
		go func(workerID int) {
			defer wg.Done()
			executeSoakTestInWorker(ctx, soakConfig, startTime, workerID, &soakWorkerResults[workerID])
		}(i)
	}
	// Wait for all goroutines to complete.
	wg.Wait()

	//Handle results.
	totalSuccesses := 0
	totalFailures := 0
	latencies := stats.NewHistogram(stats.HistogramOptions{
		NumBuckets:     20,
		GrowthFactor:   1,
		BaseBucketSize: 1,
		MinValue:       0,
	})
	for _, worker := range soakWorkerResults {
		totalSuccesses += worker.IterationsDone
		totalFailures += worker.Failures
		if worker.Latencies != nil {
			// Add latencies from the worker's Histogram to the main latencies.
			latencies.Merge(worker.Latencies)
		}
	}
	var b bytes.Buffer
	totalIterations := totalSuccesses + totalFailures
	latencies.Print(&b)
	fmt.Fprintf(os.Stderr,
		"(server_uri: %s) soak test ran: %d / %d iterations. Total failures: %d. Latencies in milliseconds: %s\n",
		soakConfig.ServerAddr, totalSuccesses, soakConfig.Iterations, totalFailures, b.String())

	if totalIterations != soakConfig.Iterations {
		logger.Fatalf("Soak test consumed all %v of time and quit early, ran %d out of %d iterations.\n", soakConfig.OverallTimeout, totalSuccesses, soakConfig.Iterations)
	}

	if totalFailures > soakConfig.MaxFailures {
		logger.Fatalf("Soak test total failures: %d exceeded max failures threshold: %d\n", totalFailures, soakConfig.MaxFailures)
	}
	if soakConfig.ChannelForTest != nil {
		_, cleanup := soakConfig.ChannelForTest()
		defer cleanup()
	}
}

Review comment (bug):
Should we rename the `IterationsDone` field, if it's only counting successful iterations, to make this kind of bug more obvious?

Code after:
		fmt.Fprintf(os.Stderr, "soakIterations must be evenly divisible by soakNumWThreads\n")
	}
	startTime := time.Now()
	var wg sync.WaitGroup
	soakWorkerResults := make([]SoakWorkerResults, soakConfig.NumWorkers)
	for i := 0; i < soakConfig.NumWorkers; i++ {
		wg.Add(1)
		go func(workerID int) {
			defer wg.Done()
			executeSoakTestInWorker(ctx, soakConfig, startTime, workerID, &soakWorkerResults[workerID])
		}(i)
	}
	// Wait for all goroutines to complete.
	wg.Wait()

	// Handle results.
	totalSuccesses := 0
	totalFailures := 0
	latencies := stats.NewHistogram(stats.HistogramOptions{
		NumBuckets:     20,
		GrowthFactor:   1,
		BaseBucketSize: 1,
		MinValue:       0,
	})
	for _, worker := range soakWorkerResults {
		totalSuccesses += worker.iterationsSucceeded
		totalFailures += worker.Failures
		if worker.Latencies != nil {
			// Add latencies from the worker's Histogram to the main latencies.
			latencies.Merge(worker.Latencies)
		}
	}
	var b bytes.Buffer
	totalIterations := totalSuccesses + totalFailures
	latencies.Print(&b)
	fmt.Fprintf(os.Stderr,
		"(server_uri: %s) soak test successes: %d / %d iterations. Total failures: %d. Latencies in milliseconds: %s\n",
		soakConfig.ServerAddr, totalSuccesses, soakConfig.Iterations, totalFailures, b.String())

	if totalIterations != soakConfig.Iterations {
		logger.Fatalf("Soak test consumed all %v of time and quit early, ran %d out of %d iterations.\n", soakConfig.OverallTimeout, totalIterations, soakConfig.Iterations)
	}

	if totalFailures > soakConfig.MaxFailures {
		logger.Fatalf("Soak test total failures: %d exceeded max failures threshold: %d\n", totalFailures, soakConfig.MaxFailures)
	}
	if soakConfig.ChannelForTest != nil {
		_, cleanup := soakConfig.ChannelForTest()
		defer cleanup()
	}
}