Skip to content

fix: send-message pending task race condition#3538

Merged
isekovanic merged 2 commits intodevelopfrom
fix/send-message-pending-task-race-condition
Apr 7, 2026
Merged

fix: send-message pending task race condition#3538
isekovanic merged 2 commits intodevelopfrom
fix/send-message-pending-task-race-condition

Conversation

@isekovanic
Copy link
Copy Markdown
Contributor

🎯 Goal

This PR resolves a very long standing race condition that has evaded me for quite a while.

Whenever we queue up send message requests while we're offline, they are to be executed when we regain connection. However, the UI SDK takes care of failed messages in the sense of re-adding them in case they weren't resolved or saved in the LLC. This is done so that when a resync happens of the channel, the state is preserved and the failed messages do not disappear.

However, since our state layer is not LLC only fully, we have 2 vectors of resyncing the SDK:

  • One that comes from the LLC (i.e channel.watch() being called)
  • One that comes from the SDK itself (re-adding those failed messages)

When send-message pending tasks are executed, the LLC takes care of upserting the state immediately so that the UI SDK can simply consume it. This is fine, except for the fact that for the UI SDK to also resolve its own state, we rely on WS events (which might be a bit late, especially for the last 1-2 messages, which is when this bug would mostly happen).

So the flow would look something like this:

  • We regain connection
  • Pending tasks are executed, channel.state.messages are updated (from the pending task execution)
  • After this, the onSyncStatusChange callback is invoked
  • The channel is actually resynced
  • channel.watch() is called
  • We also upsert all failed messages once again
    • For any message for which we did not receive a WS event of the successful pending task execution on time, both the failed message and the actual (serverside) one appear in the list

To fix this, we deem messages eligible for recovery if they:

  • are FAILED
  • are not already present in channel.state

This makes sure that the race condition never happens when the state is already up to date. If it's not, we anyway have to do it.

🛠 Implementation details

🎨 UI Changes

iOS
Before After
Android
Before After

🧪 Testing

☑️ Checklist

  • I have signed the Stream CLA (required)
  • PR targets the develop branch
  • Documentation is updated
  • New code is tested in main example apps, including all possible scenarios
    • SampleApp iOS and Android
    • Expo iOS and Android

@isekovanic isekovanic requested review from oliverlaz and szuperaz April 7, 2026 11:22
@Stream-SDK-Bot
Copy link
Copy Markdown
Contributor

Stream-SDK-Bot commented Apr 7, 2026

SDK Size

title develop branch diff status
js_bundle_size 351 KB 351 KB 0 B 🟢

@isekovanic isekovanic merged commit 5cdf7bc into develop Apr 7, 2026
5 checks passed
@isekovanic isekovanic deleted the fix/send-message-pending-task-race-condition branch April 7, 2026 11:33
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.

2 participants