[Relay Protocol](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/11/relay.md) in Waku is efficient for routing messages, but there's no guarantee that a message will reach to its destination. For example, the receiver in a chat application may miss messages when network issue happens at either sender or receiver side.
In general, a message in Waku network includes 3 status from sender's perspective:
Application like Status already uses [MVDS](https://github.com/vacp2p/rfc-index/blob/main/vac/2/mvds.md) for e2e acknowledgement in direct messages and group chat. There is an ongoing [discussion](https://forum.vac.dev/t/end-to-end-reliability-for-scalable-distributed-logs/293) about a more general and bandwidth efficient solution for e2e reliablity.
In other words, an application defines a payload over Waku and is interested in e2e delivery between application users. Waku provides a pub/sub broadcast transport, which is interested in reliably routing a message to all participants in the pub/sub broadcast group.
Before we have a complete design for e2e reliability, we need to compose existing protocols to increase the reliability of the relay protocol. This document proposes a few options for such composition. The approch proposed here can also be applied to scenarios which depend on [Light Push](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/19/lightpush.md) and [Filter](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/12/filter.md) protocol, since they are service wrappers of Relay protocol.
The [Store protocol](https://github.com/waku-org/specs/blob/master/standards/core/store.md) provides a way for nodes in the network to query the existence of messages or fetch specific messages based on the search criteria.
For the nodes that may have connection issues to **publish** messages via relay network, this search criteria can be used to check whether a message is populated in the network or not. The message exists in store node can be marked from `outgoing` to `sent` by application. If the message is not found in the store node, the application can resend the message.
For the nodes that may have connection issues to **receive** messages via relay network, this search criteria can be used to fetch missing messages from store nodes periodically after network resumes.
In summary, by leveraging the store node to provide such query services, the applications are able to mitigate the reliability issue of relay protocol.
But this approach also introduces new challenges like centralization, privacy, and scalability. It should be viewed as a temporary solution and deprecated when e2e reliability solution is ready.
For outgoing messages, the processing flow can be like this:
- create a buffer for all "outgoing" messages
- send message via relay or lightpush protocol
- add message hash to the buffer
- save the message to local database with status "outgoing"
- check the buffer periodically
- query the store node with message hash in the buffer, the messages should be posted more than a few seconds ago
- if the message exists, update the status to "sent" and remove the message hash from the buffer
- if the message does not exist, resend the message
- if the message is still missing in the store node for a period of time, trigger the message failed to send workflow and remove the message hash from the buffer
Function `checkOutgoingMessages` is called periodically, most likely every a few seconds. Message hashes can be queried in batch to reduce the number of requests to store nodes, the size in a batch shoud not exceed the max supported size by store node.
The store node can be set and updated directly by application or selected from peers which are discovery by protocols like [discv5](https://github.com/vacp2p/rfc-index/blob/main/waku/standards/core/33/discv5.md) or [peer exchange](https://github.com/waku-org/specs/blob/master/standards/core/peer-exchange.md).
When persistent network issue happens, you may not want to resend the failed messages indefinitely, the application should have a mechanism to clean the cache with failed message hashes and trigger other retry logic after a few attempts.
An application could use different pubsub topics and content topics, for example a community may have its own pubsub topic, and each channel may have its own content topic. To fetch all missing messages in a specific channel, the application can query the store node with the provided pubsub topic, content topic and time range.
`QueryParam` includes all the necessary information to fetch missing messages. The application should iterate all the interested pubsub topics, along with its content topics to construct the `QueryParam`.
Function `fetchMissingMessages` is runing periocally, for example 1 minute. It first fetch all the message hashes in the specified time range, check if message exist in local dabatase, if not, fetch the missing messages in batch. The batch size should be bounded to avoid large data transfer or exceed the max supported size by store node.
When finishing fetching missing messages, the application should update the last fetch time in `FetchRecord`. The last fetch time can be used to calculate the time range for the next fetch and avoid fetching the same messages again.
There are cases that both outgoing and incoming messages are queried in similar situation, for example at similar interval. The application can combine the above two worflows into one to have a unified query with better performance overall.
The workflow can be like this:
- create outgoing buffer for all "outgoing" messages
- create incoming buffer for all recently received message hashes
- query store node based on topics and time range for message hashes periodically
- check outgoing buffer with returned message hash, if included, mark message as `sent`, resend if needed.
- check incoming buffer with returned message hash, if not included, fetch the missing message with its hash.
The message query request exposes the metadata of clients to the store nodes, and the store node is capable to associate the messages with interested clents.
The query requests add a fair amount of load to store nodes, and increased linearly with more users onboarded. Store nodes should be able to scale up and scale down itself by monitoring or predicting the workload.
Application should provide options to configure different store nodes for its users, such nodes can either be self-hosted or public with better reputation.