App
App copied to clipboard
HIGH: [Reliability] [$250] Stop using `reportAction.originalMessage` or `reportAction.message.text`
Problem:
Each comment in the server database is stored in reportAction.message
in HTML form. However, for some reason lost to time, when we send that same comment out as an Onyx update, we:
- Rename the original message in the database to
reportAction.originalMessage
in the update (which is confusing) - Create a new
message
object that contains a copy of thehtml
message, as well as a strippedtext
version. - Put the
message
object inside a totally unnecessary one-element array
That means every comment is actually sent out three times (four, if you include the update to the report
object, but that's out of scope for now). This is clearly wasteful in a number of ways:
- It requires postprocessing on the server to do this extraneous operation
- It increases network bandwidth by 3x
- It increases RAM use 3x
- It increases on-device storage 3x
- It's confusing AF
Additionally, it's particularly problematic when sending Onyx updates out via UrbanAirship, which is extremely limited in the payload sizes allowed -- with anything over a certain limit just dropped quietly and never delivered, causing "gaps" in our update stream (which require more network calls to "backfill" the Onyx data on app open, which makes things slow). Basically, this was just a mistake introduced for reasons we can't remember, but that we want to undo.
Solution:
To solve this, please:
-
Phase 1 (External / clientside): Stop using
originalMessage
, start using array-message
- Update every instance of
originalMessage.html
to usemessage.html || message[0].html
- Update every instance of
message.text
to dynamically strip the text frommessage.html || message[0].html
- Update every instance of
-
Phase 2 (Internal / serverside): Once we've confirmed the client no longer uses
originalMessage
ormessage.text
:- Stop sending
originalMessage
- Start sending
message
without an array
- Stop sending
- Phase 3 (External / clientside): Remove checking for "array message"
This issue is for phase 1 and phase 3.
Upwork Automation - Do Not Edit
- Upwork Job URL: https://www.upwork.com/jobs/~01856d1399295ef760
- Upwork Job ID: 1776729925881405440
- Last Price Increase: 2024-04-06
- Automatic offers:
- cubuspl42 | Reviewer | 0
Job added to Upwork: https://www.upwork.com/jobs/~01856d1399295ef760
Triggered auto assignment to Contributor-plus team member for initial proposal review - @cubuspl42 (External
)
Hey Iβm going to investigate why the payload is so big in Onyx storage. Possibly track what change exactly caused the problem. Then either revert that piece of change or use one of suggestions by @thienlnam
Contributor details Your Expensify account email: [email protected] Upwork Profile Link: https://www.upwork.com/freelancers/~01fb7b172c01252917
π£ @maxim-grin! π£ Hey, it seems we donβt have your contributor details yet! You'll only have to do this once, and this is how we'll hire you on Upwork. Please follow these steps:
- Make sure you've read and understood the contributing guidelines.
- Get the email address used to login to your Expensify account. If you don't already have an Expensify account, create one here. If you have multiple accounts (e.g. one for testing), please use your main account email.
- Get the link to your Upwork profile. It's necessary because we only pay via Upwork. You can access it by logging in, and then clicking on your name. It'll look like this. If you don't already have an account, sign up for one here.
- Copy the format below and paste it in a comment on this issue. Replace the placeholder text with your actual details.
Format:
Contributor details
Your Expensify account email: <REPLACE EMAIL HERE>
Upwork Profile Link: <REPLACE LINK HERE>
Hey Iβm going to investigate why the payload is so big in Onyx storage. Possibly track what change exactly caused the problem. Then either revert that piece of change or use one of suggestions by @thienlnam
Contributor details Your Expensify account email: [email protected] Upwork Profile Link: https://www.upwork.com/freelancers/~01fb7b172c01252917
β Contributor details stored successfully. Thank you for contributing to Expensify!
Proposal
Please re-state the problem that we are trying to solve in this issue.
We don't need to use text
from reportAction, it would be better to use originalMessage.html.
What is the root cause of that problem?
New change.
text
from message in reportAction is being used at multiple places.
What changes do you think we should make in order to solve the problem?
We have text defined here:
https://github.com/Expensify/App/blob/f1502291a14d4e15e04249b241109f146bdb512f/src/types/onyx/ReportAction.ts#L18-L19
Example usage:
https://github.com/Expensify/App/blob/f1502291a14d4e15e04249b241109f146bdb512f/src/components/ReportActionItem/ReportPreview.tsx#L173
We can change this to:
const parser = new ExpensiMark();
const actionMessage = parser.htmlToText(action.message?.[0]?.html ?? '');
Add the above in a common method getActionMessagePostParsing
in ReportUtils.
Similarly, check all usages and update them to use the method mentioned above.
Finally, we can remove text
.
There's one instance of originalMessage?.html
in ReportUtils.ts which also needs to be updated to use the common method.
if (ReportActionsUtils.isWhisperAction(reportAction)) {
// Allow flagging welcome message whispers as they can be set by any room creator
if (report?.description && !isCurrentUserAction && isOriginalMessageHaveHtml && `getActionMessagePostParsing(reportAction)` === report.description) {
return true;
}
// Disallow flagging the rest of whisper as they are sent by us
return false;
}
Proposal
Please re-state the problem that we are trying to solve in this issue.
We want to remove the usage of text
of reportAction
in the payload sent to client side.
And also changes how we're using message.html
What is the root cause of that problem?
This is new feature request.
What changes do you think we should make in order to solve the problem?
- Define 2 common methods here, to get the report action html (with forward compatibility: message as single item), and to get the report action text dynamically by parsing the
html
:
function getReportActionHtml(reportAction: OnyxEntry<ReportAction>) {
return reportAction?.message?.html || reportAction?.message?.[0]?.html;
}
function getReportActionText(reportAction: OnyxEntry<ReportAction>) {
const html = getReportActionHtml(reportAction);
const parser = new ExpensiMark();
return html ? parser.htmlToText(html) : '';
}
-
Update every instance of originalMessage.html to use message.html || message[0].html
In here, replace reportAction?.originalMessage?.html
by getReportActionHtml(reportAction)
-
Update every instance of message.text to dynamically strip the text from message.html || message[0].html
There're some places that are using message?.[0]?.text
, examples: here, here, here, ... (There're more)
We can replace them by getReportActionText(reportAction)
.
-
Phase 3 (External / clientside): Remove checking for "array message"
After phase 2 is done, we can just change the implementation of getReportActionHtml
to
function getReportActionHtml(reportAction: OnyxEntry<ReportAction>) {
return reportAction?.message?.html;
}
- There're other places that use
originalMessage
, but not itshtml
field, like here
We might need to replace them with getReportActionMessage(reportAction)
Where getReportActionMessage
will be the following:
function getReportActionMessage(reportAction: OnyxEntry<ReportAction>) {
return reportAction?.message || reportAction?.message?.[0];
}
To be compatible with both current format of message
(as array) and new message format later (single item). The reportAction?.message?.[0]
part can be removed after Phase 2 is done.
What alternative solutions did you explore? (Optional)
NA
@ShridharGoel
Similarly, check all usages and update them
Are you saying to literally repeat this code snippet in all places? Couldn't we refactor the code to avoid this repetition?
As the scope of the issue is well-defined and rather narrow, I believe this part of the plan can be a part of the proposal.
@nkdengineer
Aren't you quoting browser-specific local notifications code? π€ As I understand it, the ultimate goal is to optimize the payload size of remote push notifications.
Aren't you quoting browser-specific local notifications code? π€ As I understand it, the ultimate goal is to optimize the payload size of remote push notifications.
@cubuspl42 In the OP it's mentioned
@thienlnam thinks we added
text
for notifications
So I'm quoting the place that we're showing notification based on text
, so it's clear where it's used.
It's only 1 part of the proposal though, what do you think about the rest of my proposal?
Are you saying to literally repeat this code snippet in all places? Couldn't we refactor the code to avoid this repetition?
@cubuspl42 My alternative solution achieves this
@quinthar
Would you summarize the Expensify notifications architecture in a few sentences so that we can be more confident with proposal preparation and review?
As I understand that most (if not all?) notification processing on mobile Native is server-side (push-based), and we're talking about optimizing the pushed payload size. But I'm not 100% confident how that relates to the Onyx schema.
Also, there were some doubts about the scope...
I'm assuming here that we're only omitting the text from reportActions in urban airship native updates, if we're going to remove text in Pusher update too, then we need to populate the text in the Pusher report actions updates by htmlToText.
Would you clarify this too? My intuition was that we wanted to nuke all usage of the text
property client-side, also removing it from the typing. But it would be good to be sure about it.
Great question. I've updated the original post with a more detailed description of the problem and solutions. Thanks!
Updated the proposal again (sorry!!!) in light of message
being in this weird one-element array. Thanks for your understanding and help in cleaning up this mess!
Proposal updated after latest requirements update
@nkdengineer I think your proposal shows more preparation. How is your capacity, though? Would you be able to start working on this within 24-48 hours?
Would you be able to start working on this within 24-48 hours?
@cubuspl42 Yes, I definitely can
Triggered auto assignment to @thienlnam, see https://stackoverflow.com/c/expensify/questions/7972 for more details.
π£ @cubuspl42 π An offer has been automatically sent to your Upwork account for the Reviewer role π Thanks for contributing to the Expensify app!
π£ @nkdengineer You have been assigned to this job! Please apply to the Upwork job and leave a comment on the Github issue letting us know when we can expect a PR to be ready for review π§βπ» Once you apply to this job, your Upwork ID will be stored and you will be automatically hired for future jobs! Keep in mind: Code of Conduct | Contributing π
@thienlnam Hi! I see this is a HIGH
priority issue so it should start at $500 (from the announcement here)
While bugs/feature requests will now start at $250, any that are deemed Critical or High to our roadmap (noted via the label
High Priority
) will automatically be updated to $500. This is to ensure we get the necessary attention and urgency applied to these issues.
Perhaps the automation did not work well in this case.
If that's right, could you help to adjust the bounty here, thanks π
Hm, it's likely because the issue is missing the "High priority" label. But I'm not sure if the "HIGH:" prefix and the "High priority" label represent the same concept π€
We already have a draft PR here to fix things in phase 1, @thienlnam please let me know when phase 2 is completed on the server side, I will then update phase 3 accordingly. Cc @cubuspl42
Great work! What's your ETA for submitting the PR for review?
I will open PR tomorrow
@nkdengineer Just so we're on the same page, phase 1 must be deployed to production first. Also, some time has to pass, so Native users have the time to update. So we're not blocked by phase 2 in any way.
@cubuspl42 The PR is here.
Great! @cubuspl42 what is your ETA for review?