Skip to content
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

[Access] Properly handle subscription errors in data providers #7046

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

illia-malachyn
Copy link
Contributor

@illia-malachyn illia-malachyn commented Feb 17, 2025

Distinguish between context.Canceled errors originating from the streamer and those triggered by the DataProvider’s Close() method.

  1. I Added closeChan that is used in DataProvider.Close() method to indicate DataProvider.Run() that users of data providers (WebSocket controller in our case) want to finish receiving data.
  2. HandleSubscription() function is replaced by run() function that is aware of closeChan. I made a new function for it because HandleSubscription() is widely used in the access package (HandleRPCSubscription has 22 usages atm).
  3. run() returns nil if closeChan was closed. HandleSubscription returned ctx.Canceled which lead to confusion as ctx.Canceled could come from 2 sources (streamer and websocket controller).
  4. I did a little refactoring to a bunch of sendResponse() functions to make it more readable.

Closes #7040 #7047

Distinguish between `context.Canceled` errors originating from the
streamer and those triggered by the DataProvider’s `Close()` method.
Use `wasClosedByClient()` to suppress expected cancellations while
propagating unexpected ones
@codecov-commenter
Copy link

codecov-commenter commented Feb 17, 2025

Codecov Report

Attention: Patch coverage is 87.50000% with 18 lines in your changes missing coverage. Please review.

Project coverage is 41.24%. Comparing base (bd39edc) to head (feff7e5).

Files with missing lines Patch % Lines
...ss/rest/websockets/data_providers/base_provider.go 54.16% 8 Missing and 3 partials ⚠️
...ockets/data_providers/account_statuses_provider.go 90.32% 2 Missing and 1 partial ⚠️
.../rest/websockets/data_providers/events_provider.go 88.46% 2 Missing and 1 partial ⚠️
.../rest/websockets/data_providers/blocks_provider.go 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #7046      +/-   ##
==========================================
+ Coverage   41.23%   41.24%   +0.01%     
==========================================
  Files        2138     2138              
  Lines      188585   188622      +37     
==========================================
+ Hits        77764    77799      +35     
- Misses     104327   104329       +2     
  Partials     6494     6494              
Flag Coverage Δ
unittests 41.24% <87.50%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Guitarheroua
Guitarheroua previously approved these changes Feb 17, 2025
Copy link
Contributor

@Guitarheroua Guitarheroua left a comment

Choose a reason for hiding this comment

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

Looks good to me!

@illia-malachyn
Copy link
Contributor Author

illia-malachyn commented Feb 17, 2025

After discussing with Yurii, we agreed to use a different approach here.

@illia-malachyn illia-malachyn marked this pull request as draft February 17, 2025 15:02
@illia-malachyn illia-malachyn changed the title Properly handle subscription errors in data providers [DRAFT] [Access] Properly handle subscription errors in data providers Feb 17, 2025
@Guitarheroua Guitarheroua dismissed their stale review February 18, 2025 13:43

As this will changed, will re-aprove final implementation

We use it to distinguish place where cxt.Canceled
error comes from.

Also, I refactored each data provider's Run()
function. Now it's more readable and clear.
@illia-malachyn illia-malachyn marked this pull request as ready for review February 18, 2025 18:15
@illia-malachyn illia-malachyn changed the title [DRAFT] [Access] Properly handle subscription errors in data providers [Access] Properly handle subscription errors in data providers Feb 19, 2025
Copy link
Contributor

@peterargue peterargue left a comment

Choose a reason for hiding this comment

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

why is this refactor needed? can you point out the important changes, it's not clear to me

@illia-malachyn
Copy link
Contributor Author

why is this refactor needed? can you point out the important changes, it's not clear to me

Hey. I updated PR's description. Added a context of what has been done and why.

// The block counter increments until either:
// 1. The account emits events
// 2. The heartbeat interval is reached
*blocksSinceLastMessage += 1
Copy link
Contributor Author

Choose a reason for hiding this comment

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

all places with blockSinceLastMessage were refactored

Copy link
Contributor

Choose a reason for hiding this comment

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

Your refactor variant should work as expected, I would just add to comment that:

// Only send a response if there's meaningful data to send or the heartbeat interval limit is reached

}

var accountStatusesPayload models.AccountStatusesResponse
defer messageIndex.Increment()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

all places with messageIndex increment were refactored

@@ -15,6 +17,9 @@ type baseDataProvider struct {
cancel context.CancelFunc
send chan<- interface{}
subscription subscription.Subscription
// Ensures the closedChan has been closed once.
closedFlag sync.Once
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added closedChan channel

) error {
for {
select {
case <-closedChan:
Copy link
Contributor Author

Choose a reason for hiding this comment

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

run() is aware of closedChan and returns nil if it was closed

@illia-malachyn
Copy link
Contributor Author

@peterargue I also pointed out the most important lines of code

@@ -33,6 +38,8 @@ func newBaseDataProvider(
cancel: cancel,
send: send,
subscription: subscription,
closedFlag: sync.Once{},
closedChan: make(chan struct{}, 1),
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 use unbuffered channel here, as it only needed for closing, we do not write to it anywhere?


type sendResponseCallback[T any] func(T) error

func run[T any](
Copy link
Contributor

Choose a reason for hiding this comment

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

It is self-explentatory, but I would add the function comment anyway.

p.subscription,
subscription.HandleResponse(p.send, func(b *flow.BlockDigest) (interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this subscription.HandleRespons used anywhere? If not, I think we could remove it.

Copy link
Contributor

@Guitarheroua Guitarheroua left a comment

Choose a reason for hiding this comment

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

The first round of review - DONE! I have a few small comments.

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.

[Access] Data providers should wrap context.Canceled error
4 participants