fix(rum): include unlinked session traces and filter polling noise (#14156)

This commit is contained in:
Mina Karam
2026-09-08 12:49:01 +00:00
committed by GitHub
parent a114e52772
commit 5ec64ebef5
2 changed files with 29 additions and 17 deletions
+24 -10
View File
@@ -1255,11 +1255,6 @@ describe("PlayerTracesTab", () => {
// =========================================================================
// SQL is schema-guarded so a mobile stream never 400s
//
// Mobile RUM streams lack the browser-shaped view columns: `view_loading_type`
// is browser-only, and `action_id` may be absent. Referencing a column the
// stream does not have fails the whole query with a 400. Every optional column
// must therefore be selected only when present, and the `action_id` filter
// applied only when that column exists.
// =========================================================================
describe("schema-guarded SQL", () => {
@@ -1268,9 +1263,7 @@ describe("PlayerTracesTab", () => {
return call?.[0]?.query?.query?.sql ?? "";
}
it("omits view_loading_type and the action_id filter on a mobile schema", async () => {
// Arrange: a mobile-shaped schema — trace_id present, but no view_loading_type
// and no action_id.
it("omits browser-only fields and filters on a mobile schema", async () => {
wrapper.unmount();
mockSearch.mockClear();
mockGetStream.mockResolvedValueOnce({
@@ -1294,11 +1287,12 @@ describe("PlayerTracesTab", () => {
expect(sql).not.toContain("max(view_loading_type)");
expect(sql).toContain("NULL as _view_loading_type");
expect(sql).not.toContain("action_id is not null");
expect(sql).not.toContain("resource_url");
expect(sql).toContain("_o2_trace_id");
expect(sql).toContain("max(view_url)");
});
it("keeps view_loading_type and the action_id filter on a full browser schema", async () => {
it("includes unlinked traces and filters Socket.IO groups on a browser schema", async () => {
// Arrange: browser schema carries every column.
wrapper.unmount();
mockSearch.mockClear();
@@ -1312,6 +1306,7 @@ describe("PlayerTracesTab", () => {
{ name: "type" },
{ name: "date" },
{ name: "action_id" },
{ name: "resource_url" },
],
});
@@ -1322,7 +1317,26 @@ describe("PlayerTracesTab", () => {
// Assert
const sql = lastRumSql();
expect(sql).toContain("max(view_loading_type) as _view_loading_type");
expect(sql).toContain("action_id is not null");
expect(sql).not.toContain("action_id is not null");
expect(sql).toContain(
"HAVING MAX(CASE WHEN resource_url LIKE '%/socket.io/%' AND resource_url LIKE '%transport=polling%' THEN 1 ELSE 0 END) = 0",
);
});
it("keeps null resource URLs while excluding a trace containing Socket.IO polling", async () => {
wrapper.unmount();
mockSearch.mockClear();
mockGetStream.mockResolvedValueOnce({
schema: [{ name: "_oo_trace_id" }, { name: "session_id" }, { name: "resource_url" }],
});
wrapper = mountComponent();
await flushPromises();
const sql = lastRumSql();
expect(sql).not.toContain("resource_url NOT LIKE");
expect(sql).toContain("resource_url LIKE '%/socket.io/%'");
expect(sql).toContain("resource_url LIKE '%transport=polling%'");
});
it("does not query at all when the stream has no trace_id column", async () => {
+5 -7
View File
@@ -531,11 +531,7 @@ async function fetchTraces() {
return;
}
// The view-context columns are browser-shaped and are NOT guaranteed on a mobile
// `_rumdata` schema. Referencing a column the stream lacks fails the whole query with a
// 400, so each optional column is selected only when present (NULL-aliased otherwise)
// and the `action_id` filter is applied only when that column exists. `session_id` and
// the guarded `trace_id` are the only hard requirements.
// Optional browser fields must be schema-guarded because mobile streams omit them.
const presentCols = new Set((rumStream?.schema ?? []).map((field: any) => field?.name));
const has = (col: string): boolean => presentCols.has(col);
const aggOrNull = (fn: string, col: string, alias: string): string =>
@@ -550,11 +546,13 @@ async function fetchTraces() {
aggOrNull("min", "date", "_date"),
];
const whereParts = [`session_id='${props.sessionId}'`, traceIdSet];
if (has("action_id")) whereParts.push("action_id is not null");
const having = has("resource_url")
? " HAVING MAX(CASE WHEN resource_url LIKE '%/socket.io/%' AND resource_url LIKE '%transport=polling%' THEN 1 ELSE 0 END) = 0"
: "";
const rumQuery = {
query: {
sql: `SELECT ${selectParts.join(", ")} FROM "_rumdata" WHERE ${whereParts.join(" AND ")} GROUP BY ${traceIdExpr} ORDER BY _date ASC`,
sql: `SELECT ${selectParts.join(", ")} FROM "_rumdata" WHERE ${whereParts.join(" AND ")} GROUP BY ${traceIdExpr}${having} ORDER BY _date ASC`,
start_time: searchStartTime,
end_time: searchEndTime,
from: 0,