-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor(serverv2): remove unused interface methods, honuor context #22394
Changes from all commits
82ad066
809743b
5880dea
50823c0
6fa5135
12dea2f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package api | ||
|
||
import "context" | ||
|
||
// DoUntilCtxExpired runs the given function until the context is expired or | ||
// the function exits. | ||
// This forces context to be honored. | ||
func DoUntilCtxExpired(ctx context.Context, f func()) error { | ||
done := make(chan struct{}) | ||
go func() { | ||
defer close(done) | ||
|
||
f() | ||
}() | ||
Comment on lines
+10
to
+14
Check notice Code scanning / CodeQL Spawning a Go routine Note
Spawning a Go routine may be a possible source of non-determinism
|
||
|
||
select { | ||
case <-ctx.Done(): | ||
return ctx.Err() | ||
case <-done: | ||
return nil | ||
} | ||
} | ||
Comment on lines
+8
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix potential goroutine leak on context cancellation The current implementation has a potential goroutine leak. When the context is cancelled, the goroutine continues running without any way to stop it. This could lead to resource leaks in long-running applications. Consider adding a way to signal the goroutine to stop: func DoUntilCtxExpired(ctx context.Context, f func()) error {
done := make(chan struct{})
+ // Create a child context that we can cancel
+ runCtx, cancel := context.WithCancel(ctx)
+ defer cancel()
+
go func() {
defer close(done)
-
- f()
+ // Pass the cancellable context to the function
+ go func() {
+ f()
+ cancel()
+ }()
+
+ // Wait for either completion or cancellation
+ <-runCtx.Done()
}()
select {
case <-ctx.Done():
+ // Cancel the running function
+ cancel()
return ctx.Err()
case <-done:
return nil
}
}
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,35 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package api | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"context" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"testing" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"time" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"github.com/stretchr/testify/require" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func TestDoUntilCtxExpired(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("success", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ctx := context.Background() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
funcRan := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
err := DoUntilCtxExpired(ctx, func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
funcRan = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
require.NoError(t, err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
require.True(t, funcRan) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+12
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider enhancing test robustness. While the test correctly verifies the basic functionality, consider these improvements:
t.Run("success", func(t *testing.T) {
- ctx := context.Background()
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+
+ start := time.Now()
funcRan := false
err := DoUntilCtxExpired(ctx, func() {
funcRan = true
})
require.NoError(t, err)
require.True(t, funcRan)
+ require.Less(t, time.Since(start), 5*time.Second)
}) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
t.Run("context expired", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ctx, cancel := context.WithCancel(context.Background()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
funcRan := false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
err := DoUntilCtxExpired(ctx, func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cancel() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
funcRan = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<-time.After(time.Second) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
require.ErrorIs(t, err, context.Canceled) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
require.True(t, funcRan) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+23
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix potential test flakiness and goroutine leak. The current implementation has several issues:
t.Run("context expired", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ waited := make(chan struct{})
funcRan := false
err := DoUntilCtxExpired(ctx, func() {
cancel()
funcRan = true
- <-time.After(time.Second)
+ close(waited)
+ <-ctx.Done()
})
+ <-waited
require.ErrorIs(t, err, context.Canceled)
require.True(t, funcRan)
}) 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Consider adding a configurable shutdown timeout.
While using
DoUntilCtxExpired
withGracefulStop
is good for context-aware shutdown, consider adding a configurable timeout in the server config to prevent potentially long shutdown times with hanging connections.Add a timeout configuration to the Config struct:
Then use it in the Stop method: