Skip to content
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

Can't get subscriptions to work reliably #12

Open
Twisterking opened this issue Oct 11, 2019 · 2 comments
Open

Can't get subscriptions to work reliably #12

Twisterking opened this issue Oct 11, 2019 · 2 comments

Comments

@Twisterking
Copy link

Hi guys,

After hours of trying pretty much everything I could come up with, I gave up for now - I can't get GraphQL subscriptions to work reliably. Randomly it kinda works, most of the time it does not.

Following setup:

  • Meteor v.1.8.1 App with React frontend and Grapher
  • apollo-live-server
  • apollo-live-client

What do I want to achieve (for now): Just load some items from a collection with a limit and skip (pagination). If something changes in the currently loaded (e.g. 20) items, the subscription should update the items - obviously. 😄

some code:

SERVER:

load({
  typeDefs: `
    type Query {
      lists(listId: ID, limit: Int, skip: Int): [List]
    }
    
    type List @mongo(name: "listsbody") {
      _id: ID
      itemId: ID,
      listItem: ListItem @link(field: "itemId")
      list_id: ID
      row_id: Int
    }

    type ListItem @mongo(name: "items") {
      _id: ID
      created_at: String
      supplier_id: ID,
      item_name: String
    }

    type Subscription {
      lists(listId: ID, limit: Int, skip: Int): SubscriptionEvent
    }

  `,
  // https://github.com/cult-of-coders/grapher/blob/master/docs/graphql.md
  resolvers: {
    Query: {
      lists(_, {listId, limit, skip}, { db }, ast) {
        return db.listsbody.astToQuery(ast, {
          $filters: { list_id: listId },
          $options: { limit, skip, sort: { row_id: 1 } }
        }).fetch();
      },
    },
    List: {
      listItem(parent, args, { db }, ast) {
        return parent.listItem;
      },
    },
    Subscription: {
      lists: {
        resolve: ({event, doc}, args, { db }, ast) => {
          if(event === Event.CHANGED) {
            Object.assign(doc, {
              listItem: db.items.findOne({ _id: doc.itemId })
            });
            // this is very infrequently called, for some reason this is NOT called everytime I change something in the DB, sometimes it is but after a verly large delay of many seconds.
          }
          console.log('resolve() doc:', doc);
          return {event, doc};
        },
        subscribe(_, {listId, limit, skip}, { db }, ast) {
          console.log('params', {listId, limit, skip});
          const observer = db.listsbody.find({ list_id: listId }, { limit, skip, sort: { row_id: 1 } });
          console.log('FETCH!', observer.fetch()); // again, this is sometimes called, sometimes it is not, sometimes with a very big delay ...
          return asyncIterator(observer);
        }
      }
    }
  },
});
initialize();

Any my Client:

const GET_LIST = gql`
  query($listId: ID, $limit: Int, $skip: Int) {
    lists(listId: $listId, limit: $limit, skip: $skip) {
      _id
      list_id
      row_id
      itemId
    }
  }
`

const SUBSCRIBE_LIST = gql`
  subscription($listId: ID, $limit: Int, $skip: Int) {
    lists(listId: $listId, limit: $limit, skip: $skip) {
      event
      doc
    }
  }
`

class Test extends React.Component {
  constructor() {
    super();
    this.state = {
      page: 1
    }
  }
  prevPage = () => {
    this.setState({ page: this.state.page - 1 });
  }
  nextPage = () => {
    this.setState({ page: this.state.page + 1 });
  }
  // https://www.apollographql.com/docs/react/v2.5/api/react-apollo/#optionsfetchpolicy
  render(){
    const {page} = this.state;
    const variables = {listId: "pshLYD4optX3yj9Pw", limit: 20, skip: (page-1)*20};
    console.log('variables', variables);
    return (
      <Fragment>
        <ul>
          <ReactiveQuery
            query={GET_LIST}
            variables={variables}
            subscription={SUBSCRIBE_LIST}
            fetchPolicy='no-cache'
          >
            {
              ({ loading, error, data }) => {
                if(loading) return <h2>loading</h2>
                if(error) {
                  console.log(error)
                  return <h2>Error</h2>
                }                
                let { lists } = data;
                console.log(lists);
                lists = lists.map(list => (
                  <li key={list._id}>
                    {list._id} - {list.itemId} - { list.listItem && list.listItem.item_name || 'N/A' } : {list.row_id}
                  </li>
                ));
                // this is NOT working at all - no reloads, no updates, no nothing when I change anything in the DB - basically the subscription is dead alltogether         
                return lists
              }
            }
          </ReactiveQuery>
        </ul>
        <br />
        Seite {this.state.page}
        <button onClick={this.prevPage}>&lt;</button>
        <button onClick={this.nextPage}>&gt;</button>
      </Fragment>
    )
  }
}

I really really hope someone can help. I would be glad to even pay for maybe 30min - 1h of your time. I am pretty desperate right now, as I think my setup is pretty basic and it is not working at all.

Thanks a bunch for any help in advance! Cheers, Patrick

@theodorDiaconu
Copy link
Contributor

@Twisterking pls create reproduction repo.

@Twisterking
Copy link
Author

So I did some more research, but in a very basic setup apollo-live-server and its subscriptions work properly! :)
As soon as I move over to my "real app", the subscriptions are completely broken.

I copied the following stuff over 100% identically:

import { typeDefs, resolvers } from '/api/schema';
const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});
createApolloServer({
  schema
});

Both versions (the very basic setup (https://github.com/Twisterking/meteor-graphql-test) and the real app) are connected to the MONGO_URL from my main meteor app running on a different port.

BOTH versions use the same setup for subscribing to my openorderbody/cart:

openorderbody: {
  resolve: ({ event, doc }, args, context, ast) => {
    console.log('openorderbody sub resolve:', { event, doc, args });
    doc.__typename = 'OpenOrderElement';
    if(event == 'added' || event == 'changed') {
      Object.assign(doc, {
        item: Items.findOne(doc.itemId)
      });
    }
    return { event, doc };
  },
  subscribe(_, args, context) {
    const { openOrderId } = args;
    const openOrderHead = OpenOrdersHead.findOne({ _id: openOrderId });
    if(!openOrderHead) {
      console.error(`openOrderHead not found for _id ${openOrderId}`);
      return;
    }
    console.log('SUB openOrderHead', openOrderHead);
    const observable = OpenOrdersBody.find({ list_id: openOrderId });
    return asyncIterator(observable, {
      sendInitialAdds: true
    });
  }
}

I my minimal version, the console.log('openorderbody sub resolve:') shows up properly and works just fine, in my real app this console.log is NOT shown.

Any ideas how this is possible? Since all of this is server-only, it really should be a server-only problem correct?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants