Skip to content

Conversation

wdsjk
Copy link

@wdsjk wdsjk commented Sep 27, 2025

Fixes: #8596

Added cancellation by context in benchmark/client instead of custom channel approach. Not sure about returning ctx.Err() (as mentioned in the original issue under point 4), so please review

RELEASE NOTES: None

Copy link

linux-foundation-easycla bot commented Sep 27, 2025

CLA Signed

The committers listed above are authorized under a signed CLA.

Copy link

codecov bot commented Sep 27, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.39%. Comparing base (8110884) to head (7161064).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8614      +/-   ##
==========================================
- Coverage   79.45%   79.39%   -0.06%     
==========================================
  Files         415      415              
  Lines       41339    41354      +15     
==========================================
- Hits        32844    32833      -11     
- Misses       6621     6622       +1     
- Partials     1874     1899      +25     

see 24 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@wdsjk wdsjk marked this pull request as draft September 27, 2025 17:22
@wdsjk wdsjk marked this pull request as ready for review September 27, 2025 17:30
@wdsjk
Copy link
Author

wdsjk commented Sep 27, 2025

Could you please add "Type: Internal Cleanup" label? Thanks

@Pranjali-2501 Pranjali-2501 added the Type: Internal Cleanup Refactors, etc label Sep 30, 2025
@Pranjali-2501 Pranjali-2501 self-requested a review September 30, 2025 06:53
@Pranjali-2501 Pranjali-2501 self-assigned this Sep 30, 2025
@Pranjali-2501 Pranjali-2501 added this to the 1.77 Release milestone Sep 30, 2025
select {
case <-bc.stop:
if bc.ctx.Err() != nil {
return
Copy link
Contributor

@Pranjali-2501 Pranjali-2501 Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change it to return bc.ctx.Err() and change function unaryLoop to return error?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we can, but error is happening inside a goroutine and I'm not quite sure how to implement handling. Could you suggest an approach?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arjan-bal arjan-bal assigned arjan-bal and unassigned wdsjk Oct 7, 2025
Copy link
Contributor

@arjan-bal arjan-bal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies for the delayed review and thanks for the contribution!

A few main points:

  1. We should pass the context though function params.
  2. We should try to derive contexts from other contexts as much as possible instead of using context.Background.
  3. We can avoid a lot of the ctx.Err() checks by letting RPCs fail and handling the case when the error had a status of CANCELLED.

lockingHistograms: make([]lockingHistogram, rpcCountPerConn*len(conns)),

stop: make(chan bool),
ctx: ctx,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we pass the context as an argument here instead of storing it in the struct? The standard practice in Go is to pass context.Context as the first parameter, which helps make cancellation and deadlines clearer across API boundaries. The Go team wrote a post about it here: https://go.dev/blog/context-and-structs

}

// DoUnaryCallWithContext performs a unary RPC with propagated context and given stub and request and response sizes.
func DoUnaryCallWithContext(ctx context.Context, tc testgrpc.BenchmarkServiceClient, reqSize, respSize int) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can change the existing DoUnaryCall function to accept a context. Since the benchmarking client is a binary (not a library), we don't need to be concerned too much about maintaining existing function signatures.

return nil
}

func startBenchmarkClient(config *testpb.ClientConfig) (*benchmarkClient, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change the startBenchmarkClient to accept a context? We can create the context in main.go we can derive a new context from the the stream context:

bc, err = startBenchmarkClient(t.Setup)

// Using the stream context instead of context.Background()
ctx, cancel := context.WithCancel(stream.Context())

When the client is to be closed, the context will be cancelled just before that.

defer func() {
// Shut down benchmark client when stream ends.
logger.Infof("shutting down benchmark client")
if bc != nil {
bc.shutdown()
}
}()

start := time.Now()
if err := benchmark.DoUnaryCall(client, reqSize, respSize); err != nil {
if err := benchmark.DoUnaryCallWithContext(bc.ctx, client, reqSize, respSize); err != nil {
if status.Code(err) == codes.Canceled || errors.Is(err, context.Canceled) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need the second part of the condition (|| errors.Is(err, context.Canceled). gRPC should always be returning an error which can be converted to a gRPC status. Please do let me know if this is not the case, it may be a bug.

Comment on lines 406 to 408
if bc.ctx.Err() != nil {
return
}
Copy link
Contributor

@arjan-bal arjan-bal Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid the context check since the stream context will be cancelled when the client is being closed, automatically failing the stream.

Comment on lines 394 to 396
if bc.ctx.Err() != nil {
return
}
Copy link
Contributor

@arjan-bal arjan-bal Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can avoid the context check since the stream context will be cancelled when the client is being closed, automatically failing the stream.

Comment on lines 383 to 385
if bc.ctx.Err() != nil {
return
}
Copy link
Contributor

@arjan-bal arjan-bal Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we can avoid the context check since the stream context will be cancelled when the client is being closed, automatically failing the stream.

Comment on lines 371 to 373
if bc.ctx.Err() != nil {
return
}
Copy link
Contributor

@arjan-bal arjan-bal Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we can avoid the context check since the stream context will be cancelled when the client is being closed, automatically failing the stream.

Comment on lines 357 to 359
if bc.ctx.Err() != nil {
return
}
Copy link
Contributor

@arjan-bal arjan-bal Oct 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we can avoid the context check since the stream context will be cancelled when the client is being closed, automatically failing the stream.

@arjan-bal arjan-bal assigned wdsjk and unassigned arjan-bal Oct 10, 2025
@wdsjk
Copy link
Author

wdsjk commented Oct 10, 2025

Thank you very much for the review, it is very helpful!

Comment on lines 312 to 319
bc.lockingHistograms[idx].histogram = stats.NewHistogram(bc.histogramOptions)
if poissonLambda == nil { // Closed loop.
if stream.Context().Err() != nil {
return
}
// Start goroutine on the created mutex and histogram.
go func(idx int) {
// TODO: do warm up if necessary.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You said

We usually do the check just before performing an asynchronous operation

so I decided to do it like that. If it is not the appropriate way, please tell me

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

benchmark/client: Use context for cancellation

3 participants