⚗️ Partial view updates — batch optimizations#4756
Conversation
| // On flush: advance batchBase to lastSentView (what the backend now has) and clear the flag. | ||
| batch.flushController.flushObservable.subscribe(() => { | ||
| batchHasFullView = false | ||
| batchBase = lastSentView |
There was a problem hiding this comment.
batchBase tracks what the backend actually received, not just the latest event we processed. It advances to lastSentView on flush, so aggregate diffs in the next batch are always computed against the right base. If we used lastSentView directly as the diff base without this reset, we'd start each new batch from the wrong state.
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 3a03a5f | Docs | Datadog PR Page | Give us feedback! |
Bundles Sizes Evolution
|
14ceaa8 to
6d8d24e
Compare
ebd0be3 to
3a03a5f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a03a5f081
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| lastSentView = serverRumEvent | ||
| batchBase = serverRumEvent | ||
| batchHasFullView = true | ||
| viewUpdatesSinceCheckpoint = 0 | ||
| batch.upsert(viewEvent as unknown as Context, viewId) | ||
| batch.upsert(serverRumEvent, viewId) |
There was a problem hiding this comment.
Keep router state aligned when upsert flushes first
issue: When starting a new view while the current batch is near the 16 KiB bytes limit, batch.upsert() can synchronously flush the existing batch in notifyBeforeAddMessage before the new view is stored. Because lastSentView/batchHasFullView are updated before that call, this router's flush subscriber resets batchHasFullView to false, then the full VIEW is added to the fresh batch; the next update for that same view takes the opt-2 path and upserts a view_update under the same key, replacing the unsent full VIEW. In that scenario the intake receives only a view_update for a view it has never seen, so the initial view can be lost.
Useful? React with 👍 / 👎.
| sessionExpireObservable, | ||
| }), | ||
| // On flush: advance batchBase to lastSentView (what the backend now has) and clear the flag. | ||
| batch.flushController.flushObservable.subscribe(() => { |
There was a problem hiding this comment.
Unsubscribe the router flush listener on stop
issue: This new flushObservable subscription is never tied into batch.stop(). In the non-event-bridge path, startRum() only calls batch.stop() during cleanup, and createBatch.stop() removes only the batch's own flush observer; this extra observer keeps the flush controller subscribed to page-exit/session-expire sources after RUM is stopped, so repeated stop/re-init cycles accumulate stale listeners and retain this router state.
Useful? React with 👍 / 👎.
Motivation
The initial partial view updates implementation (already on main) sends diffs via
batch.add(). Two inefficiencies: when the full VIEW is still in the current batch, intermediate updates add separateview_updateevents on top of it instead of replacing the VIEW in place. And when multiple updates land in the same batch, each one becomes a separateview_updateinstead of a single aggregate.Changes
startRumBatch.ts— refactored intocreateViewBatchRouterto make the routing logic unit-testable without the full batch setup. Two batch-level optimizations:If the current batch already has a full VIEW, intermediate updates replace it in the upsert buffer (
batchHasFullViewpath). Noview_updateemitted. Same as the non-experimental behavior.If the batch was already flushed, updates compute an aggregate diff from
batchBase(the last state the backend received) and upsert it under the same key. Multiple updates collapse to oneview_updateper batch.Test instructions
yarn dev, openhttp://localhost:8080enableExperimentalFeatures: ['partial_view_updates']to theDD_RUM.init()call insandbox/index.html/proxyOptimization 1 (no view_update emitted):
DD_RUM.setViewName('test')— update arrives while the VIEW is still in the batchwindow.dispatchEvent(new Event('beforeunload'))view_updateOptimization 2 (aggregate view_update sent):
window.dispatchEvent(new Event('beforeunload'))DD_RUM.setViewName('test2')— update arrives in a fresh batchwindow.dispatchEvent(new Event('beforeunload'))view_updatediff, not a full VIEWChecklist