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

Infinite loop after last unsubscribe in withFilter function. #81

Closed
swangee opened this issue Jun 23, 2017 · 6 comments
Closed

Infinite loop after last unsubscribe in withFilter function. #81

swangee opened this issue Jun 23, 2017 · 6 comments
Labels

Comments

@swangee
Copy link

swangee commented Jun 23, 2017

I use withFilter to validate payload and got an infinite loop when there are no subscribers left (payload is resolved immediately with {value: undefined, done: true}).

const getNextPromise = () => {
        return asyncIterator
          .next()
          .then(payload => Promise.all([
            payload,
            Promise.resolve(filterFn(payload.value, args, context, info)).catch(() => false),
          ]))
          .then(([payload, filterResult]) => {
            if (filterResult === true) {
              return payload;
            }

            // Skip the current value and wait for the next one
            return getNextPromise();
          });
      };

This function recursively calls itself even if payload.done is true and it leads to infinite call. Adding check for payload.done before return getNextPromise(); fixes this problem or am I missing something?

@maktouch
Copy link
Contributor

Doing if (filterResult === true || payload.done === true) fixes it for me.

    const getNextPromise = () => {
        return asyncIterator
          .next()
          .then(payload => Promise.all([
            payload,
            Promise.resolve(filterFn(payload.value, args, context, info)).catch(() => false),
          ]))
          .then(([payload, filterResult]) => {
            if (filterResult === true || payload.done === true) {
              return payload;
            }

            // Skip the current value and wait for the next one
            return getNextPromise();
          });
      };

@cncolder
Copy link

+1 Fall into this trap

@swangee
Copy link
Author

swangee commented Jun 26, 2017

@maktouch Firstly if fixed it in the same way, but it has a side effect - undefined is passed to filter function once, so check for undefined is needed in a filterFn.
So I decided it add some inner state to withFilter with flag returned and added the check to it. Now it looks in this way:

module.exports.withFilter = (asyncIteratorFn, filterFn) => {
    return (rootValue, args, context, info) => {
      const asyncIterator = asyncIteratorFn();

      let returned = false;

      const getNextPromise = () => {
        if (returned) {
          return {done: true, value: undefined};
        }

        return asyncIterator
          .next()
          .then(payload => Promise.all([
            payload,
            returned ? false : Promise.resolve(filterFn(payload.value, args, context, info)).catch(() => false),
          ]))
          .then(([payload, filterResult]) => {
            if (filterResult === true) {
              return payload;
            }

            // Skip the current value and wait for the next one
            return getNextPromise();
          });
      };

      return {
        next() {
          return getNextPromise();
        },
        return() {
          returned = true;
          return asyncIterator.return();
        },
        throw(error) {
          return asyncIterator.throw(error);
        },
        [$$asyncIterator]() {
          return this;
        },
      };
    };
};

@cncolder
Copy link

@maktouch What about only skip filterFn if value is undefined?

Promise.resolve(!!payload.value && filterFn(payload.value, args, context, info)).catch(() => false)

@maktouch
Copy link
Contributor

I don't know, actually. I'm not sure what's the intended behaviour of getNextPromise; I noticed that my subscribe functions gets called every time I subscribe to something, and it's passing undefined. It would be nice to have input from the maintainers for some more clarification.

In the meantime, I'm checking for payload === undefined in the filterFn

maktouch added a commit to playerme/graphql-subscriptions that referenced this issue Jun 27, 2017
apollographql#81

Check for filterResult or if it `payload.done`.  Has a side effect that it `undefined` is passed to filterFn. At least it doesn't infinite loop.
@pcarrier pcarrier added the bug label Jun 28, 2017
maktouch added a commit to playerme/graphql-subscriptions that referenced this issue Jun 30, 2017
maktouch added a commit to playerme/graphql-subscriptions that referenced this issue Jun 30, 2017
stubailo pushed a commit that referenced this issue Jul 3, 2017
Fix Issue #81 Infinite loop after last unsubscribe in withFilter function
@green-coder
Copy link
Contributor

.. so @cncolder and @vedebel actually saw the bug #132 coming but it was eventually merged into the codebase. :-(

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

No branches or pull requests

5 participants