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

Live Queries Server: wrong event returned when multiple subscription triggered #7340

Closed
5 of 6 tasks
percypyan opened this issue Apr 12, 2021 · 7 comments
Closed
5 of 6 tasks
Labels
state:released Released as stable version state:released-beta Released as beta version type:bug Impaired feature or lacking behavior that is likely assumed

Comments

@percypyan
Copy link
Contributor

percypyan commented Apr 12, 2021

New Issue Checklist

Issue Description

Since #7097, when two subscriptions on a collection are triggered simultaneously by a change but with different events (e.g. leave for the first and enter for the second), the same event is returned to the two web socket clients.
This is because of the combinaison of the forEach at ParseLiveQueryServer.js#L240 and the message.event = ... at ParseLiveQueryServer.js#L301.

message.event = ... is an update of the argument and cause issues because it's used in the asynchronous forEach. When the message.event is updated in one of the forEach loop run, it is updated for all. So when the function name is computed at ParseLiveQueryServer.js#L338, the last event attributed is used.

We should use res.event instead of message.event: this way we avoid updating a function argument and we ensure that the right event is triggered.

Steps to reproduce

Create a client with 2 subscriptions on the same collection:

  1. const sub1 = await Parse.Query("MyCollection").equalTo("myBoolean", true).subscribe()
  2. const sub2 = await Parse.Query("MyCollection").equalTo("myBoolean", false).subscribe()

Change an object by updating field myBoolean from true to false (or in the other way).

You'll see that on the client you will receive two identical events (e.g. leave) instead of two opposite event (leave and enter).

Actual Outcome

With the example above we received two identical event (e.g. leave).

Expected Outcome

We expect to receive two opposite events, leave and enter.

Failing Test Case / Pull Request

We are working on the PR! It will be created soon 🙂

Environment

Server

  • Parse Server version: 4.5.0
  • Operating system: Node 12 on Heroku (Ubuntu)
  • Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): Heroku

Database

  • System (MongoDB or Postgres): MongoDB
  • Database version: 4.2.13
  • Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): MongoDB Atlas

Client

  • SDK (iOS, Android, JavaScript, PHP, Unity, etc): JavaScript
  • SDK version: 2.10.0

Logs

Logging doesn't work well on our local or Heroku machines. We get %s instead of the actual value.
But we logged the event computed res.event and the message.event and seen the difference. You'll see the details of the bug with the coming PR.

Contributors

Discovered by @fbeiger and @percypyan from PINPODEV.

@mtrezza
Copy link
Member

mtrezza commented Apr 12, 2021

Thanks for reporting!

That's a great in-depth examination of the issue, we'll look forward to the PR.

To classify the bug severity, are you aware of any workaround to mitigate this issue?

@mtrezza mtrezza added the type:bug Impaired feature or lacking behavior that is likely assumed label Apr 12, 2021
@percypyan
Copy link
Contributor Author

There is no workaround for all I know...

@fbeiger
Copy link

fbeiger commented Apr 12, 2021

Well if all the subscriptions concerned are in the same app, you can create 1 subscription (with a bigger filter) and use the update event to interpolate logic. So for our precedent example, instead of having 2 subscriptions we can have:

const sub = await Parse.Query("MyCollection").subscribe()

sub.on("update", (object) => {
   if (object.get("myBoolean") === true) {
      // logic if true
   } else {
      // logic if false
   }
})

But this can lead to a lot of events being sent, and also you'll need to cache locally the previous value of myBoolean for each object (because now you can't have enter or leave events when myBoolean switches).

Now if the subscriptions are in different apps I think the only way to bypass this would be to add a "buffer" state for your object, i.e. instead of switching myBoolean from true => false (or vice versa), you'd need to do something like true => undefined => false. But now you have to ensure that your data will always go through this "buffer" state whenever you want to change myBoolean ...
We didn't think of this case because our subscriptions were all in the same app, but now I reckon it's a pretty big deal.

percypyan pushed a commit to PINPODEV/parse-server that referenced this issue Apr 12, 2021
If any delay occurs after "message.event" assignation in
LiveQueryServer._onAfterSave, the next subscription or request with a different
event might overwrite it, and by that using the wrong "push" function name.
@percypyan
Copy link
Contributor Author

percypyan commented Apr 12, 2021

Yes, that's true: I've answered too quickly. Those workarounds may work in some specific cases but aren't really sustainable, as explained by @fbeiger.

@mtrezza
Copy link
Member

mtrezza commented Apr 12, 2021

Thanks for providing more details.

I classify this issue as bug with severity S3 (normal - blocks non-critical functionality and a work around exists):

@mtrezza mtrezza added the S3 label Apr 12, 2021
Arul- pushed a commit to Arul-/parse-server that referenced this issue Apr 20, 2021
…h event (parse-community#7341)

* Add a failing test for issue parse-community#7340

If any delay occurs after "message.event" assignation in
LiveQueryServer._onAfterSave, the next subscription or request with a different
event might overwrite it, and by that using the wrong "push" function name.

* Remove updade of message and use res.event instead

This prevent computing function name from a
incorrect event if multiple subscriptions override
one by one the message.event.

* Update CHANGELOG.md

* Replace setTimeout by async/await expressions
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 5.0.0-beta.1

@parseplatformorg parseplatformorg added the state:released-beta Released as beta version label Nov 1, 2021
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 5.0.0

@parseplatformorg parseplatformorg added the state:released Released as stable version label Mar 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released Released as stable version state:released-beta Released as beta version type:bug Impaired feature or lacking behavior that is likely assumed
Projects
None yet
Development

No branches or pull requests

4 participants