centrifugal / centrifugo

Scalable real-time messaging server in a language-agnostic way. Self-hosted alternative to Pubnub, Pusher, Ably. Set up once and forever.
https://centrifugal.dev
Apache License 2.0
8.3k stars 587 forks source link

Best way to integrate push notification #110

Closed narup closed 7 years ago

narup commented 7 years ago

Centrifugo looks great and I am planning to try it out. I have one question. Real-time messaging on apps is not complete without push notifications, so what's the best way to handle that. So if user is not connected to the centrifugo server or app is not active, how the app backend can send push? I guess we can do a app backend to centrifugo to check if client is connected?

Thanks

FZambia commented 7 years ago

@narup hello, glad you like it!

There is last question in FAQ covering this - see https://fzambia.gitbooks.io/centrifugal/content/FAQ.html page. Its not reliable to just check if user online or not - because he can leave your app right after that moment. So implementing custom ack endpoint in your backend is necessary - so you can push notification after some interval.

narup commented 7 years ago

Thanks, even with this approach I can see out of sync issues happening. I see publish response has error field, will it send any specific error response if it cannot deliver the message?

FZambia commented 7 years ago

Could you explain why out of sync issues happen?

Publish response only knows that message was successfully delivered to Centrifugo internal engine. For example it means that message was published into Redis channel (in case of Redis engine working). And actually there could be lots of active subscribers in channel. So it does not show was message delivered to someone or not.

FZambia commented 7 years ago

Just to formulate my ack suggestion into some pseudocode.

On client side you write sth like this:

centrifuge.subscribe("channel", function(message) {
    renderMessage(message);
    // at this moment you know exactly that message was read by client.
    // use application specific message id to ack message.
    sendMessageAckToAppBackend(message.data["app_message_id"]);
});

On backend you have a message table in database with fields like this:

Message table:

id ...
body ...
author ...
time_sent datetime

MessageToUser:

user_id ...
message_id ...
ack_received bool
push_sent bool

So when sendMessageAckToAppBackend called you send request to app server and set ack_received for message and that user to true.

Periodically you can look at time_sent field and send push notifications to all users with messages that have ack_received == false and message.time_sent < now() - 10seconds. Maybe you also need to check push notification delivery status via notification centers API.

As option you can also aggregate messages per user during this interval and send one push to them instead of separate for each message.

If you think about reliable push notifications then I suppose you already have something like structure above in your database.

narup commented 7 years ago

Thanks, I got that part. But, I am thinking more in terms of group messaging. It seems bit too much of work to check message acknowledgement for each users within a group. I am not saying it's hard or not doable, but feels like should be handled by the centrifugo. It would be nice if centrifugo has some kinds of plugin handler or hook which can be used by developers to customize behaviors like these.

FZambia commented 7 years ago

Even with groups this looks like one query to database to get all messages without acknowlegement. I would be happy if Centrifugo can make this easier but I just dont see how. Do you have any ideas? What kind of hook you mean?

narup commented 7 years ago

I know what you mean, It's just that I am not very comfortable with maintaining status for each message on a group to each user of a group, at least for my use case. By hook, I mean some kind of callback URL that centrifugo can call on app's backend side for different events that could be interesting for app logic. User not in session is one event but there could be others. But, it means definitely more work for centrifugo :) But, for now I will try to go with your above solution you suggested. Thanks much for all the help.

FZambia commented 7 years ago

Yeah, I understand that Centrifugo does not solve everything application developers need but at the same time it does not limit final possible application functionality too much - i.e. most of missing features can be implemented on application level.

About callback url - it will be almost the same as hook from client side I described above - we will save only few lines of code that initiates HTTP request that should be written now in browser. One of the advantages of making HTTP request manually from browser (and not from Centrifugo internals) is that you get authorized request in application (by native session mechanism used on your backend).

narup commented 7 years ago

Yeah sounds good.