Skip to content

Fix: Incorrect unread actions counts#5302

Merged
marcaaron merged 2 commits into
Expensify:mainfrom
kidroca:kidroca/maxSequenceNumber-fix
Sep 27, 2021
Merged

Fix: Incorrect unread actions counts#5302
marcaaron merged 2 commits into
Expensify:mainfrom
kidroca:kidroca/maxSequenceNumber-fix

Conversation

@kidroca

@kidroca kidroca commented Sep 16, 2021

Copy link
Copy Markdown
Contributor

Details

Include the existing maxSequenceNumber in the logic as we might already have a higher value

Fixed Issues

$ #4273

Tests

Note: the bug might have already caused to have incorrect unread notifications count stored for your account on the backed.
To ensure you're starting from a clean slate:

  1. Log out
  2. Sign in
  3. If there are any chats with unread notifications - open them to clear unread messages (don't scroll up, just open the convo)
  4. Finally log out and continue with the rest of the steps

For all platforms

  1. Be logged out
  2. Sign in
  3. Open a chat with more than 50 messages
  4. Scroll up to trigger loading older messages
  5. Switch to a different chat
  6. Switch back to the chat from 3)
  7. Switch to another chat again
  8. Refresh/Reload the app
    • Cmd + R for web/mWeb/desktop
    • Terminate and re-launch the app on iOS/Android
  9. Following these steps should not create any "unread messages" proving the fix is working
    • use the same steps on staging to see them recreate the bug

QA Steps

Follow the same tests steps as above. You can also try the rest of the discovered ways to recreate this issue - here #4273 (comment) and here #4273 (comment). These steps should no longer produce unread messages

Tested On

  • Web
  • Mobile Web
  • Desktop
  • iOS
  • Android

Screenshots

Web

Screen.Recording.2021-09-17.at.19.40.03.mov

Web: before: #4273 (comment)

Mobile Web

Screen.Recording.2021-09-17.at.19.36.38.mov

Desktop

Screen.Recording.2021-09-17.at.18.52.48.mov

Desktop before: #4273 (comment)

iOS

Screen.Recording.2021-09-17.at.19.24.15.mov

iOS before: #4273 (comment)

Android

Android.Emulator.-.Pixel_2_API_29_5554.2021-09-16.21-35-06.mp4

Android before: #4273 (comment)

Include the existing maxSequenceNumber in the logic as we might already have a higher max sequence number
@kidroca kidroca marked this pull request as ready for review September 17, 2021 16:41
@kidroca kidroca requested a review from a team as a code owner September 17, 2021 16:41
@MelvinBot MelvinBot requested review from marcaaron and removed request for a team September 17, 2021 16:41
Comment thread src/libs/actions/Report.js Outdated
const currentMaxSequenceNumber = reportMaxSequenceNumbers[reportID] || 0;
if (maxSequenceNumber > currentMaxSequenceNumber) {
Onyx.merge(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {maxSequenceNumber});
}

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.

So, this change makes sense to me on the one hand because I'd expect things to get very screwed up when we call Report_GetHistory with paginated results. Since what comes back can be historical it possibly won't be the "max sequence number we have" so it makes sense to compare it with some previously fetched result. Great job identifying that.

On the other hand, it really feels like if Report_GetHistory can return some set of items that may or may not contain the "maxSequenceNumber" then maybe we should not be relying on Report_GetHistory at all to derive the "maxSequenceNumber".

