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

🐛 [RUM-4436] fix detached node memory leak on CLS #2749

Merged
merged 9 commits into from
May 16, 2024

Conversation

thomas-lebeau
Copy link
Contributor

@thomas-lebeau thomas-lebeau commented May 8, 2024

Motivation

trackCumulativeLayoutShift keep track of the LCP target Element using a sliding window of layout shift events but this window is cleared 5 minutes after the view ends.

Within these 5 minutes, many new view events might be started meaning an increase in memory consumption du to the reference to the LCP target Element kept and preventing garbage collection of these elements (and potentially large amount of descendent)

Changes

Hold onto the LCP target element using a weak reference (WeakRef) to not prevent it to be garbage-collected.

Testing

  • Local
  • Staging
  • Unit
  • End to end

I have gone over the contributing documentation.

Copy link

cit-pr-commenter bot commented May 8, 2024

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 157.60 KiB 157.55 KiB -44 B -0.03%
Logs 56.29 KiB 56.29 KiB 0 B 0.00%
Rum Slim 104.09 KiB 104.04 KiB -48 B -0.05%
Worker 25.21 KiB 25.21 KiB 0 B 0.00%
🚀 CPU Performance
Action Name Base Average Cpu Time (ms) Local Average Cpu Time (ms) 𝚫
adderror 0.030 0.038 0.008
addaction 0.014 N/A N/A
logmessage 0.005 0.005 0.000
startview 0.804 0.955 0.151
startstopsessionreplayrecording 0.762 0.836 0.074
addtiming 0.001 0.001 0.000
addglobalcontext 0.001 0.001 0.000

@thomas-lebeau thomas-lebeau force-pushed the thomas.lebeau/memory-leak branch 2 times, most recently from 381ad9e to 6d48530 Compare May 9, 2024 09:37
@thomas-lebeau thomas-lebeau changed the title 🐛 [RUM-3708] slidingSessionWindow returns selector instead of node to allow garbage collection of unused nodes after view end 🐛 [RUM-4436] slidingSessionWindow returns selector instead of node to allow garbage collection of unused nodes after view end May 9, 2024
@codecov-commenter
Copy link

codecov-commenter commented May 9, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 93.28%. Comparing base (b0d638c) to head (ca2802a).
Report is 9 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2749      +/-   ##
==========================================
+ Coverage   93.27%   93.28%   +0.01%     
==========================================
  Files         241      241              
  Lines        7034     7032       -2     
  Branches     1554     1555       +1     
==========================================
- Hits         6561     6560       -1     
+ Misses        473      472       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@thomas-lebeau thomas-lebeau marked this pull request as ready for review May 9, 2024 11:01
@thomas-lebeau thomas-lebeau requested a review from a team as a code owner May 9, 2024 11:01
Copy link
Member

@BenoitZugmeyer BenoitZugmeyer left a comment

Choose a reason for hiding this comment

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

Thought: maybe a simpler solution could be to avoid storing the target altogether.
In trackCumulativeLayoutShift, we could have something like this:

const isLargestLayoutShift = window.update(entry)
// ... then:
if (isLargestLayoutShift) {
  const clsTarget = find(
            entry.sources,
            (s): s is { node: HTMLElement } => !!s.node && isElementNode(s.node)
          )?.node;

          if (
            clsTarget &&
            // Check if the CLS target have been removed from the DOM between the time we collect the target reference and when we compute the selector
            clsTarget.isConnected
          ) {
            clsTargetSelector = getSelectorFromElement(clsTarget, configuration.actionNameAttribute)
          }
  }

}

and remove largestLayoutShiftTarget from the window target. Also we could do the same for largestLayoutShiftTime.

@thomas-lebeau
Copy link
Contributor Author

thomas-lebeau commented May 13, 2024

@BenoitZugmeyer
I think this is not possible. The problem is that we need to calculate the selector of the largest layout shift within the window and check if it's still connected. Because of this we need to keep a reference to target of the largest layout shift for as long as the window is open.

Edit: As discussed during our 1:1, the isConnected check is only relevant to calculate the selector (Because performance event are async, it's possible that the node is disconnected at the time the event is handled).
With the changes in this PR, it is now possible to report a selector instead of undefined for nodes that are responsible for the largest shift but are disconnected before the folowing shift within the same window.

@thomas-lebeau
Copy link
Contributor Author

/to-staging

@dd-devflow
Copy link

dd-devflow bot commented May 15, 2024

🚂 Branch Integration: starting soon, merge in < 9m

Commit 4ebadc8b01 will soon be integrated into staging-20.

This build is going to start soon! (estimated merge in less than 9m)

Use /to-staging -c to cancel this operation!

dd-mergequeue bot added a commit that referenced this pull request May 15, 2024
Co-authored-by: Thomas Lebeau <thomas.lebeau@datadoghq.com>
@dd-devflow
Copy link

dd-devflow bot commented May 15, 2024

🚂 Branch Integration: This commit was successfully integrated

Commit 4ebadc8b01 has been merged into staging-20 in merge commit 173bf4e0d7.

Check out the triggered pipeline on Gitlab 🦊

@amortemousque
Copy link
Contributor

amortemousque commented May 15, 2024

🔨 warning: ‏With this implementation we are computing the CSS selector lot more often:

  • For each window even if it's not a maxClsValue
  • For each shift entry if the value is superior to the precedent
    Since layout shift can be frequently triggered by some UI elements, it can have an impact on the processing.

tsconfig.base.json Outdated Show resolved Hide resolved
@thomas-lebeau thomas-lebeau changed the title 🐛 [RUM-4436] slidingSessionWindow returns selector instead of node to allow garbage collection of unused nodes after view end 🐛 [RUM-4436] fix detached node memory leak on CLS May 16, 2024
@thomas-lebeau thomas-lebeau merged commit 33159a6 into main May 16, 2024
20 checks passed
@thomas-lebeau thomas-lebeau deleted the thomas.lebeau/memory-leak branch May 16, 2024 12:43
thomas-lebeau added a commit that referenced this pull request May 16, 2024
 pm_trace_id: 34453927
 feature_branch_pipeline_id: 34453927
 source: to-staging

* commit '56e6713bc9a96f8df5a65c36773f4153d80ac34d':
  ✏️ various comment fixes (#2760)
  v5.18.0 (#2761)
  🐛 [RUM-4436] fix detached node memory leak on CLS (#2749)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants