Skip to content
This repository has been archived by the owner on Nov 27, 2022. It is now read-only.

UpdateTracter to reconnect and and re-subscribe on close events #30

Merged

Conversation

jholleran
Copy link
Contributor

Resolves: #29

Kind of change

  • Bugfix
  • Feature
  • Code style update
  • Refactor
  • Build-related changes
  • Other, please describe:

Proposed changes

Resubscribing to resources when Websocket connection closes. It will make 6 retry attempts with a exponential back-off time (1, 2, 4, 8, 16, 32 seconds) in between each attempt.

Also, as part of this PR it fixes an issue when unsubscribing from resources it will now allow the same resources to be fully subscribed again.

Relevant issues

Breaking change?

  • Yes
  • No

Requirements check

  • All tests are passing
  • New/updated tests are included

Additional info

First basic implementation of re-subscribing to resources when connection closes

resubscribe with five attempts with 1 second backoff

now with exponential backoff

removed console log messages

code style issues removed

Increased to 6 attempts, also reset backoff times if connection succeeded

Code style issue removed
@coveralls
Copy link

coveralls commented Mar 16, 2020

Pull Request Test Coverage Report for Build 163

  • 19 of 19 (100.0%) changed or added relevant lines in 1 file are covered.
  • No unchanged relevant lines lost coverage.
  • Overall coverage remained the same at 100.0%

Totals Coverage Status
Change from base Build 152: 0.0%
Covered Lines: 291
Relevant Lines: 291

💛 - Coveralls

@RubenVerborgh RubenVerborgh self-assigned this Mar 16, 2020
@RubenVerborgh RubenVerborgh added the enhancement New feature or request label Mar 16, 2020
subscribers[url].delete(this.subscriber);
delete subscribers[url];
delete webSockets[new URL(url).host];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is an issue here if there are multiple subscribed resources under the same domain. I'll change this to only delete if its the last resource with the domain.

Copy link
Owner

@RubenVerborgh RubenVerborgh left a comment

Choose a reason for hiding this comment

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

Hi @jholleran, thanks for the effort on this, especially the detailed unit tests.
The overall picture seems good, but I have some concerns here and there, which I have indicated with comments.

.gitignore Outdated
@@ -3,3 +3,5 @@ dist
lib
module
node_modules
.idea/
*.iml
Copy link
Owner

Choose a reason for hiding this comment

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

Would suggest to keep those in a personal .gitignore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reverted this change.

subscribers[url].delete(this.subscriber);
delete subscribers[url];
Copy link
Owner

Choose a reason for hiding this comment

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

The above will cause issues when there are multiple UpdateTracker instances. The idea is that subscribers[url] is shared across all UpdateTracker instances, representing a set of all subscribed UpdateTrackers. When unsubcribe is on a single instance, it should subscribe only that instance.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Reverted this change.

subscribers[url].delete(this.subscriber);
delete subscribers[url];
delete webSockets[new URL(url).host];
Copy link
Owner

Choose a reason for hiding this comment

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

This seems to cause issues when an UpdateTracker is subscribed to two URLs from the same host, and we are only unsubscribing from one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah I agree this was incorrect and I've reverted it.

@@ -57,11 +60,17 @@ function trackResource(url) {
if (!webSocket) {
const socketUrl = `${protocol.replace('http', 'ws')}//${host}/`;
webSockets[host] = webSocket = new WebSocket(socketUrl);
const backOffDelay = delay || 1000;
Copy link
Owner

Choose a reason for hiding this comment

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

Should we just call the function parameter backOffDelay instead of delay.
We can also initialize it as trackResource(url, retryAttempt = 0, backOffDelay = 1000).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I've moved the default initialization to the setBackoff function as trackResource is called in a number of places already.

}

webSocket.resources.push(url); // Storing URL of resouce that will be subscribed to
Copy link
Owner

Choose a reason for hiding this comment

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

Typo; can we put the comment above the line, and perhaps clarify that we keep track of resources so we can resubscribe if needed?

Also, might want to make webSocket.resources a Set to avoid multiple subscriptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Object.assign(webSocket, { enqueue, onmessage,
ready: new Promise(resolve => (webSocket.onopen = resolve)),
onclose: oncloseFor(host),
resources: [],
Copy link
Owner

Choose a reason for hiding this comment

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

Might want to make resources a Set to avoid multiple re-subscriptions.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

function setUpBackOff(webSocket, retryAttempt, backOffDelay) {
Object.assign(webSocket, {
delay: backOffDelay,
backoff: new Promise(resolve => (setTimeout(resolve, backOffDelay))),
Copy link
Owner

Choose a reason for hiding this comment

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

Given that backoff always seems to wait delay seconds, is it necessary to assign this as a function to the socket? Could we just do the waiting directly here https://github.com/solid/react-components/pull/30/files#diff-b2ffa2da88905c291381e2f58b66eb35R115 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. This is a lot clearer moving it out of here.

@RubenVerborgh RubenVerborgh force-pushed the feature/reconnect-on-close branch 2 times, most recently from 256c9de to 12fce38 Compare March 23, 2020 22:46
@RubenVerborgh
Copy link
Owner

Hi @jholleran, I spent some time with the code, and simplified it down to 341ec20.

However, while doing so, I noticed this case: 12fce38
The reconnectAfterBackoff seems rely on the assumption that the socket on which it is called, is the active socket. However, that assumption fails.

I think this is an error in the tests, but wanted to verify with you.

@RubenVerborgh RubenVerborgh self-requested a review March 23, 2020 22:48
@jholleran
Copy link
Contributor Author

Yes, this is an error in the tests. If you run the two failing tests individually they will pass but when the full UpdateTracker-test suite runs they will fail.

The problem here is the webSocket array that is used in the test will hold the created WebSockets from the previous tests.

If the webSockets array is cleared before each test then there would be no WS to call the onclose on. So it was a bit of a hack to use the first WS in that array. Here the webSocket array is reset and will have the correct values for the rest of the test.

This was one thing that I struggled with writing them tests. It would be great to tidy this up a bit and have the test totally isolated. That was the reason that I was removing WS's on unsubscribe so that I could remove all and test on a clean slate (but this caused a bigger problem as I shouldn't have been deleting them).

@RubenVerborgh
Copy link
Owner

Got it, thanks. Will add a reset function for testing purposes.

@RubenVerborgh RubenVerborgh force-pushed the feature/reconnect-on-close branch 2 times, most recently from 8333fb6 to b7e8cad Compare March 29, 2020 19:17
@RubenVerborgh RubenVerborgh merged commit e5685e4 into RubenVerborgh:master Mar 29, 2020
@RubenVerborgh
Copy link
Owner

Thanks @jholleran!

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UpdateTracter to reconnect and re-subscribe on close events
3 participants