parse-community / parse-server

Parse Server for Node.js / Express
https://parseplatform.org
Apache License 2.0
20.8k stars 4.77k forks source link

LiveQuery for real time messaging - Discussion and Thoughts and Questions #3103

Closed ranhsd closed 5 years ago

ranhsd commented 7 years ago

Hi,

In my app i currently use LiveQuery for the "real time" parts. Currently it serve the following:

  1. User Presence - when a user go online/offline
  2. User profile update - when a user update his/her profile all of the user friends are being updated in real time
  3. Friend request accepted - When a pending friend request was accepted on the other side and the current user is online then it will get notified
  4. Other in-app notifications

Now I have another part in my app there i let users to chat in realtime with each other. Currently only one user can chat with only another user but in the future we will also add groups so i design my MongoDB schema to support both cases. Currently i am implementing the real time chat via LiveQuery and it works fine. The big advantage to use LiveQuery is pretty straightforward... it allows me to store all the info in MongoDB, support media out of the box (images, videos etc.) and not count on other 3-party solution that i start to investigate (e.g. firebase, pubnub etc.)

The question that i wanted to ask if you think that it's safe to go with LiveQuery for my real time chat or should i go with another 3-party provider? I am a huge fan of parse-server and i really like the LiveQuery concept but i am wondering if it is good for real time messaging.

to summarize this, my questions are (based on my current tech stack (see below)):

  1. Scalability - can i serve hundred of thousands of connections with this solution?
  2. Performance - What about the performance? I want to the message will be sent to other side as fast as possible (because we are talking about "real time messaging")
  3. Flexibility - If i want to enrich my solution with more capabilities like: user is typing, user read the message, last seen and more...

My Tech Stack

  1. Docker image for Parse Server (currently 2 replicas)
  2. Docker image for Parse Live Query server (currently 3 replicas)
  3. Docker image for Parse Dashboard (1 replica)
  4. NGINX for load balancing 5.. Redis server for pub/sub and other key-value queries
  5. Docker image for MongoDB with 3 replicas
  6. Google cloud storage - for hosting images, videos and other binary content
  7. All images are running inside a kubernetes cluster which deployed on GKE (google container engine)
  8. My cluster is auto scaled means that i can increase or decrease the number of nodes and i can also increase my replicas dynamically for parse server or live query servers.

Thanks in advanced for your input!

dpoetzsch commented 7 years ago

I am implementing a similar thing at the moment.

It does work fine with live query for now. However, I can't tell you about scalability as we haven't scaled so much yet. I'd be also interested in experiences here.

We easily implemented stuff like "read" and "last seen" in our model. I was quite happy about the flexibility. Performance-wise we couldn't complain so far.

Still, be careful on how to design your data model: Live Query currently only supports simple, non-relational queries as discussed in issue #2946. This was a small issue for me when trying to have group chats as I didn't want to copy all receivers into each message at first.

ranhsd commented 7 years ago

Hi @dpoetzsch , thanks for your response.. Where your parse-server and parse-livequery servers are deployed ?

dpoetzsch commented 7 years ago

We currently work with heroku but are only in a closed beta phase so far.

ideastouch commented 7 years ago

Hi, I'm also using LiveQuery and Parse-Server both together at same Heroku app. I have also still running Parse.com so I have a daemon on Heroku that search for changes done through Parse.com server and then I send directly to LiveQueryServer at heroku a _onAfterSave message. About the kind of queries

// Chat is a PFObject extender object.
var query = Chat.query()
// row customerUser is pointer to CustomerUser
// CustomerUser is _User extender object.
query.whereKey("customerUser", equalTo: customerUser);
query.whereKey("readByCustomer", equalTo: false) ;
// Appointment is a PFObject extender object.
var queryAppointment = Appointment.query();
// row date is a Date Object.
queryAppointment.whereKey("date", greaterThan: new Date());
queryAppointment.whereKey("cancelled", notEqualTo: true)
// row appointment is pointer to Appointment.
query.whereKey("appointment", matchesQuery: queryAppointment)

BTW: I also use a modificated ParseLiveQuery Dictionary::init, see my comments at https://github.com/ParsePlatform/ParseLiveQuery-iOS-OSX/issues/40#issuecomment-255806307

malhal commented 7 years ago

I was wondering about the issue around missing object changes, i.e. it likely depends on the order of subscribe and find.

faizsameerahmed96 commented 7 years ago

The best way according to me is use an established real time service. You can create your own easily through Socket.io or use an existing service like firebase.

blainefarr commented 7 years ago

@ranhsd @dpoetzsch I'm interested in this as well. We're just now evaluating Live Query vs a 3rd party tool for chat and group chat / scalability is the big question. What did you end up using?

How difficult has it been to build chat in with Live Query so far? We may go down that path for MVP and migrate later as need dictates.

ranhsd commented 7 years ago

Hi @blainefarr - I think that the LiveQuery is great for "real time database" scenarios where you want to get an update about the status of one or more objects ... it is also possible to use LiveQuery for real time messaging scenario (like i did) but it will require a bit more work on your side. Think about all the features that you have in a simple chat application like:

If you have more ideas it will be great if you can share them here...

davidruisinger commented 7 years ago

Hi all, I'm currently building a small chat feature within our app using the LiveQuery implementation. My main concerns are all security related. Besides the fact, that LiveQueries (at least for me) do not respect class level permissions (see: https://github.com/ParsePlatform/parse-server/issues/3427) I'm still not sure how to secure my Messages.

I basically use 2 parse classes for my implementation:

I don't have a problem with my Chat class beeing publicly readable since it only show some user ids. So I focused on securing the Message class.

Initial attempt I initially thought I could use class level permissions and restrict read access to the author. Even if those class level permissions would be respected by LiveQueries, this wouldn't work since that would mean the user would only subscribe to messages he wrote himself.

Current solution I think it might work to add a beforeSafe function in Cloud code to set some custom ACL which would set read access to all participants of the chat. When a new user enters the chat, he would NOT have access to previous/old messages but could read along all messages written from now on (which is quite similar to common apps like whatsapp)

Do you guys have any other idea or tips on how to secure a chat application running on parse?

ranhsd commented 7 years ago

Hi @flavordaaave What about create ACL's (for read/write) only for the list of participants that you have under the Chat object?

Samigos commented 7 years ago

Hi everyone!

I'm in the same place as you were! I have a Parse Server where I run my app and store my db and I have another one for LiveQuery...

That's where I'm stuck... I've seen Parse's documentation, but it's not much explanatory! Can someone please tell me why do I need redis and how can I use it?

flovilmart commented 7 years ago

@Samigos what's not clear about the redis usage? Consider opening an issue on the docs repository if the notes about scaling LiveQuery are not clear.

Samigos commented 7 years ago

@flovilmart Thanks for responding! It says how to connect to a redis IP, but it doesn't clarify what we need to do redis-side... (I don't mean the exact steps, but what needs to be done)

For people like me, that hadn't heard redis before, it's a bit tough!

flovilmart commented 7 years ago

There's nothing to do redis-side, it's a Key-Value store, just follow any documentation about redis to start it up

Samigos commented 7 years ago

That's great! So if I got it right, I need to deploy somewhere the redis-server, correct? If so, is it a good idea to install it on the server where my Parse Server is deployed, or should I do it on a separate one?

Thanks in advance!

Samigos commented 7 years ago

Sorry if here's not the place to ask these questions, but just now things started clearing up in my head, about how to proceed.

ranhsd commented 7 years ago

@Samigos - If you are planning to run it in production then the best will be to run it on a separate service. By service i refer to either: server, container, etc.

THEDOWNCOUNTRY commented 7 years ago

Hello guys,

I am using the latest JS client to connect to Parse-Server LiveQuery I notice that when I close my app there is a disconnect log that is triggered I need some help to use that event to updated the user account status... Any input or code snippets are welcome. Thanks in advance.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Etto91 commented 4 years ago

someone can help me? https://github.com/parse-community/parse-server/issues/1906#issuecomment-616805834

maxiqsoft commented 3 years ago

I found a solution.

I've added one line of code to the file /node_modules/parse-server/lib/LiveQuery/ParseLiveQueryServer.js

The line is sessionToken: client.sessionToken, and in my case in line 459.

With that you are able to recognize who disconnected from the websocket by the users sessionToken.

Before it was just possible to get the sessionToken when the user connected.

With that you can make a function to get the user object from the Session class and save it maybe in an array.

This is where I added the line:

(0, _triggers.runLiveQueryEventHandlers)({
        event: 'ws_disconnect',
        clients: this.clients.size,
        sessionToken: client.sessionToken,
        subscriptions: this.subscriptions.size,
        useMasterKey: client.hasMasterKey,
        installationId: client.installationId
      });