Graphql-yoga: Subscriptions fire multiple times

Created on 10 Jan 2018  路  22Comments  路  Source: dotansimha/graphql-yoga

I have an issue where the subscriptions are firing multiple times per event when they should only fire once. The number of times the subscriptions fire increases the more requests the server receives.

The issue can be reproduced with this project: https://github.com/howtographql/react-apollo/tree/gc-1.0

statupr-welcome

Most helpful comment

This problem still happens while using the managed Graphcool service

All 22 comments

Any news on this @schickling?

For me it doesn't get fired :(

@tfiwm can you share a link to your repo so we can reproduce your issue?

Might be related to this, @tfiwm: https://github.com/graphcool/prisma/issues/1734

I'm experiencing this as well:

Response from http://localhost:4466/homeroom-api/dev:
{
  "message": {
    "node": {
      "text": "twice?",
      "sender": {
        "id": "cjcq8hd7e00130143cj9sg8mz",
        "name": "Logan Bernard",
        "__typename": "User"
      },
      "__typename": "Message"
    },
    "__typename": "MessageSubscriptionPayload"
  }
}
Response from http://localhost:4466/homeroom-api/dev:
{
  "message": {
    "node": {
      "text": "twice?",
      "sender": {
        "id": "cjcq8hd7e00130143cj9sg8mz",
        "name": "Logan Bernard",
        "__typename": "User"
      },
      "__typename": "Message"
    },
    "__typename": "MessageSubscriptionPayload"
  }
}
Response from http://localhost:4466/homeroom-api/dev:
{
  "message": {
    "node": {
      "text": "twice?",
      "sender": {
        "id": "cjcq8hd7e00130143cj9sg8mz",
        "name": "Logan Bernard",
        "__typename": "User"
      },
      "__typename": "Message"
    },
    "__typename": "MessageSubscriptionPayload"
  }
}

@tfiwm found out that this is easily reproduced when refreshing the browser. Then a new connection is created, but old connections are _also_ set up again.

Is this reproducable in the playground? Or just combined with the apollo-client?

It's reproducible with this "subscription client": https://gist.github.com/marktani/5df524523693c88be425bfb623ca8b8a

Note that this observation might be a different phenomenon than what @nikolasburk originally reported.

It might be two separate problems, client and server side.

https://github.com/oxy88/prisma_subscription
In my case,

  1. [Prisma only] Subscription fires once
  2. [Prisma with Apollo] Subscriptions fire multiple times
  3. [Turn off Apollo] Subscriptions fire multiple times
  4. [Turn off Apollo and restart Prisma] Subscription fires once

@oxy88 Your steps description seems to indicate that yoga is not involved here?

Anyone have an update here? I'm not sure if this is graphql-yoga related or not.

I currently have a work around to ignore duplicate subscription messages, but it's less than ideal 馃槄

We are currently looking into this with a fix coming up soon 馃檪

I did a PR to graphql-tools, as soon as it's merged, the problem is fixed. https://github.com/apollographql/graphql-tools/pull/609

The fix was merged and is available in version [email protected].

Thanks everyone for feedback and help to pinpoint this issue 馃檶

This problem still happens while using the managed Graphcool service

@darmie maybe worth opening a new issue?

This problem still happens while using Prisma

any word on this? Still happens when using prisma

I have an issue where the subscriptions are firing multiple times per event when they should only fire once. The number of times the subscriptions fire increases the more requests the server receives.

The issue can be reproduced with this project: https://github.com/howtographql/react-apollo/tree/gc-1.0

@nikolasburk
Hi Nikolas,

I was doing your tutorial with react and apollo on howtographql.com, and I seam to figure out why subscriptions fire multiple times.
It seams to have nothing to do with Prisma or Yoga server, but with component of react-apollo

The number of firing doesn't depend on number of requests on server but rather on number of renderings of Query component in LinkList, each time receiving new data from subscription, LinkList component rerender Query and makes new subscription for the same data, and then get it multiple times per request.

I solved this issue with dirty fix by wrapping subscription functions with flags, that prevent Query from subscribing multiple times.

Here is your LinkList code with my fixes.

`import React, { Component } from 'react'
import Link from './Link'
import { Query } from 'react-apollo'
import gql from 'graphql-tag'

export const FEED_QUERY = gql { feed { links { id createdAt url description postedBy { id name } votes { id user { id } } } } }

const NEW_LINKS_SUBSCRIPTION = gql subscription { newLink { node { id url description createdAt postedBy { id name } votes { id user { id } } } } }

const NEW_VOTES_SUBSCRIPTION = gql subscription { newVote { node { id link { id url description createdAt postedBy { id name } votes { id user { id } } } user { id } } } }
class LinkList extends Component {
state = {
subscribedToNewLinks:false,
subscribedToNewVotes:false,
}
_updateCacheAfterVote = (store, createVote, linkId) => {
const data = store.readQuery({ query: FEED_QUERY })

    const votedLink = data.feed.links.find(link => link.id === linkId)
    votedLink.votes = createVote.link.votes

    store.writeQuery({ query: FEED_QUERY, data })
}

_subscribeToNewLinks = subscribeToMore => {
    subscribeToMore({
        document: NEW_LINKS_SUBSCRIPTION,
        updateQuery: (prev, { subscriptionData }) => {
            if (!subscriptionData.data) return prev
            const newLink = subscriptionData.data.newLink.node

            return Object.assign({}, prev, {
                feed: {
                    links: [newLink, ...prev.feed.links],
                    count: prev.feed.links.length + 1,
                    __typename: prev.feed.__typename
                }
            })
        }
    })
    this.setState({subscribedToNewLinks:true})
}

_subscribeToNewVotes = subscribeToMore => {
    subscribeToMore({
        document: NEW_VOTES_SUBSCRIPTION
    })
    this.setState({subscribedToNewVotes:true})
}

render() {

    return (
        <Query query={FEED_QUERY}>
            {({loading, error, data, subscribeToMore }) => {
                if (loading) return <div>Fetching</div>
                if (error) return <div>Error</div>
                if(!this.state.subscribedToNewLinks){
                    this._subscribeToNewLinks(subscribeToMore)
                }
                if(!this.state.subscribedToNewVotes){
                    this._subscribeToNewVotes(subscribeToMore)
                }
                const linksToRender = data.feed.links

                return (
                    <div>
                        {linksToRender.map((link, index) => (
                        <Link
                            key={link.id}
                            link={link}
                            index={index}
                            updateStoreAfterVote={this._updateCacheAfterVote}/>
                        ))}
                    </div>
                )
            }}
        </Query>
    )
}

}

export default LinkList`

I think that problem is that subscriptions should be registered on mount and unregistered on unmount of the component.

Hope that helps,

Best regards,
Myroshnyk Roman

PS. I opened an issue on react-apollo https://github.com/apollographql/react-apollo/issues/2656

Was able to do it by passing subscribeToMore into a child component and subscribing in componentDidMount

Was able to do it by passing subscribeToMore into a child component and subscribing in componentDidMount

I think that's valid approach, but you should be careful to unregister subscription on componentWillUnmount in case the whole component will be rerendered upon link change or some other scenarios,
It feels for me, all subscriptions are saved somewhere in singleton of react-apollo, and leaving subscription uregistered may lead to multiple mutations of store on one emitted subscription.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

yesprasad picture yesprasad  路  3Comments

ahmedosama5200 picture ahmedosama5200  路  4Comments

asci picture asci  路  3Comments

chakrihacker picture chakrihacker  路  5Comments

SebastianEdwards picture SebastianEdwards  路  4Comments