Skip to content

Conversation

@vigodsky
Copy link
Contributor

Hello,

Some data race fixes (lastResponse shared between ping handler and ticker goroutine)

@vigodsky vigodsky marked this pull request as ready for review October 27, 2025 15:53
@carlosmiei
Copy link
Collaborator

@vigodsky Thanks, we will review it soon

@carlosmiei carlosmiei requested a review from Copilot October 27, 2025 17:27
Copy link
Contributor

Copilot AI left a 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.Time with int64 (Unix nanoseconds) for the lastResponse variable
  • 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()
Copy link

Copilot AI Oct 27, 2025

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.

Suggested change
var lastResponse int64 = time.Now().UnixNano()
lastResponse := time.Now().UnixNano()

Copilot uses AI. Check for mistakes.
ticker := time.NewTicker(timeout)

lastResponse := time.Now()
var lastResponse int64 = time.Now().UnixNano()
Copy link

Copilot AI Oct 27, 2025

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.

Suggested change
var lastResponse int64 = time.Now().UnixNano()
lastResponse := time.Now().UnixNano()

Copilot uses AI. Check for mistakes.
ticker := time.NewTicker(timeout)

lastResponse := time.Now()
var lastResponse int64 = time.Now().UnixNano()
Copy link

Copilot AI Oct 27, 2025

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.

Suggested change
var lastResponse int64 = time.Now().UnixNano()
lastResponse := time.Now().UnixNano()

Copilot uses AI. Check for mistakes.
ticker := time.NewTicker(timeout)

lastResponse := time.Now()
var lastResponse int64 = time.Now().UnixNano()
Copy link

Copilot AI Oct 27, 2025

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.

Suggested change
var lastResponse int64 = time.Now().UnixNano()
lastResponse := time.Now().UnixNano()

Copilot uses AI. Check for mistakes.
ticker := time.NewTicker(timeout)

lastResponse := time.Now()
var lastResponse int64 = time.Now().UnixNano()
Copy link

Copilot AI Oct 27, 2025

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.

Suggested change
var lastResponse int64 = time.Now().UnixNano()
lastResponse := time.Now().UnixNano()

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we keep lastResponse := ...?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

Copy link
Contributor

@sc0Vu sc0Vu left a 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?

@vigodsky
Copy link
Contributor Author

@sc0Vu

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.

Copy link
Contributor

@sc0Vu sc0Vu left a comment

Choose a reason for hiding this comment

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

LGTM

@sc0Vu
Copy link
Contributor

sc0Vu commented Oct 29, 2025

@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.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants