Skip to content

feat(sds): recover missed messages using SDS#7378

Open
jrainville wants to merge 3 commits into
developfrom
feat/recover-missed-messages-with-sds
Open

feat(sds): recover missed messages using SDS#7378
jrainville wants to merge 3 commits into
developfrom
feat/recover-missed-messages-with-sds

Conversation

@jrainville

@jrainville jrainville commented Mar 18, 2026

Copy link
Copy Markdown
Member

Fixes #7363

Companion PR: status-im/status-app#21455
See this PR for a demo ^

Based on top of #7613

Enables the SDS wrapping flag.

Sets up the handler that wraps SDS messages with retrieval hints. Those hints are the envelope IDs of the messages that were sent and received

Sets up the unwrapping and fetching when there are missed messages detected.

Adds a new function that enables fetching per envelope ID instead than per topic.

Also checks the cache before fetching, to avoid fetching messages we already know about.

@github-actions

github-actions Bot commented Mar 18, 2026

Copy link
Copy Markdown

⚠️ Companion PR Needs Update

#21455 is not using the latest status-go commit (e48cbc70e510377a2e96ae186c198f993563e9c3).

Update vendor/status-go in the companion PR.

@status-im-auto

status-im-auto commented Mar 18, 2026

Copy link
Copy Markdown
Member

Jenkins Builds

