Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: React Query Events seen and other optimizations #10588

Conversation

bnussman-akamai
Copy link
Member

Description 📝

  • Fixes cache not updating when marking events as seen 🔧
  • Fixes possible duplicate event bug on Linode Details page activity feed 🔧
  • Optimizes Events Polling System ⏲️
    • Cuts down the number of fetches to /v4/events on page load
    • Only calls POST /v4/account/events/:id/seen when needed (previously it would make this POST every time the Event Menu was closed even if the event was already seen)
    • Excludes profile_update events from polling because we don't use them

Preview 📷

Screen.Recording.2024-06-17.at.5.40.57.PM.mov

How to test 🧪

Prerequisites

  • You need to test with both Events v2 on and off. This PR affects both.

Verification steps

  • Verify there are no regressions with events polling and all of the items in the description are true

As an Author I have considered 🤔

  • 👀 Doing a self review
  • ❔ Our contribution guidelines
  • 🤏 Splitting feature into small PRs
  • ➕ Adding a changeset
  • 🧪 Providing/Improving test coverage
  • 🔐 Removing all sensitive information from the code and PR description
  • 🚩 Using a feature flag to protect the release
  • 👣 Providing comprehensive reproduction steps
  • 📑 Providing or updating our documentation
  • 🕛 Scheduling a pair reviewing session
  • 📱 Providing mobile support
  • ♿ Providing accessibility support

@bnussman-akamai bnussman-akamai added the React Query Relating to the transition to use React Query label Jun 17, 2024
@bnussman-akamai bnussman-akamai self-assigned this Jun 17, 2024
@bnussman-akamai bnussman-akamai requested a review from a team as a code owner June 17, 2024 21:44
@bnussman-akamai bnussman-akamai requested review from dwiley-akamai and cpathipa and removed request for a team June 17, 2024 21:44
@@ -32,7 +32,7 @@ export const EventsLanding = (props: Props) => {
const { emptyMessage, entityId } = props;
const flags = useFlags();

const filter = EVENTS_LIST_FILTER;
const filter = { ...EVENTS_LIST_FILTER };
Copy link
Member Author

Choose a reason for hiding this comment

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

I was having issues with EVENTS_LIST_FILTER getting mutated and causing issues elsewhere.

Copy link
Contributor

Choose a reason for hiding this comment

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

How is it getting mutated??

Copy link
Member Author

Choose a reason for hiding this comment

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

3 lines down we do

filter['entity.id'] = entityId;

Copy link
Member Author

Choose a reason for hiding this comment

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

I think this is a bug caused by introducing EVENTS_LIST_FILTER. I'm seeing the issue in develop.

Steps to reproduce:

  • Go to a Linode details page
  • Go to the Activity Feed tab
  • Observe the Events Menu is broken

Maybe we can just Object.freeze EVENTS_LIST_FILTER?

Copy link
Contributor

Choose a reason for hiding this comment

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

You're right. am thinking doing

let filter = { ...EVENTS_LIST_FILTER };
if (entityId) {
  filter = {
    ...filter,
    'entity.id': entityId,
    'entity.type': 'linode',
  };
}

would be fine, but it won't prevent people from doing that mistake in the future. not sure we can eslint that either. So either that or we don't do this through a sharable const

Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we have object constants in other places in the app? Maybe a comment would suffice? Otherwise, +1 for Object.freeze, it's also friendly with Typescript.

Copy link
Contributor

Choose a reason for hiding this comment

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

A code comment re: the mutation danger and why we're doing this is warranted

Copy link
Member Author

Choose a reason for hiding this comment

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

So either that or we don't do this through a sharable const

I like the idea of not using a shareable const, but do others object to that?

For now I pushed a comment + Object.freeze

(notificationItem) => notificationItem.showProgress
);

const showInProgressEventIcon = events?.some(isInProgressEvent);
Copy link
Member Author

Choose a reason for hiding this comment

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

This might not be what we want. I think previously it is being determined by the factory result

