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

Code before:

func doOneSoakIteration(ctx context.Context, config SoakIterationConfig) (latency time.Duration, err error) {
	start := time.Now()
	// Do a large-unary RPC.
	// Create the request payload.
	pl := ClientNewPayload(testpb.PayloadType_COMPRESSABLE, config.RequestSize)
	req := &testpb.SimpleRequest{
		ResponseType: testpb.PayloadType_COMPRESSABLE,
		ResponseSize: int32(config.ResponseSize),
		Payload:      pl,
	}
	// Perform the GRPC call.
	var reply *testpb.SimpleResponse
	reply, err = config.Client.UnaryCall(ctx, req, config.CallOptions...)
	if err != nil {
		err = fmt.Errorf("/TestService/UnaryCall RPC failed: %s", err)
		return 0, err
	}
	// Validate response.
	t := reply.GetPayload().GetType()
	s := len(reply.GetPayload().GetBody())
	if t != testpb.PayloadType_COMPRESSABLE || s != config.ResponseSize {
		err = fmt.Errorf("got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, config.ResponseSize)
		return 0, err
	}
	// Calculate latency and return result.
	latency = time.Since(start)
	return latency, nil
}


func executeSoakTestInWorker(ctx context.Context, config SoakTestConfig, startTime time.Time, workerID int, soakWorkerResults *SoakWorkerResults) {
	timeoutDuration := config.OverallTimeout
	soakIterationsPerWorker := config.Iterations / config.NumWorkers
	if soakWorkerResults.Latencies == nil {
		soakWorkerResults.Latencies = stats.NewHistogram(stats.HistogramOptions{
			NumBuckets:     20,
			GrowthFactor:   1,
			BaseBucketSize: 1,
			MinValue:       0,
		})
	}


	for i := 0; i < soakIterationsPerWorker; i++ {
		if ctx.Err() != nil {
			return
		}
		if time.Since(startTime) >= timeoutDuration {
			fmt.Printf("Test exceeded overall timeout of %v, stopping...\n", config.OverallTimeout)
			return

Review comment (suggestion):
The root cause of the previous seg issue was that the Latencies field in SoakWorkerResults was not initialized, leading to a nil pointer dereference when attempting to call .Add(). Thus, to fix the issue,  in the executeSoakTestInWorker function, we initialize the Latencies histogram before using it.

Code after:
		ResponseSize: int32(config.ResponseSize),
		Payload:      pl,
	}
	// Perform the GRPC call.
	var reply *testpb.SimpleResponse
	reply, err = config.Client.UnaryCall(ctx, req, config.CallOptions...)
	if err != nil {
		err = fmt.Errorf("/TestService/UnaryCall RPC failed: %s", err)
		return 0, err
	}
	// Validate response.
	t := reply.GetPayload().GetType()
	s := len(reply.GetPayload().GetBody())
	if t != testpb.PayloadType_COMPRESSABLE || s != config.ResponseSize {
		err = fmt.Errorf("got the reply with type %d len %d; want %d, %d", t, s, testpb.PayloadType_COMPRESSABLE, config.ResponseSize)
		return 0, err
	}
	// Calculate latency and return result.
	latency = time.Since(start)
	return latency, nil
}

func executeSoakTestInWorker(ctx context.Context, config SoakTestConfig, startTime time.Time, workerID int, soakWorkerResults *SoakWorkerResults) {
	timeoutDuration := config.OverallTimeout
	soakIterationsPerWorker := config.Iterations / config.NumWorkers
	if soakWorkerResults.Latencies == nil {
		soakWorkerResults.Latencies = stats.NewHistogram(stats.HistogramOptions{
			NumBuckets:     20,
			GrowthFactor:   1,
			BaseBucketSize: 1,
			MinValue:       0,
		})
	}

	for i := 0; i < soakIterationsPerWorker; i++ {
		if ctx.Err() != nil {
			return
		}
		if time.Since(startTime) >= timeoutDuration {
			fmt.Printf("Test exceeded overall timeout of %v, stopping...\n", config.OverallTimeout)
			return
		}
		earliestNextStart := time.After(config.MinTimeBetweenRPCs)
		currentChannel, cleanup := config.ChannelForTest()
		defer cleanup()
		client := testgrpc.NewTestServiceClient(currentChannel)
		var p peer.Peer
		iterationConfig := SoakIterationConfig{
			RequestSize:  config.RequestSize,
			ResponseSize: config.ResponseSize,
			Client:       client,