Sorry for long typing, it helps me to clarify my approach and I hope it can help someone learning like me to move faster…
Investigating this further I found that most of “state of the art” apps like Signal are actually sending extra request from the client SDK to mark message as delivered and later as read. The count of write operations in the MongoDB can’t be really reduced as each “delivery” is marked by different user (i.e. user B can’t update delivery for user C and D…). What can be reduced - either for lower billing of back-end service or lower data traffic size - is the count of http requests.
I also think it is enough if:
- Message deletion is certain (only deleted after really delivered into device)
- “Delivered” mark is only UI info feature and can be considered unreliable
Therefore I think there are two ways how to approach this:
Split “delivery” from "fetch"
- When fetched into the device, the recipient
objectId
would be removed from ACL
and rids
with a call by SDK (.save()
, cloud code function,…) → this prevents refetching given message again (ACL
) and marks as delivered (rids
) and can happen in batch
- In
beforeSave
trigger of Message
object would check when the ACL
contains only the sender’s objectId
or nothing → then the object gets deleted
- In case the chat is currently on the screen and LiveQuery active, the
afterFind
trigger would be the place where to remove recipient from rids
→ this keeps the delivery unreliable but immediate and without http request.
This might produce smooth “User B” experience, because he sees messages delivery mark immediately, but also cause higher count of database writes (one for .rids
and one for ACL
) . I am also not sure if it is so easy to loop through ACLs in the cloud code and might not be possible for everyone (i.e. when using Role).
Set “delivery” in batches
- use Cloud function sending array of
Message.objectId
to set rids
and ACL
for all messages fetched on app start → 1 request, nothing delayed from users perspective, object fetched by recipient only once.
- delete
Message
when rids
is empty in beforeSave
trigger
- for the case of currently running chat on screen set
rids
only for that particular chat either every time new message comes or batch the response based on some criteria like:
- user A receives n messages and only sends out “delivery” response when sending his message back to user B (here can be even a
context
in .save() used instead of cloud function)
- user A sends out “delivery” response when he leaves the chat view or close app
- for all other conversations the delivery would be again in batch with a cloud function (i.e. when user enters conversation list view, app resigns active,…)
This is a trade-off of request count and delivery mark being immediate.
A sender either has always his ACL on a Message
sent by him and will fetch the message until the message gets deleted (can see delivery updates) or does not set his ACL when saving new message (he does not care about delivery status) and never re-fetch his messages.
I think the second solution is more scalable and can be further tuned. Or do I miss any simple logic here?
If anyone would share ho she/he handles deletion of delivered messages I would appreciate it! Thank you!