Comment on lines +69 to +70
if (events && events.length >= 1 && !events[0].seen) {
markEventsAsSeen(events[0].id);
Copy link
Member Author

Choose a reason for hiding this comment

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

Only make the POST if the event is not already seen 🧹

Comment on lines +133 to +149
<Box>
<Box display="flex" justifyContent="space-between" px={2}>
<Typography variant="h3">Events</Typography>
<LinkButton
onClick={() => {
history.push('/events');
handleClose();
}}
>
View all events
</LinkButton>
</Box>
<Divider spacingBottom={0} />
{data?.pages[0].data.slice(0, 20).map((event) => (
<RenderEventV2 event={event} key={event.id} onClose={handleClose} />
))}
</Box>
Copy link
Member Author

Choose a reason for hiding this comment

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

This might be too soon, but just let me know @abailly-akamai . Feels much better from a simplicity standpoint. Needs an empty state and maybe other things..

I went this route because I needed to check events[0].seen above and eventNotifications doesn't have seen exposed. I can revert this is you want to handle this separately

Copy link
Contributor

Choose a reason for hiding this comment

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

Chances are NotificationMenuV2 won't remain like this after the cleanup/refactor and will be further refactored. This is a fine first step 👍

Copy link
Contributor

Choose a reason for hiding this comment

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

Should we at least move this Box into a new component that receives events?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm down unless @abailly-akamai would rather save that for an upcoming PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll take care of it, no worries. This is still very much in development - I selfishly want to do the cleanup cause it will be so very healing 🤣

return filter;
return {
...filter,
...EVENTS_LIST_FILTER,
Copy link
Member Author

Choose a reason for hiding this comment

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

This excludes profile_update events when polling because we never use it. This just results in less data over the wire and less cache updates

Copy link
Contributor

Choose a reason for hiding this comment

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

I am also on the fence about this. On one end we get less cache updates, but those are super cheap. On the other we know how often profile_update is being called so there's value. Is garbage collection a factor here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, I think profile_update never gets written to the cache so the only benefit here would be less data over the wire

Copy link
Contributor

Choose a reason for hiding this comment

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

I think excluding it is fine if we never use it

Comment on lines +128 to +129
'+order': 'desc',
'+order_by': 'id',
Copy link
Member Author

Choose a reason for hiding this comment

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

Just being explicit about what order we want from the API to prevent unexpected breakages

Comment on lines +49 to +50
'+order': 'desc',
'+order_by': 'id',
Copy link
Member Author

Choose a reason for hiding this comment

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

Just being explicit about what order we want from the API to prevent unexpected breakages

Comment on lines +210 to +211
queryClient.setQueriesData<InfiniteData<ResourcePage<Event>>>(
['events', 'infinite'],
Copy link
Member Author

Choose a reason for hiding this comment

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

This is the primary fix for the seen issue. This now updates all infinite stores rather than just ['events', 'infinite', undefined]

@bnussman-akamai bnussman-akamai changed the title fix: React Query Events Polling fix: React Query Events seen and other optimizations Jun 17, 2024
Copy link

github-actions bot commented Jun 18, 2024

Coverage Report:
Base Coverage: 82.85%
Current Coverage: 82.85%

Copy link
Contributor

@hkhalil-akamai hkhalil-akamai left a comment

Choose a reason for hiding this comment

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

seen bug has been resolved. Tested and found no regressions.

const { dismissNotifications } = useDismissibleNotifications();
const { data: notifications } = useNotificationsQuery();
const formattedNotifications = useFormattedNotifications();
const eventNotifications = useEventNotificationsV2();
Copy link
Contributor

Choose a reason for hiding this comment

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

We are no longer using this hook anywhere

Comment on lines +133 to +149
<Box>
<Box display="flex" justifyContent="space-between" px={2}>
<Typography variant="h3">Events</Typography>
<LinkButton
onClick={() => {
history.push('/events');
handleClose();
}}
>
View all events
</LinkButton>
</Box>
<Divider spacingBottom={0} />
{data?.pages[0].data.slice(0, 20).map((event) => (
<RenderEventV2 event={event} key={event.id} onClose={handleClose} />
))}
</Box>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we at least move this Box into a new component that receives events?

Comment on lines +136 to +143
<LinkButton
onClick={() => {
history.push('/events');
handleClose();
}}
>
View all events
</LinkButton>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be a Link instead? Also slight visual regression, in develop this is wrapped in a <strong>

Copy link
Contributor

Choose a reason for hiding this comment

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

@hkhalil-akamai

Should we at least move this Box into a new component that receives events?

Should this be a Link instead? Also slight visual regression, in develop this is wrapped in a

I will take care of those when I clean up the whole feature once the flag goes away

@@ -32,7 +32,7 @@ export const EventsLanding = (props: Props) => {
const { emptyMessage, entityId } = props;
const flags = useFlags();

const filter = EVENTS_LIST_FILTER;
const filter = { ...EVENTS_LIST_FILTER };
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't we have object constants in other places in the app? Maybe a comment would suffice? Otherwise, +1 for Object.freeze, it's also friendly with Typescript.

Copy link
Contributor

@dwiley-akamai dwiley-akamai left a comment

Choose a reason for hiding this comment

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

Functionality and optimizations look good with no regressions observed ✅

return filter;
return {
...filter,
...EVENTS_LIST_FILTER,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think excluding it is fine if we never use it

@@ -32,7 +32,7 @@ export const EventsLanding = (props: Props) => {
const { emptyMessage, entityId } = props;
const flags = useFlags();

const filter = EVENTS_LIST_FILTER;
const filter = { ...EVENTS_LIST_FILTER };
Copy link
Contributor

Choose a reason for hiding this comment

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

A code comment re: the mutation danger and why we're doing this is warranted

@abailly-akamai abailly-akamai self-requested a review June 18, 2024 17:00
@bnussman-akamai bnussman-akamai merged commit 9939b41 into linode:develop Jun 18, 2024
17 of 18 checks passed
nikhagra-akamai pushed a commit to nikhagra-akamai/manager that referenced this pull request Jun 19, 2024
* initial work

* don't poll for `profile_update` events

* fixes

* update unit test

* freeze constant, add comment, delete unused code

* Added changeset: React Query Events `seen` behavior and other optimizations

---------

Co-authored-by: Banks Nussman <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
React Query Relating to the transition to use React Query
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants