-
Notifications
You must be signed in to change notification settings - Fork 767
fix: keepalive data race #769
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
base: master
Are you sure you want to change the base?
Conversation
|
@vigodsky Thanks, we will review it soon |
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.
Pull Request Overview
This PR fixes a data race condition in websocket keepalive functionality by making the lastResponse timestamp thread-safe using atomic operations. The lastResponse variable was being concurrently accessed by both the ping handler and ticker goroutine without synchronization.
- Replaced
time.Timewithint64(Unix nanoseconds) for thelastResponsevariable - Added atomic operations for reading and writing the timestamp
- Applied the same fix consistently across all websocket implementations
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| v2/websocket.go | Fixed data race in keepAlive by using atomic operations for lastResponse timestamp |
| v2/portfolio/websocket.go | Applied identical data race fix to portfolio package keepAlive function |
| v2/options/websocket.go | Applied identical data race fix to options package keepAlive function |
| v2/futures/websocket.go | Applied identical data race fix to futures package keepAlive function |
| v2/delivery/websocket.go | Applied identical data race fix to delivery package keepAlive function |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
v2/websocket.go
Outdated
| ticker := time.NewTicker(timeout) | ||
|
|
||
| lastResponse := time.Now() | ||
| var lastResponse int64 = time.Now().UnixNano() |
Copilot
AI
Oct 27, 2025
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.
[nitpick] The explicit type declaration is unnecessary. Consider using short variable declaration: lastResponse := time.Now().UnixNano() for cleaner code.
| var lastResponse int64 = time.Now().UnixNano() | |
| lastResponse := time.Now().UnixNano() |
v2/portfolio/websocket.go
Outdated
| ticker := time.NewTicker(timeout) | ||
|
|
||
| lastResponse := time.Now() | ||
| var lastResponse int64 = time.Now().UnixNano() |
Copilot
AI
Oct 27, 2025
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.
[nitpick] The explicit type declaration is unnecessary. Consider using short variable declaration: lastResponse := time.Now().UnixNano() for cleaner code.
| var lastResponse int64 = time.Now().UnixNano() | |
| lastResponse := time.Now().UnixNano() |
v2/options/websocket.go
Outdated
| ticker := time.NewTicker(timeout) | ||
|
|
||
| lastResponse := time.Now() | ||
| var lastResponse int64 = time.Now().UnixNano() |
Copilot
AI
Oct 27, 2025
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.
[nitpick] The explicit type declaration is unnecessary. Consider using short variable declaration: lastResponse := time.Now().UnixNano() for cleaner code.
| var lastResponse int64 = time.Now().UnixNano() | |
| lastResponse := time.Now().UnixNano() |
v2/futures/websocket.go
Outdated
| ticker := time.NewTicker(timeout) | ||
|
|
||
| lastResponse := time.Now() | ||
| var lastResponse int64 = time.Now().UnixNano() |
Copilot
AI
Oct 27, 2025
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.
[nitpick] The explicit type declaration is unnecessary. Consider using short variable declaration: lastResponse := time.Now().UnixNano() for cleaner code.
| var lastResponse int64 = time.Now().UnixNano() | |
| lastResponse := time.Now().UnixNano() |
v2/delivery/websocket.go
Outdated
| ticker := time.NewTicker(timeout) | ||
|
|
||
| lastResponse := time.Now() | ||
| var lastResponse int64 = time.Now().UnixNano() |
Copilot
AI
Oct 27, 2025
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.
[nitpick] The explicit type declaration is unnecessary. Consider using short variable declaration: lastResponse := time.Now().UnixNano() for cleaner code.
| var lastResponse int64 = time.Now().UnixNano() | |
| lastResponse := time.Now().UnixNano() |
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.
Should we keep lastResponse := ...?
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.
fixed
sc0Vu
left a comment
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.
There is receiving loop that call ReceiveMessage() actively. I wonder if we can replace lastResponse with SetReadDeadline(). How do you think?
|
Looks good overall. One caveat: if the read loop gets blocked (i.e., we’re not inside ReadMessage/NextReader), we won’t observe the SetReadDeadline, so the timeout won’t trigger. Keeping the read pump non-blocking (or adding a thin watchdog to close on no progress) would cover that edge case. I would suggest to keep ticker goroutine. |
sc0Vu
left a comment
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.
LGTM
|
@vigodsky We can keep ticker goroutine now. For SetReadDeadline, I wonder if it's possible to do that in PongHandler, I saw gorilla and other project did that. They don't use ticker goroutine. |
Hello,
Some data race fixes (lastResponse shared between ping handler and ticker goroutine)