It feels like it could be possible (although I haven't verified this) that we might:

  1. have no local reportMaxSequenceNumbers[reportID] for a given report
  2. then we get some older paginated history from this API
  3. then we would set the max to the highest returned (which is possibly not the "true max")

This still feels wrong to me and I wanted to propose that maybe instead of setting the max when fetching history we should always use the reportSummaryList to do it. Hard to see from this without inspecting network requests, but we have a reportActionCount that is returned when we get the reportSummaryList here:

maxSequenceNumber: lodashGet(report, 'reportActionCount', 0),

That said, I don't know if it's a perfect solution since we'd have to add a network request next to some places where we are fetching the report history. Gonna tag in @tgolen for his opinion on this.

@kidroca kidroca Sep 17, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Great observations @marcaaron
From my experience paging API's would usually respond with the total amount of items anytime you fetch a page
If that's added to the response we can just use it here

It feels like it could be possible (although I haven't verified this) that we might:

  1. have no local reportMaxSequenceNumbers[reportID] for a given report
  2. then we get some older paginated history from this API

I thought of that as well and did an investigation - it doesn't look likely, at least I couldn't find a way. fetchActions is called (with offset) only through ReportActionView paging and here:

_.each(reportIDsToFetchActions, (reportID) => {
const offset = dangerouslyGetReportActionsMaxSequenceNumber(reportID, false);
fetchActions(reportID, offset);
});

Since the above code runs in a timer I think we'll have a reportActionCount as you've pointed out is fetched with the reportSummaryList

The other time would be when a new report comes from pusher, but this also triggers a call that should fetch the correct data

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 wanted to propose that maybe instead of setting the max when fetching history we should always use the reportSummaryList to do it

As I was reading through the comments, this is exactly the solution that came to my mind.

we'd have to add a network request next to some places where we are fetching the report history

This is the part that I'm not too sure about. Is this really necessary? I am just trying to think of any place where we:

  1. Haven't called reportSummaryList
  2. Are fetching the report history
  3. Need the max sequence number

From my experience paging API's would usually respond with the total amount of items anytime you fetch a page

I also agree with Kidroca here, that it's very typical that pagination APIs will return the total number of records along with the paginated results, so I'd be fine with adding it here too.

@kidroca kidroca Sep 17, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Seems we're calling fetchChatReportsByIDs which in the end updates reportActionCount for these cases:

  • during init/refresh (for all reports)
  • for new report we learn about through pusher (receiving an action from a report we don't have)
  • when we're back online (for all reports)

So it's totally possible we don't need to update max count when we fetch actions, but I haven't tested.
I think we're missing the case where we resume a suspended app e.g.

AppState.addEventListener('change', this.onVisibilityChange);

There's no code that will fetch all reports when we bring focus back to the app, though it might not be necessary. There's one listener in ReportActionsView that will refetch the current report (wrong)
AFAIK the pusher connection is dropped when the app is suspended for more than 5-10 minutes (ios), so we might have a problem there

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Is this possible: we've lost connection to pusher, so we miss some new messages, when we open a chat we always fetch the latest (no offset) so we'll fetch the new messages through http, but they will not update the maxSequenceNumber if we remove the logic

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.

This is the part that I'm not too sure about. Is this really necessary?

We might be able to get rid of it entirely. Tbh, I am missing some context for why we started setting the maxSequenceNumber when fetching the history in the first place. I think it was added in the POC stages here.

Maybe we thought fetching the actions was a good place to poll for the max sequence number and update the unreads? And since we could guarantee that it would always include the highest was safe/convenient to do this here?

So, it sounds like our current options are:

  1. Hold this PR on a backend change to return a reportActionCount with Report_GetHistory
  2. Investigate whether we can stop updating the maxSequenceNumber here entirely and no backend change would be necessary
  3. Merge this current change temporarily then follow up and do 2 and possibly 1 if we decide that we do want to set the maxSequenceNumber here.

I'm 👍 on doing 3 as long as we do a follow up after.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

IMO we should continue updating the maxSequenceNumber in fetchActions - in case it fetches (new) actions we don't have locally. I think it can still happen sometime.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We're using fetchActions as a "message source", we're using Pusher as a "message source", when we receive new messages through any of these sources we should update the maxSequenceNumber. Pusher handles it in a different way since it would only receive new messages

Setting `maxSequenceNumber` during `fetchActions` is no longer necessary
@kidroca

kidroca commented Sep 27, 2021

Copy link
Copy Markdown
Contributor Author

So, it sounds like our current options are:

  1. Hold this PR on a backend change to return a reportActionCount with Report_GetHistory
  2. Investigate whether we can stop updating the maxSequenceNumber here entirely and no backend change would be necessary
  3. Merge this current change temporarily then follow up and do 2 and possibly 1 if we decide that we do want to set the maxSequenceNumber here.

I'm 👍 on doing 3 as long as we do a follow up after.

I did an investigation regarding 2)

We're calling fetchActions in 3 places

  • after fetchAllReports
  • paging - scrolling to past messages
  • when we open a report - ReportActinosView.componentDidMount

Overall it looks safe to drop setting of maxSequenceNumber in fetchActions

  • fetchAllReports gets the reports summary so we already have the latest count saved before fetchActions is called
  • paging - we don't need to update maxSequenceNumber while scrolling to older messages
  • when we open a report - this is the only time I'm not certain could there be some discrepancy. I lean towards "not a problem" - it doesn't seem to be a problem through normal usage. I had concerns about being offline but fetchAllReports is called when we resume connectivity so that should fix the count. My only remaining concern is losing connection to pusher - clicking on a report might load newer messages but we won't update maxSequenceNumber, not sure if this is possible though.

I've pushed an update that removes maxSequenceNumber from fetchActions, tested more and didn't experience any issues

@kidroca kidroca requested review from marcaaron and tgolen September 27, 2021 12:07

@tgolen tgolen left a comment

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.

Thanks for the investigation! I agree with your conclusions and hopefully this is a step forward!

@marcaaron marcaaron merged commit 1fdfbd7 into Expensify:main Sep 27, 2021
@OSBotify

Copy link
Copy Markdown
Contributor

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to staging by @marcaaron in version: 1.1.2-9 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@OSBotify

Copy link
Copy Markdown
Contributor

🚀 Deployed to production by @timszot in version: 1.1.3-1 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

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.

4 participants