Click to see older builds (90)
Commit #️⃣ Finished (UTC) Duration Platform Result
✔️ 63b799f 1 2026-03-18 17:25:14 ~4 min macos/status-go 📦zip
✔️ 63b799f 1 2026-03-18 17:25:29 ~4 min linux/status-go 📦zip
✖️ 63b799f 1 2026-03-18 17:27:35 ~6 min tests 📄log
✔️ 63b799f 1 2026-03-18 17:29:04 ~8 min linux/nwaku 📦zip
✔️ 63b799f 1 2026-03-18 17:29:09 ~8 min macos/nwaku 📦zip
✔️ 63b799f 1 2026-03-18 17:31:31 ~10 min windows/status-go 📦zip
✔️ 63b799f 1 2026-03-18 17:36:57 ~15 min windows/nwaku 📦zip
✔️ 63b799f 1 2026-03-18 17:42:19 ~21 min tests-rpc 📄log
✔️ 63b799f 1 2026-03-18 17:42:24 ~21 min tests-rpc 📄log
✖️ 63b799f 1 2026-03-18 18:03:31 ~42 min tests 📄log
9e0bebe 2 2026-03-19 17:15:15 ~2 min linux/status-go 📄log
✖️ 9e0bebe 2 2026-03-19 17:16:19 ~3 min tests 📄log
✖️ 9e0bebe 2 2026-03-19 17:16:19 ~3 min tests 📄log
9e0bebe 2 2026-03-19 17:18:19 ~5 min macos/status-go 📄log
9e0bebe 2 2026-03-19 17:19:10 ~6 min linux/nwaku 📄log
9e0bebe 2 2026-03-19 17:22:23 ~9 min macos/nwaku 📄log
✔️ 9e0bebe 2 2026-03-19 17:23:29 ~10 min windows/status-go 📦zip
✔️ 9e0bebe 2 2026-03-19 17:28:50 ~15 min windows/nwaku 📦zip
✔️ 9e0bebe 2 2026-03-19 17:30:01 ~16 min tests-rpc 📄log
✔️ 9e0bebe 2 2026-03-19 17:33:22 ~20 min tests-rpc 📄log
2b8230a 3 2026-07-06 19:58:32 ~2 min macos/status-go 📄log
✖️ 2b8230a 3 2026-07-06 19:58:38 ~2 min tests 📄log
2b8230a 3 2026-07-06 19:58:42 ~2 min linux/status-go 📄log
✖️ 2b8230a 1 2026-07-06 19:58:59 ~3 min tests-rpc-compat 📄log
2b8230a 3 2026-07-06 20:02:03 ~5 min windows/status-go 📄log
✖️ 2b8230a 3 2026-07-06 20:02:11 ~6 min tests-rpc 📄log
✔️ 0750286 4 2026-07-09 15:49:16 ~5 min linux/status-go 📦zip
✖️ 0750286 4 2026-07-09 15:50:37 ~6 min tests 📄log
✔️ 0750286 4 2026-07-09 15:51:52 ~7 min macos/status-go 📦zip
✔️ 0750286 4 2026-07-09 15:52:16 ~8 min windows/status-go 📦zip
✖️ 0750286 4 2026-07-09 16:04:12 ~20 min tests-rpc 📄log
✔️ 0750286 2 2026-07-09 16:15:46 ~31 min tests-rpc-compat 📄log
✔️ aee42ce 5 2026-07-09 18:35:28 ~3 min linux/status-go 📦zip
✔️ aee42ce 5 2026-07-09 18:35:46 ~4 min macos/status-go 📦zip
✖️ aee42ce 5 2026-07-09 18:37:54 ~6 min tests 📄log
✔️ aee42ce 5 2026-07-09 18:39:30 ~7 min windows/status-go 📦zip
✖️ aee42ce 5 2026-07-09 18:56:39 ~24 min tests-rpc 📄log
✔️ aee42ce 3 2026-07-09 19:19:46 ~48 min tests-rpc-compat 📄log
✔️ 746e5d5 6 2026-07-09 20:08:44 ~4 min macos/status-go 📦zip
✖️ 746e5d5 6 2026-07-09 20:10:24 ~5 min tests 📄log
✔️ 746e5d5 6 2026-07-09 20:10:28 ~6 min linux/status-go 📦zip
✔️ 746e5d5 6 2026-07-09 20:13:20 ~8 min windows/status-go 📦zip
✖️ 746e5d5 6 2026-07-09 20:30:11 ~25 min tests-rpc 📄log
✔️ 746e5d5 4 2026-07-09 20:36:59 ~32 min tests-rpc-compat 📄log
✔️ c8406d8 7 2026-07-09 20:13:04 ~4 min macos/status-go 📦zip
✔️ c8406d8 7 2026-07-09 20:16:08 ~5 min linux/status-go 📦zip
✖️ c8406d8 7 2026-07-09 20:18:21 ~7 min tests 📄log
✔️ b52eef0 8 2026-07-09 20:17:15 ~4 min macos/status-go 📦zip
✔️ b52eef0 8 2026-07-09 20:20:28 ~4 min linux/status-go 📦zip
✔️ b52eef0 7 2026-07-09 20:22:20 ~8 min windows/status-go 📦zip
✖️ b52eef0 8 2026-07-09 20:24:26 ~5 min tests 📄log
✔️ 2d3507c 9 2026-07-09 20:24:54 ~3 min linux/status-go 📦zip
✔️ 2d3507c 9 2026-07-09 20:25:11 ~4 min macos/status-go 📦zip
✔️ 2d3507c 8 2026-07-09 20:30:24 ~7 min windows/status-go 📦zip
✖️ 2d3507c 9 2026-07-09 20:30:48 ~6 min tests 📄log
✖️ 2d3507c 7 2026-07-09 20:51:40 ~21 min tests-rpc 📄log
✔️ 2d3507c 5 2026-07-09 21:12:51 ~35 min tests-rpc-compat 📄log
✔️ edd9a5a 10 2026-07-10 18:10:17 ~4 min macos/status-go 📦zip
✔️ edd9a5a 10 2026-07-10 18:12:14 ~6 min linux/status-go 📦zip
✔️ edd9a5a 9 2026-07-10 18:16:51 ~10 min windows/status-go 📦zip
✖️ edd9a5a 10 2026-07-10 18:17:26 ~10 min tests 📄log
✔️ edd9a5a 8 2026-07-10 18:26:52 ~20 min tests-rpc 📄log
✔️ edd9a5a 6 2026-07-10 18:44:31 ~38 min tests-rpc-compat 📄log
✔️ 39f181c 11 2026-07-10 18:48:28 ~3 min linux/status-go 📦zip
✔️ 39f181c 11 2026-07-10 18:48:45 ~4 min macos/status-go 📦zip
✔️ 39f181c 10 2026-07-10 18:52:53 ~8 min windows/status-go 📦zip
✖️ 39f181c 11 2026-07-10 18:54:21 ~9 min tests 📄log
✔️ 39f181c 9 2026-07-10 19:02:49 ~18 min tests-rpc 📄log
✔️ 39f181c 7 2026-07-10 19:21:53 ~37 min tests-rpc-compat 📄log
✔️ 28fc2cd 11 2026-07-13 15:40:54 ~8 min windows/status-go 📦zip
✖️ 28fc2cd 10 2026-07-13 15:42:47 ~9 min tests-rpc 📄log
✖️ 28fc2cd 12 2026-07-13 15:43:12 ~10 min tests 📄log
✔️ 28fc2cd 13 2026-07-13 15:46:25 ~13 min linux/status-go 📦zip
✔️ 28fc2cd 13 2026-07-13 15:49:18 ~16 min macos/status-go 📦zip
✔️ 28fc2cd 8 2026-07-13 16:21:36 ~48 min tests-rpc-compat 📄log
✔️ 28fc2cd 11 2026-07-13 16:59:13 ~18 min tests-rpc 📄log
✔️ 28fc2cd 9 2026-07-13 17:13:26 ~51 min tests-rpc-compat 📄log
✖️ bacb7e5 12 2026-07-13 18:11:03 ~1 min tests-rpc 📄log
✖️ bacb7e5 10 2026-07-13 18:11:03 ~1 min tests-rpc-compat 📄log
✖️ bacb7e5 13 2026-07-13 18:11:04 ~1 min tests 📄log
✔️ bacb7e5 14 2026-07-13 18:13:33 ~3 min linux/status-go 📦zip
✔️ bacb7e5 14 2026-07-13 18:13:46 ~4 min macos/status-go 📦zip
✔️ bacb7e5 12 2026-07-13 18:17:41 ~7 min windows/status-go 📦zip
✖️ bacb7e5 14 2026-07-13 18:54:27 ~5 min tests 📄log
✔️ 8740af7 15 2026-07-14 17:11:51 ~4 min macos/status-go 📦zip
✔️ 8740af7 15 2026-07-14 17:13:27 ~6 min linux/status-go 📦zip
✔️ 8740af7 13 2026-07-14 17:16:22 ~8 min windows/status-go 📦zip
✔️ 8740af7 15 2026-07-14 17:17:25 ~9 min tests 📄log
✔️ 8740af7 13 2026-07-14 17:25:48 ~18 min tests-rpc 📄log
✔️ 8740af7 11 2026-07-14 17:54:01 ~46 min tests-rpc-compat 📄log
Commit #️⃣ Finished (UTC) Duration Platform Result
0eefaf8 16 2026-07-14 17:17:21 ~1 min macos/status-go 📄log
✔️ 0eefaf8 16 2026-07-14 17:19:10 ~3 min linux/status-go 📦zip
✔️ 0eefaf8 14 2026-07-14 17:24:29 ~7 min windows/status-go 📦zip
✔️ 0eefaf8 16 2026-07-14 17:27:13 ~9 min tests 📄log
✖️ 0eefaf8 14 2026-07-14 17:27:15 ~1 min tests-rpc 📄log
✔️ 0eefaf8 12 2026-07-14 18:43:27 ~49 min tests-rpc-compat 📄log
✔️ e48cbc7 17 2026-07-20 19:56:18 ~4 min macos/status-go 📦zip
✔️ e48cbc7 15 2026-07-20 20:00:48 ~8 min windows/status-go 📦zip
✔️ e48cbc7 17 2026-07-20 20:02:12 ~10 min tests 📄log
✔️ e48cbc7 17 2026-07-20 20:05:26 ~13 min linux/status-go 📦zip
✔️ e48cbc7 15 2026-07-20 20:13:40 ~21 min tests-rpc 📄log
✔️ e48cbc7 13 2026-07-20 20:32:43 ~40 min tests-rpc-compat 📄log

@codecov

codecov Bot commented Mar 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 42.49201% with 180 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.19%. Comparing base (3c23ea8) to head (e48cbc7).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
pkg/messaging/core.go 16.16% 81 Missing and 2 partials ⚠️
pkg/messaging/waku/gowaku.go 0.00% 53 Missing ⚠️
pkg/messaging/controller/sender/sender_public.go 13.63% 17 Missing and 2 partials ⚠️
pkg/messaging/layers/transport/transport.go 79.24% 10 Missing and 1 partial ⚠️
pkg/messaging/layers/reliability/reliability.go 81.81% 6 Missing and 4 partials ⚠️
pkg/messaging/layers/reliability/sds.go 80.00% 3 Missing and 1 partial ⚠️

❌ Your patch check has failed because the patch coverage (42.49%) is below the target coverage (50.00%). You can increase the patch coverage or adjust the target coverage.

Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #7378      +/-   ##
===========================================
- Coverage    62.25%   62.19%   -0.06%     
===========================================
  Files          868      869       +1     
  Lines       119860   120151     +291     
===========================================
+ Hits         74621    74732     +111     
- Misses       37613    37792     +179     
- Partials      7626     7627       +1     
Flag Coverage Δ
functional 42.14% <10.22%> (-0.05%) ⬇️
unit 55.84% <42.49%> (-0.09%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
pkg/messaging/controller/processor/processor.go 81.15% <100.00%> (-0.76%) ⬇️
pkg/messaging/core_config.go 83.72% <100.00%> (+1.66%) ⬆️
pkg/messaging/layers/reliability/channel_id.go 100.00% <100.00%> (ø)
pkg/messaging/layers/reliability/sds.go 79.03% <80.00%> (+28.01%) ⬆️
pkg/messaging/layers/reliability/reliability.go 81.62% <81.81%> (-1.09%) ⬇️
pkg/messaging/layers/transport/transport.go 63.88% <79.24%> (+1.63%) ⬆️
pkg/messaging/controller/sender/sender_public.go 66.20% <13.63%> (-7.44%) ⬇️
pkg/messaging/waku/gowaku.go 66.97% <0.00%> (-3.42%) ⬇️
pkg/messaging/core.go 43.78% <16.16%> (-13.86%) ⬇️

... and 18 files with indirect coverage changes

@jrainville
jrainville force-pushed the feat/recover-missed-messages-with-sds branch 2 times, most recently from 2b8230a to 0750286 Compare July 9, 2026 15:43
@jrainville
jrainville changed the base branch from develop to bump-sds July 9, 2026 20:05
@jrainville
jrainville force-pushed the feat/recover-missed-messages-with-sds branch from b52eef0 to 2d3507c Compare July 9, 2026 20:20
@jrainville jrainville changed the title WIP recover messages using SDS feat(sds): recover missed messages using SDS Jul 9, 2026
@jrainville
jrainville marked this pull request as ready for review July 9, 2026 20:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.

Comment thread pkg/messaging/waku/gowaku.go Outdated
Comment thread pkg/messaging/controller/sender/sender_public.go Outdated
@Ivansete-status
Ivansete-status requested a review from a team as a code owner July 9, 2026 22:47
@jrainville
jrainville force-pushed the feat/recover-missed-messages-with-sds branch 2 times, most recently from edd9a5a to 39f181c Compare July 10, 2026 18:44
Base automatically changed from bump-sds to develop July 13, 2026 15:32
@jrainville
jrainville force-pushed the feat/recover-missed-messages-with-sds branch 3 times, most recently from bacb7e5 to 8740af7 Compare July 14, 2026 17:07
@jrainville
jrainville force-pushed the feat/recover-missed-messages-with-sds branch from 8740af7 to 0eefaf8 Compare July 14, 2026 17:15
@jrainville
jrainville removed the request for review from a team July 15, 2026 19:50
@jrainville

Copy link
Copy Markdown
Member Author

@caybro @noeliaSD @micieslak reminder to review please

Comment on lines +122 to +124
if len(missingDepsAsString) == 0 {
return
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this guard is duplicated as per the one at line 112?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there are no RetrievalHints, missingDeps could have length, but missingDepsAsString would be empty, so the guard has some use.

Comment on lines +112 to +113
if handler == nil || len(missingDeps) == 0 {
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think is better to have a stronger constraint in Reliability ctor that ensures a valid missingDepsHandler has ben set. Then, I'd move the handle != nil assert into NewReliability.

Suggested change
if handler == nil || len(missingDeps) == 0 {
return
if len(missingDeps) == 0 {
return


// FirstTrackedEnvelopeHash returns the first tracked envelope hash for a given
// message identifier. The returned hash is hex-encoded (0x-prefixed).
func (t *Transport) FirstTrackedEnvelopeHash(identifier []byte) (string, bool) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps is interesting to return an error so that we can provide richer feedback to the caller.

Suggested change
func (t *Transport) FirstTrackedEnvelopeHash(identifier []byte) (string, bool) {
func (t *Transport) FirstTrackedEnvelopeHash(identifier []byte) (string, error) {

sdsWrappedPayload, err := s.stack.Reliability.WrapPayloadForSDS(params.Payload, sdsChannelID)
if err != nil {
return errors.Wrap(err, "failed to wrap payload for SDS")
if strings.Contains(err.Error(), "reMessageTooLarge") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall it return the err to the caller anyways?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it returned, the message wouldn't be sent. We currently have the segmenting mechanism for that, but then with SDS wrapping failing, it failed to go to that layer.

Currently, this is not a problem in the app, it's just a regression test that caught it, but I think it makes sense to just warn and continue.

Comment thread pkg/messaging/core.go

func (c *Core) stop() error {
close(c.quit)
c.cancel()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe aside from the PR's purpose. Consider this as possible addition plus removal of the call at line 253.

Suggested change
c.cancel()
c.cancel()
defer c.wg.Wait()

)

const sdsForCommunitiesEnabled = false
const sdsForCommunitiesEnabled = true

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd suggest to enable it in a separate PR. Though not a big deal.

@igor-sirotin igor-sirotin Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you go for enabling it in this PR, please link this ticket:


s.stack.Transport.Track(messageID, hashes, wakuMessages)
if len(sdsMessageIDBytes) > 0 {
s.stack.Transport.TrackMany([][]byte{[]byte(messageID), sdsMessageIDBytes}, hashes, wakuMessages)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to track both messageID and sdsMessageIDBytes? 🤔 With SDS enabled, only 1 message will be sent.
(I feel that I'm missing something, but couldn't quickly figure out what it is)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok I guess it's needed to make RetrievalHintProvider work properly

// messages. Keep the format centralized so sender/receiver code changes in one
// place.
func BuildChannelID(communityID []byte, contentTopic string) string {
return cryptotypes.EncodeHex(communityID) + sdsChannelIDSeparator + contentTopic

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why encode ContentTopic into sds channel id? 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, the SDS context was the whole community, but we want it to be per channel.

Let me know if that's not how it should be, but it seems to be what the SDS docs recommend.

Comment thread pkg/messaging/core.go Outdated
Comment on lines 110 to 114

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we create stack.Reliability after core, and this way avoid having Set<handler> methods? We could then pass the handlers into NewReliability constructor and we won't need the mutexes on both handlers. These mutexes are unnecessarily locked on each event, despite the handlers are never replaced.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah good idea. Ivan had the same recommendation. Done in my upcoming commit

logger.Debug("send public message with SDS", zap.String("communityID", cryptotypes.EncodeHex(params.CommunityID)))
sdsWrappedPayload, err := s.stack.Reliability.WrapPayloadForSDS(params.Payload, params.CommunityID)
sdsChannelID := reliability.BuildChannelID(params.CommunityID, params.ContentTopic)
sdsMessageIDBytes = crypto.Keccak256(params.Payload)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message id is also calculated here:

sdsMessageID := crypto.Keccak256(payload)

Like a few lines after this.

I think we could return it from WrapPayloadForSDS instead.

Comment on lines +49 to +53
logger.Debug("send public message with SDS",
zap.String("communityID", cryptotypes.EncodeHex(params.CommunityID)),
zap.String("sdsChannelID", sdsChannelID),
zap.String("sdsMessageID", sdsMessageIDHex),
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logging is already done here:

r.logger.Debug("original payload wrapped with SDS",
zap.String("channelId", channelID),
zap.Int("payloadLength", len(payload)),
zap.String("messageId", cryptotypes.EncodeHex(sdsMessageID)),
)

Comment thread pkg/messaging/core.go Outdated
return nil
}

hash, ok := c.stack.Transport.FirstTrackedEnvelopeHash(decodedMessageID)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But restoring only first segment won't help 🤔
Maybe we should encode multiple hashes here? It's a pure []byte, we could put anything there in theory. It could even be a protobuf to make it changes-proof in the future

Fixes #7363

Enables the SDS wrapping flag.
Sets up the handler that wraps SDS messages with retrieval hints. Those hints are the envelope IDs of the messages that were sent and received
Sets up the unwrapping and fetching when there are missed messages detected.
Adds a new function that enables fetching per envelope ID instead than per topic.
Fixes #7363

The SDS library can detect some messages are missing by accident. Either because it didn't process them yet or just because the app was restarted.
This change checks the cache before fetching to make sure we are not fetching messages we already know.
@jrainville
jrainville force-pushed the feat/recover-missed-messages-with-sds branch from 0eefaf8 to e48cbc7 Compare July 20, 2026 19:51
@jrainville

Copy link
Copy Markdown
Member Author

@Ivansete-status @igor-sirotin thanks a lot of the comments. I added a new commit that applies them

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[SDS] fetch missing envelopes when we get an SDS signal

5 participants