refactor: [M3-6570] - RQ-ify Events - Part 1: NotificationMenu, EventsLanding#9046
Conversation
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
5 flaky tests on run #3978 ↗︎Details:
|
|||||||||||||||||||||||||||
| Test | Artifacts | |
|---|---|---|
| update firewall > updates a firewall's linodes and rules |
Output
Screenshots
Video
|
|
linodes/rescue-linode.spec.ts • 1 flaky test
| Test | Artifacts | |
|---|---|---|
| Rescue Linodes > Cannot reboot a provisioning Linode into rescue mode |
Output
Screenshots
Video
|
|
This comment has been generated by cypress-bot as a result of this project's GitHub integration settings.
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
There was a problem hiding this comment.
I think it would be cleaner for this logic to live in its own function or in the onSuccess function of useEventsPolling. I don't think useEvents should necessarily be dependent on useEventsPolling like it is right now.
There was a problem hiding this comment.
Do you anticipate any issues with the dependency? I think composing the hooks like this de-deduplicates some code.
There was a problem hiding this comment.
Because useEventsPolling is just a wrapper over useQuery, I think for the sake of maintainability, we should try to keep this wrapper as "React Query"-like as possible. I think it would be good if we keep the param here of type UseQueryOptions. If you think (event: Event) => void is truly better, we can keep it, but I generally like to keep things as "React Query"-like as possible.
There was a problem hiding this comment.
I am more in favor of keeping the signature and usage of this hook as simple as possible. Even though it uses RQ under the hook, it's not really relevant to components that use it.
There was a problem hiding this comment.
This is done to ensure the callback is called in chronological order; by default, events are sorted with the most recent events coming first.
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
a67f848 to
8a111c0
Compare
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Fixes notification menu button background color disappearing on hover in dark mode Fix Notification menu doesn't close when navigating to EventsLanding Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
bnussman-akamai
left a comment
There was a problem hiding this comment.
Things are shaping up nicely. Awesome job so far.
| const events = await requestUnreadEvents(filter); | ||
| await markCompletedEventsAsRead(events); | ||
| setIntervalMultiplier(Math.min(intervalMultiplier + 1, 16)); | ||
| return events; |
There was a problem hiding this comment.
Also, I'm wondering if await markCompletedEventsAsRead(events); should be moved into the onSuccess. I think that would be more appropriate because we don't want this query to report an error if markCompletedEventsAsRead throws an error
There was a problem hiding this comment.
This was how I did it initially but the onSuccess callback is executed multiple times if there are multiple instances of this hook mounted. Having it in the actual request avoids this. I investigated alternatives to onSuccess that only execute once but I don't think RQ has that option.
| const events = await requestUnreadEvents(filter); | ||
| await markCompletedEventsAsRead(events); |
There was a problem hiding this comment.
Testing on my person account with thousands of unread events caused events to be continuously marked as read every time the internal runs.
This isn't that bad because it does all of this pretty gracefully but I worry we will run into a rate limit or hit the API pretty hard.
I think we talked about this before and a possible solution may be to add a { created: { '+gte': timestampMountedAt } }
There was a problem hiding this comment.
Capturing our discussion here for future reference:
I implemented this idea but @bnussman-akamai pointed out that filtering out events that occur before the hook was mounted would ignore previous in-progress events.
Possible ways to mitigate this include: modifying the API to allow filtering on events' status and percent_complete fields or the updated field.
The current working solution to this is that the useEventsInfiniteQuery hook will report in-progress events to the useEventsPolling hook to poll separately from new events.
There was a problem hiding this comment.
makes since but I worry about multiple sources of polling. I'm starting to lean towards one more API change to solve the edge case of capturing in-progress events created before mount.
There was a problem hiding this comment.
Another option is to fix filtering not working when 'read' is used within '+and' and '+or'. Then, we could use a single polling hook and OR the two conditions together -- this would be more in line with what we had before.
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
Co-authored-by: Hussain Khalil <hussain@sanpilot.co>
There was a problem hiding this comment.
If you're reviewing the diff, I would recommend starting at queries/events.ts to see the new hooks and how they work.
dwiley-akamai
left a comment
There was a problem hiding this comment.
LGTM 🎉
The only issue I'm seeing right now is when you power off a Linode, the percentage starts at 100% before adjusting to 0% when it first begins. I think this popped up on another PR recently but I don't see it in prod or develop, so merging the latest develop into the feature branch after this PR gets merged should probably solve that.
This fix was in the v1.93.1 hotfix PR. Also fixed in that PR was the regression where clicking on a secondary status (e.g. Migrating (0%)) on Linodes Landing or Linodes Detail didn't open the notification menu. We'll just want to make sure that fix is still looking good once develop is merged into this feature branch. One thing I noticed on this branch but not prod or develop:
When a linode is cloned, once the event is complete, the linode we're cloning from has a secondary status of Cloning (100%) that doesn't go away on the Linode Details page. I think this is most likely a bug we introduced and then fixed in the v1.93.0 (re)release process, but I hadn't noticed it before this branch, so wanted to note it. |
|
@dwiley-akamai @mjac0bs merging in the latest develop seems to fix most of issues we were seeing. |
mjac0bs
left a comment
There was a problem hiding this comment.
Tested notification functionality in various scenarios and pages and observed all the things you noted in the steps to test. Also confirmed that the issues we were seeing before (starting at 100% to power on/off, Cloning stuck at 100%, and the secondary status not opening the menu) were all resolved once develop was merged in.
Looks like you have a few other comments on the code to circle back to, but I'm going to go ahead and approve this. Thanks for your clear PR description, that was great!
| showMore={{ | ||
| target: '/events', | ||
| text: 'View all events', | ||
| onClick: notificationContext.closeMenu, |
| }, | ||
| }), | ||
| })); | ||
|
|
There was a problem hiding this comment.
I was trying to figure out what the inconsistency was here, but couldn't seem to spot it. 🔍 I'll trust that it's fixed and this wasn't meant to address the blue button background in dark mode, which we do still need to fix but have a ticket for.
* add `fs` import * use cleaner import --------- Co-authored-by: Banks Nussman <banks@nussman.us>
* move styles to theme and update stories * remove incorrect color --------- Co-authored-by: Banks Nussman <banks@nussman.us>
## Description 📝 Migrate styles for the Tag component ## How to test 🧪 Confirm that there have been no Tag regressions across the app Co-authored-by: Jaalah Ramos <jaalah.ramos@gmail.com>
…l, Tags, AddTag` (linode#9186) ## Description 📝 Migrate styles for the TagCell and AddTag component Update Tags component to match our current patterns ## How to test 🧪 - Check tags around the app for regressions - Ensure the Tags story renders in Storybook
|
I think I discovered the cause for the failing test ("uploads machine image, mock expired upload event"): This test intercepts the getEvents call, but since these calls are duplicated for the time being (see here for why), it intercepts the wrong call 50% of the time. I'm going ahead with the merge since this failure should be addressed in part 2 when I remove the previous events polling. |
* refactor: [M3-6570] - RQ-ify Events - Part 1: NotificationMenu, EventsLanding (#9046) * Initial idea for RQ events Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Initial working version of useEventsPolling hook Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Re-implementation of useEventsPolling hook Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Completed initial implementation of useEvents hook Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Invalidate events cache when new events are fetched Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Use read instead of seen flag for polling Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Implement polling interval Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Switch to RQ hook for notifications menu Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Switch EventsLanding to RQ hook and enhance top-menu Fixes notification menu button background color disappearing on hover in dark mode Fix Notification menu doesn't close when navigating to EventsLanding Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Implement markEventsAsSeen mutation Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Temporarily re-enable redux polling Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Track previously existing in-progress events Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Remove old test Co-authored-by: Hussain Khalil <hussain@sanpilot.co> * Move NotificationMenu styled component to dedicated file * Follow style guide in EventsLanding * Rename TopMenuIconWrapper * fix: Re-add `fs` import to Cypress config (#9199) * add `fs` import * use cleaner import --------- Co-authored-by: Banks Nussman <banks@nussman.us> * refactor: [M3-6382] - MUI v5 - `Components > Radio` (#9185) * move styles to theme and update stories * remove incorrect color --------- Co-authored-by: Banks Nussman <banks@nussman.us> * refactor: [M3-6411] - MUI v5 - `Components > Tag` (#9164) ## Description 📝 Migrate styles for the Tag component ## How to test 🧪 Confirm that there have been no Tag regressions across the app Co-authored-by: Jaalah Ramos <jaalah.ramos@gmail.com> * refactor: [M3-6412, M3-6413, M3-6650] - MUI v5 - `Components > TagCell, Tags, AddTag` (#9186) ## Description 📝 Migrate styles for the TagCell and AddTag component Update Tags component to match our current patterns ## How to test 🧪 - Check tags around the app for regressions - Ensure the Tags story renders in Storybook --------- Co-authored-by: Hussain Khalil <hussain@sanpilot.co> Co-authored-by: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Co-authored-by: Banks Nussman <banks@nussman.us> Co-authored-by: Hana Xu <115299789+hana-linode@users.noreply.github.com> Co-authored-by: Jaalah Ramos <jaalah.ramos@gmail.com> * Undo no-longer needed changes to top menu icons * refactor: [M3-6571] - RQ-ify Events - Part 2: Event Handlers (#9293) * Make polling part of EventsInfiniteQuery * Move event utilities to utilities folder * Create events query container * Migrate App events handlers to new events hook * Migrate toast notifications to new hook * Move app event handlers to hook * Finish migrating event handlers * Remove events redux store * Fix failing test * Fix issue causing multiple pages of events to refresh * implement page zero for new events * Optimize/reduce calls to events endpoint * Implement rolling-time polling * Fix events being duplicated * Fix unsupported API filter * Fix index out of bounds bug * Fix broken in-progress events polling * Get rid of separate page zero cache and store new events in first page instead * Undo accidental changes to cachedData * remove leftover pageZero stuff * Fix undefined recent event * Start from page 1 * Feedback @jaalah-akamai * New hook for synchronizing polling intervals * Remove unused exports * Custom implementation of partition util * Sort in-progress events to top * Use events instead of data * Use React.useMemo * Fix state accidental deletion * Clean up useToastNotifications * Add changesets * Remove Redux connector in EventsLanding * Rename to NotificationMenu.styles.ts * nodebalanacer -> nodebalancer * Import from src/components instead of MUI * Replace CombinedProps with EventsLandingProps --------- Co-authored-by: Hussain Khalil <hussain@sanpilot.co> Co-authored-by: Banks Nussman <115251059+bnussman-akamai@users.noreply.github.com> Co-authored-by: Banks Nussman <banks@nussman.us> Co-authored-by: Hana Xu <115299789+hana-linode@users.noreply.github.com> Co-authored-by: Jaalah Ramos <jaalah.ramos@gmail.com>


Description 📝
Part 1 implements the RQ hooks in the Notifications menu and EventsLanding page. Part 2 will cover event handlers.
This PR migrates all Events endpoints to two React Query hooks (
useEventsInfiniteQueryfor fetching event notifications anduseEventsPollingfor registering event handlers) while hugely simplifying the current implementation.The current Redux-based events system in Cloud Manager is pretty complex, making it difficult to understand and develop. Functionality is defined across several files, including
events.ts,eventsPolling.ts,event.request.ts,event.reducer.ts,event.actions.tsandevent.helpers.ts. Event handlers are used for important app functions like snackbar/toasts and cache invalidation but are defined via two redundant mechanisms (an rxjs subject and a custom Redux middleware).The current system handles a lot of considerations and edge cases that must be accounted for:
The plan is to reduce complexity by consolidating all Events-related functionality to these two hooks, remove the singular usage of the rxjs library and leverage the API-provided
seenandreadflags on events to avoid the complicated logic needed for maintaining a local events store.How to test 🧪
Verify the notifications menu, which can be opened by clicking the bell icon, works as expected:
Verify
EventsLanding(accessed by opening the notifications menu and clicking "View all events"), works as expected:Verify Linode Activity Feed, accessed by navigating to a linode and clicking on the "Activity Feed" tab, works as expected:
EventsLandingOpen DevTools Networking tab and filter to 'events' requests. Verify the polling interval starts at once per second and backs-off to once per 16 seconds (currently, polling is duplicated since the existing Events architecture is still enabled; it will be removed in Part 2).
Verify unit and E2E tests pass.