Closed tleish closed 2 years ago
Could you please reproduce it with the anycable-client
debug logs (createCable({logLevel: 'debug', ...})
) and show the console output?
Console output with logLevel: 'debug'
.
### PAGE 1
index.js:9 [debug] connecting
index.js:7 [debug] incoming data {type: 'welcome'}
index.js:9 [info] connected
index.js:7 [debug] subscribing {identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] incoming data {type: 'confirm_subscription', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] subscribed {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}', identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762965}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762968}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762971}
### PAGE 2
index.js:7 [debug] unsubscribing... {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] outgoing message {command: 'unsubscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] subscribing {identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] unsubscribed remotely {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] incoming data {type: 'confirm_subscription', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] subscribed {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}', identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762974}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762977}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762980}
### BACK TO PAGE 1 (TURBO PREVIEW)
index.js:7 [debug] unsubscribing... {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] outgoing message {command: 'unsubscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] subscribing {identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] unsubscribed remotely {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] subscribing {identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] incoming data {type: 'confirm_subscription', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] subscribed {id: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}', identifier: 'Turbo::StreamsChannel', params: {…}}
### BACK TO PAGE 1 (TURBO FETCH)
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762983}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762986}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}'}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762989}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762992}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762995}
### BROADCAST MESSAGE
index.js:7 [debug] incoming data {identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}', message: '<turbo-stream action="append" target="turbo-alerts…vg>\n </button>\n</div>\n</template></turbo-stream>'}
index.js:7 [debug] processed incoming message {identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_..."}', message: '<turbo-stream action="append" target="turbo-alerts…vg>\n </button>\n</div>\n</template></turbo-stream>'}
index.js:7 [debug] incoming data {type: 'ping', message: 1657762998}
index.js:7 [debug] incoming data {type: 'ping', message: 1657763001}
index.js:7 [debug] incoming data {type: 'ping', message: 1657763004}
So, here we had the following issues:
1) await subscribeTo
could still be pending (due to unsubscribe/subscribe race conditions and the way we handle them) when disconnectedCallback
is called, resulting in never invoked subscription.disconnect
2) The subscribe retry mechanism has another race condition (when we retry if no ack has been received), which could result in a subscribe command called for the stale/disconnected channel instance.
I released a new version of @anycable/turbo-stream
, which resolves the first problem and also reduces the number of potential race conditions (or even eliminates—I wasn't able to reproduce them) by ignore Turbo cached pages (because we don't really need to react on them).
This version is actually a complete rewrite (planned a long time ago), which brings more flexibility and extensibility to the Turbo Streams integration. See https://github.com/anycable/anycable-client/tree/master/packages/turbo-stream
Re: 2. I plan to spend more time, revert a quick fix I pushed before and provide more robust implementation. I have to admit, that my initial idea of having async/await API for subscriptions doesn't play well with the asynchronous nature of WebSockets. And Turbo Streams revealed all the pain points 🙂 Thus, some minor API breaking changes (and significant internal rewrites) are required.
Please, check the updated version of @anycable/turbo-stream
and let me know, if it solves the issue.
Just tested it out and the turbo-stream now works.
Note that the logs still seem a little odd.
### BACK TO PAGE 1
index.js:7 [debug] subscribing {identifier: 'Turbo::StreamsChannel', params: {…}}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_…0eee98b502ed69ac53dce55054526f950487debca56a47b"}'}
index.js:7 [debug] incoming data {type: 'ping', message: 1657832674}
index.js:7 [debug] incoming data {type: 'ping', message: 1657832677}
index.js:7 [debug] outgoing message {command: 'subscribe', identifier: '{"channel":"Turbo::StreamsChannel","signed_stream_…0eee98b502ed69ac53dce55054526f950487debca56a47b"}'}
index.js:7 [debug] incoming data {type: 'ping', message: 1657832680}
index.js:7 [debug] incoming data {type: 'ping', message: 1657832683}
index.js:7 [debug] incoming data {type: 'ping', message: 1657832686}
The client attempts to subscribe twice when visiting a prior page, but it doesn't appear to receive and confirmation back from the server that it's subscribed.
anycable-go logs failures of "Already Subscribed":
### BACK TO PAGE 1
D 2022-07-14T21:04:32.794Z context=node sid=16ocsA8akFjnbSrZ5ekdG Incoming message: &{subscribe {"channel":"Turbo::StreamsChannel","signed_stream_name":"..."} <nil>}
W 2022-07-14T21:04:32.794Z context=node sid=16ocsA8akFjnbSrZ5ekdG Failed to handle incoming message '{"command":"subscribe","identifier":"{\"channel\":\"Turbo::StreamsChannel\",\"signed_stream_name\":\"...\"}"}' with error: Already subscribed to {"channel":"Turbo::StreamsChannel","signed_stream_name":"..."}
D 2022-07-14T21:04:37.796Z context=node sid=16ocsA8akFjnbSrZ5ekdG Incoming message: &{subscribe {"channel":"Turbo::StreamsChannel","signed_stream_name":"..."} <nil>}
W 2022-07-14T21:04:37.796Z context=node sid=16ocsA8akFjnbSrZ5ekdG Failed to handle incoming message '{"command":"subscribe","identifier":"{\"channel\":\"Turbo::StreamsChannel\",\"signed_stream_name\":\"...\"}"}' with error: Already subscribed to {"channel":"Turbo::StreamsChannel","signed_stream_name":"..."}
Is this expected?
That relates to the second problem, so, it's an expected behavior for now 😕
It looks like with the latest client our system tests now fail with a bunch of the following errors:
Failure/Error: RACK_HIJACK => lambda { raise NotImplementedError, "only partial hijack is supported."},
NotImplementedError:
only partial hijack is supported.
Not sure if users will experience the same issues.
Hm, it's not related to the client library; it's the exception from the Ruby web server. Which one do you use in your system tests? Looks like Webrick (it doesn't support full hijack).
False alarm. I saw ActionCable in the callstack and assumed it was related to these changes. Thanks.
Released @anycable/core
and @anecable/web
0.5.0, and @anycable/turbo-stream
0.2.0. The number 2 from the above is fixed; now everything should be smooth.
Some warnings could still appear if you navigating too fast, for example:
[warn] ReasonError: Channel was disconnected before subscribing
[warn] ignore subscription confirmation, unsubscribed Object
They indicate that we caught some race conditions: attempting to unsubscribe right after sending subscribe
and receiving confirmation, when it's not longer needed.
In testing this latest release
@anycable/web-0.4.0
and@anycable/core-0.4.1
withanycable-go-1.2.1
(from changes in https://github.com/anycable/anycable-client/issues/17), I found that a turbo-stream connection on cached turbo drive page no longer processes websocket messages.Steps to reproduce:
Results: browser logs show message received, but the javascript does not process the stream and there's no change in the current DOM.
Server Client Logs
Client Logs
On visiting a turbo cached page, turbo loads the cached version (as a preview) and replaces it with the fetched version when loaded. To met it appears that
I confirmed that disabling turbo-cache-control temporarily resolves the issue.
Not a permanent solution, just narrowing down where the problem exists.
FYI, I tested
@anycable/web-0.3.2
and@anycable/core-0.3.3
withanycable-go-1.2.1
and don't see this issue, so this was introduced in the latest changes.