Repository navigation
Conversation
A socket error or close during a subscribe left the pending reply state set, so every later subscribe was rejected before anything was sent. Events only came back after a page reload. - Clear the pending subscribe reply on an unexpected disconnect - Stop reusing a rejected shared asset or attribute subscription, and remove the callback of the failed call - Detach and close the previous socket before opening a new one - Restore subscriptions after a reconnect - Let unsubscribe cancel a subscription that is still being restored - Add component tests and npmTest for the core package
|
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Description
Events could stop until the page was reloaded after a connection drop. The event provider reconnected and reported
CONNECTED, but every subscribe failed withThere's already a pending subscriptionand nothing was sent to the server.Cause: a socket
errororclosewhile a subscribe was waiting for its reply left_subscribeDeferredset, because_beforeDisconnectdidn't clear it. Only a deliberate_doDisconnectdid. The interrupted subscribe was queued again, but_doSubscriberejected it and every later subscribe before sending a frame. The rejected_attributeEventPromiseor_assetEventPromisethen stayed cached, so later calls got the same rejection.Two related problems made this worse:
closechanged the state of the current connection, because itsonclosehandler works on the provider's shared state andnew WebSocketreplaced the socket without closing the old one. This could fail a reconnect in progress, trigger the stuck state above, and leave an extra socket open._onConnectusedObject.keys()andfor...inon aMap, which see no entries, so subscriptions were never restored after a reconnect.Evidence: confirmed through Web Inspector on an iPhone, in the app using
core1.28.1, whoseevent.tsbehaves the same asmain. The stuck provider wasCONNECTEDwith_subscribeDeferredset and_pendingSubscriptionnull, and it had two open sockets, only one of them held by the provider. A fresh subscribe was rejected with no frames sent. Clearing_subscribeDeferredand_attributeEventPromiseby hand brought events back without a reload. In the manager, the reproduction below makes creating an asset hang, withUncaught (in promise) There's already a pending subscriptionin the console.Behavior change for consumers: subscriptions now survive a reconnect and keep their ID, so a consumer that holds on to its subscription keeps getting events. A consumer that resubscribes on reconnect still ends with one server subscription, but may send an extra
SUBSCRIBEandUNSUBSCRIBE.unsubscribenow also covers a subscription still being restored, otherwise that consumer would end up with two subscriptions and receive every event twice. Components using thesubscribemixin unsubscribe onDISCONNECTEDand subscribe again onCONNECTED. I traced that flow through the code, but no test covers it.Testing
The 11 new tests use a fake socket, so each test controls the event order. Removing any single change makes at least one test fail.
The issue is confirmed resolved on a test server running this branch, and in the app on 1.28.1 with the fix backported, in Chromium and Firefox.
How the fix was manually tested on the app
Get 1.28.1 with the fix
Build the package
Writes to
ui/component/core/openremote-core-1.28.1-event-provider-fix.tgz.Build the app with it
package.jsonresolutions, set:"@openremote/core": "file:./openremote-core-1.28.1-event-provider-fix.tgz"./gradlew clean installDistyment/build/image/manager/app/<app>/bundle.*.js.Serve it over the deployed app
Chromium:
<app>/bundle.<deployed hash>.jsand choose Override content. DevTools creates<folder>/<host>/<app>/bundle.<deployed hash>.js.cp deployment/build/image/manager/app/<app>/bundle.*.le.<deployed hash>.jstypeof __p._discardWebSocketgives 'function'.Firefox: in Debugger, right-click the bundle, choose Add script override and save it. Copy your build over the saved file, then reload with the cache disabled.
Reproduction
These steps work in any browser and in any app built on core, including the manager.
Found the event provider.to be logged).Shorter Safari only alternative
In Safari's Web Inspector, including iOS web views, this finds the event provider straight away, with no tab switch needed:
There's already a pending subscriptionChangelog
_subscribeDeferredon an unexpected disconnect, so the interrupted subscribe goes out again after the reconnectunsubscribecancel a subscription that is still being restored, once its reply arrives or when the connection drops againnpmTestfor the core packageChecklist