Repeated node lookups against a logged-in linkedin.com/in/<account> push memory up sharply, and it does not come back down between lookups. Ordinary pages are fine; it shows on DOM-heavy, script-heavy ones.
Two things in the CDP lookup path look like they contribute.
1. DOM.getDocument on every locate
locate resolves the document root before every query:
https://github.com/dashn9/rustenium/blob/main/rustenium/src/conduit/cdp/adapter.rs#L316
let root_id = self.get_root_node_id().await?;
let cmd = QuerySelector::builder().node_id(root_id)...
Each DOM.getDocument makes Chrome's DOM agent mint and retain a fresh NodeId → backend-node mapping. That map is owned by the browser and is only dropped on document update or DOM.disable, so it grows per call and the growth is browser-side — which matches the spike persisting rather than being reclaimed when the DomNode goes out of scope on our side. locate_all and wait_for (which polls locate every 100ms) multiply it.
2. DescribeNode with depth(-1)
describe_by_id asks for the whole subtree:
https://github.com/dashn9/rustenium/blob/main/rustenium/src/conduit/cdp/adapter.rs#L305
DescribeNode::builder().node_id(node_id).depth(-1).build()
So matching a container selector on a LinkedIn profile serialises and deserialises that entire subtree into a DomNode tree, every time. Most callers want the node itself — its box, its attributes, its text — not its descendants.
Repro
- Open a logged-in
linkedin.com/in/<account>.
- Loop
locate on a container selector (or any wait_for, which polls it) a few hundred times.
- Watch RSS of the browser process and of the driving process. Both climb; neither returns to the baseline after the loop ends.
Suggested direction
- Cache the document root per navigation instead of calling
DOM.getDocument per lookup, and invalidate it on Page.frameNavigated / DOM.documentUpdated.
- Take the depth as an option rather than hardcoding
-1, defaulting to the node alone. A caller that wants a subtree can ask; today everyone pays for one.
Repeated node lookups against a logged-in
linkedin.com/in/<account>push memory up sharply, and it does not come back down between lookups. Ordinary pages are fine; it shows on DOM-heavy, script-heavy ones.Two things in the CDP lookup path look like they contribute.
1.
DOM.getDocumenton everylocatelocateresolves the document root before every query:https://github.com/dashn9/rustenium/blob/main/rustenium/src/conduit/cdp/adapter.rs#L316
Each
DOM.getDocumentmakes Chrome's DOM agent mint and retain a freshNodeId→ backend-node mapping. That map is owned by the browser and is only dropped on document update orDOM.disable, so it grows per call and the growth is browser-side — which matches the spike persisting rather than being reclaimed when theDomNodegoes out of scope on our side.locate_allandwait_for(which pollslocateevery 100ms) multiply it.2.
DescribeNodewithdepth(-1)describe_by_idasks for the whole subtree:https://github.com/dashn9/rustenium/blob/main/rustenium/src/conduit/cdp/adapter.rs#L305
So matching a container selector on a LinkedIn profile serialises and deserialises that entire subtree into a
DomNodetree, every time. Most callers want the node itself — its box, its attributes, its text — not its descendants.Repro
linkedin.com/in/<account>.locateon a container selector (or anywait_for, which polls it) a few hundred times.Suggested direction
DOM.getDocumentper lookup, and invalidate it onPage.frameNavigated/DOM.documentUpdated.-1, defaulting to the node alone. A caller that wants a subtree can ask; today everyone pays for one.