-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Make sure ActionCableHandler only unsubscribes once #5109
Conversation
This didn't help :S |
@rmosolgo That's actually helps in my case, can you please reconsider? My case is: I'm using Urql for frontend, and when I'm doing unmount of one component that uses Urql.useSubscription, and rendering second component (switching components on the same page), that also uses Urql.useSubscription, unsubscribe happens twice. Here is how it looks in websocket trace:
This leave second component not subscribed. I ended up monkey patching createUrqlActionCableSubscription for fix. import { Consumer, Subscription } from "@rails/actioncable"
type ForwardCallback = (...args: any[]) => void
function createUrqlActionCableSubscription(consumer: Consumer, channelName: string = "GraphqlChannel") {
return function(operation: Operation) {
let subscription: Subscription | null = null
let subscribed = false
return {
subscribe: ({next, error, complete}: { next: ForwardCallback, error: ForwardCallback, complete: ForwardCallback}) => {
subscription = consumer.subscriptions.create(channelName, {
connected() {
console.log(subscription)
subscription?.perform("execute", { query: operation.query, variables: operation.variables }).then(() => {
subscribed = true
});
},
received(data: any) {
if (data?.result?.errors) {
error(data.errors)
}
if (data?.result?.data) {
next(data.result)
}
if (!data.more) {
complete()
}
}
} as any)
return {
unsubscribe: () => {
console.log('unsubscribe', subscribed)
if (subscribed) {
subscribed = false
subscription?.unsubscribe()
} else {
console.log('not subscribed')
}
}
}
}
}
}
}
const forwardToActionCable = createUrqlActionCableSubscription(actionCable); |
Hey @vbatychko-modeln, sure, I'd be happy to merge and release this -- thanks for sharing what you found! |
🚢 in graphql-ruby-client v1.14.3. Please let me know if you have any more trouble with it! |
@rmosolgo I have spent some time debugging that issue with double unsubscriptions. Looks like the problem, at least in my case is as follows. Note that I'm using Urql with ActionCable. Preconditions:
The problem exposes itself, when first component is unmounting (that triggers also unsubscribe() call), and the second is starting subscription negotiation. In this case, new handler for second component receives {more: false} AC message, which trigger complete() callback, and I suspect that complete() callback in turn calls unsubscribe(), causing subscription to interrupt negotiation. This leaves second component unsubscribed. I have reworked initial monkey patch from my previous message to following, which fixes doubled unsubscription calls. import { Consumer, Subscription, logger } from "@rails/actioncable"
type ForwardCallback = (...args: any[]) => void
function createUrqlActionCableSubscription(consumer: Consumer, channelName: string = "GraphqlChannel") {
return function (operation: Operation) {
const subscribe = ({ next, error, complete }: { next: ForwardCallback, error: ForwardCallback, complete: ForwardCallback }) => {
let subscribed = false;
const subscription: Subscription = consumer.subscriptions.create(channelName, {
connected() {
subscription.perform("execute", { query: operation.query, variables: operation.variables });
subscribed = true;
},
received(data: any) {
if (data?.result?.errors) {
error(data.errors);
}
if (data?.result?.data) {
next(data.result);
}
if (!data.more && subscribed) {
complete();
}
}
} as any);
const unsubscribe = () => {
if (subscribed) {
subscribed = false;
subscription?.unsubscribe();
}
};
return { unsubscribe };
};
return { subscribe };
};
}
const forwardToActionCable = createUrqlActionCableSubscription(actionCable);
const client = createClient({
url: '/graphql',
exchanges: [
cacheExchange,
fetchExchange,
subscriptionExchange({
forwardSubscription: forwardToActionCable as any
}),
],
}); The change that matters at large is here:
I'm not sure how this translates to createActionCableHandler, which is targeted in this PR, but here are my 2c. |
Trying to fix #5086