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

fix: http unsubscribe #98

Merged
merged 2 commits into from
Nov 28, 2024
Merged

fix: http unsubscribe #98

merged 2 commits into from
Nov 28, 2024

Conversation

marcinczenko
Copy link
Contributor

@marcinczenko marcinczenko commented Nov 27, 2024

Unsubscribing from a non-existent subscription as well as duplicate unsubscribe should have no effect (harmless).

@marcinczenko marcinczenko self-assigned this Nov 27, 2024
Copy link
Contributor

@emizzle emizzle left a comment

Choose a reason for hiding this comment

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

Good find!

Comment on lines 255 to 267
if subscriptions.subscriptionMapping.hasKey(id):
let sub = subscriptions.subscriptionMapping[id]
subscriptions.subscriptionMapping.del(id)
try:
discard await subscriptions.client.eth_uninstallFilter(sub)
except CancelledError as e:
raise e
except CatchableError:
# Ignore if uninstallation of the filter fails. If it's the last step in our
# cleanup, then filter changes for this filter will no longer be polled so
# if the filter continues to live on in geth for whatever reason then it
# doesn't matter.
discard
Copy link
Contributor

Choose a reason for hiding this comment

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

Another option is to move the try two lines up, since except CatchableError will catch KeyError. But the way you have it is also fine.

The main advantage of doing it this way is that they compiler will not think that KeyError can be leaked out from this proc (even though it shouldn't based on the hasKey usage). To go one step further, we could (and probably should) annotate the proc with {.async: (raises: [CancelledError]).} to indicate that no other exceptions, except CancelledError, could potentially be emitted by the proc.

Copy link
Member

Choose a reason for hiding this comment

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

You could also use this construct from questionable to deal with the KeyError:

  if sub =? subscriptions.subscriptionMapping.?[id]:

Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, so .? works even without Options or Results?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am still making mistakes when applying "questionables", but I like the conciseness of Mark's proposal. I will update it.

Copy link
Contributor Author

@marcinczenko marcinczenko Nov 28, 2024

Choose a reason for hiding this comment

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

Adding raises btw is also good thing to do I suppose. Perhaps we should do it always where proc is handling exceptions... But here it makes things more complicated. We need to modify signature of the base method, and the override for web sockets does not do any exceptions handling so it also throws CatchableError. Thus... perhaps we should not add raises to unsubscribe.

Copy link
Contributor Author

@marcinczenko marcinczenko Nov 28, 2024

Choose a reason for hiding this comment

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

I actually aways like in short methods like this to have try/catch on top-level. If we move try to the top, then the KeyError would be cough even without using the conditional. For the readability reasons I would still use the conditional.

Copy link
Member

Choose a reason for hiding this comment

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

Interesting, so .? works even without Options or Results?

The .?[] syntax is a special syntax for indexing strings, arrays and tables: https://github.com/codex-storage/questionable/blob/main/questionable/indexing.nim

Copy link
Contributor

Choose a reason for hiding this comment

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

Adding raises btw is also good thing to do I suppose. Perhaps we should do it always where proc is handling exceptions... But here it makes things more complicated. We need to modify signature of the base method, and the override for web sockets does not do any exceptions handling so it also throws CatchableError. Thus... perhaps we should not add raises to unsubscribe.

It's a good point. And also makes me think that we should be handling errors in WebSocketSubscriptions.unsubscribe the same way as we are here in PollingSubscriptions.unsubscribe (explicitly raising only CancelledError), but could do that in a different PR at some point later, so we can keep this changeset small.

Copy link
Contributor Author

@marcinczenko marcinczenko Nov 29, 2024

Choose a reason for hiding this comment

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

Yes, when we see it is best to record it. I will create a draft for this later.

So the difference between the two "unsubscribes" is the following:

# websockets
method unsubscribe*(subscriptions: WebSocketSubscriptions,
                   id: JsonNode)
                  {.async.} =
  subscriptions.callbacks.del(id)
  discard await subscriptions.client.eth_unsubscribe(id)
#http
method unsubscribe*(subscriptions: PollingSubscriptions,
                   id: JsonNode)
                  {.async.} =
  try:
    subscriptions.logFilters.del(id)
    subscriptions.callbacks.del(id)
    if sub =? subscriptions.subscriptionMapping.?[id]:
      subscriptions.subscriptionMapping.del(id)
      discard await subscriptions.client.eth_uninstallFilter(sub)
  except CancelledError as e:
    raise e
  except CatchableError:
    # Ignore if uninstallation of the filter fails. If it's the last step in our
    # cleanup, then filter changes for this filter will no longer be polled so
    # if the filter continues to live on in geth for whatever reason then it
    # doesn't matter.
    discard

So we have two different RPC calls: eth_unsubscribe for websockets and eth_uninstallFilter for http.
It is kind of tricky to get a good picture of what exceptions those two calls are throwing. Is it by any chance implementation dependent? Like depending which RPC provider or chain we are using? The docs are not really aligned about that...

I will look further to see if it is possible to find some common ground.
In general though, it is preferred that things like unsubscribe should not really throw anything. @markspanbroek see the CancelledError for http as a tolerable exception to that rule.

What is your opinion about it?

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it by any chance implementation dependent? Like depending which RPC provider or chain we are using?

I'm not sure, honestly. However, as you mentioned, regardless of what kind of error response is returned, we don't really need to re-raise or handle these errors for unsubscriptions. We should only re-raise CancelledError so that we can bubble cancellations.

Copy link
Member

@markspanbroek markspanbroek left a comment

Choose a reason for hiding this comment

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

Thanks @marcinczenko, looks good to me

@marcinczenko marcinczenko merged commit ba00602 into main Nov 28, 2024
6 checks passed
@marcinczenko marcinczenko deleted the fix/http-unsubscribe branch November 28, 2024 13:53
@marcinczenko marcinczenko restored the fix/http-unsubscribe branch November 28, 2024 14:14
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