performance updates checkpoint

This commit is contained in:
Francis Cao committed 2026-03-05 10:12:30 -08:00
1 parent 8563fe1d30
commit f6a6d2cc31
25 files changed
+488 -343

No files matched your search

+33 -6
View File
@@ -53,6 +53,13 @@ export interface SaveEventArgs {
ttclid?: string;
lifatid?: string;
twclid?: string;
// Performance
lcp?: number;
inp?: number;
cls?: number;
fcp?: number;
ttfb?: number;
}
export async function saveEvent(args: SaveEventArgs) {
@@ -89,6 +96,11 @@ async function relationalQuery({
ttclid,
lifatid,
twclid,
lcp,
inp,
cls,
fcp,
ttfb,
}: SaveEventArgs) {
const websiteEventId = uuid();
@@ -119,6 +131,11 @@ async function relationalQuery({
eventName: eventName ? eventName?.substring(0, EVENT_NAME_LENGTH) : null,
tag,
hostname,
lcp,
inp,
cls,
fcp,
ttfb,
createdAt,
},
});
@@ -186,6 +203,11 @@ async function clickhouseQuery({
ttclid,
lifatid,
twclid,
lcp,
inp,
cls,
fcp,
ttfb,
}: SaveEventArgs) {
const { insert, getUTCString } = clickhouse;
const { sendMessage } = kafka;
@@ -221,12 +243,17 @@ async function clickhouseQuery({
tag: tag,
distinct_id: distinctId,
created_at: getUTCString(createdAt),
browser,
os,
device,
screen,
language,
hostname,
browser: browser,
os: os,
device: device,
screen: screen,
language: language,
hostname: hostname,
lcp: lcp,
inp: inp,
cls: cls,
fcp: fcp,
ttfb: ttfb,
};
if (kafka.enabled) {
+3 -3
View File
@@ -68,7 +68,7 @@ async function relationalQuery(
${excludeBounceQuery}
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${dateQuery}
${filterQuery}
group by prefix,
@@ -163,7 +163,7 @@ async function clickhouseQuery(
VIDEO_DOMAINS,
)}]) != 0 or position(lower(utm_medium), 'video') > 0 then concat(prefix, 'Video')
when referrer_domain != hostname and referrer_domain != '' then 'referral'
else '' end AS name,
else '' end AS "name",
session_id,
visit_id,
count(*) c,
@@ -174,7 +174,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
and name != ''
${filterQuery}
group by prefix, name, session_id, visit_id
+2 -2
View File
@@ -45,7 +45,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
${excludeBounceQuery}
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${dateQuery}
${filterQuery}),
@@ -123,7 +123,7 @@ async function clickhouseQuery(
${cohortQuery}
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and event_type != 2
and event_type NOT IN (2, 5)
${dateQuery}
${filterQuery}
group by 1, 2
+3 -3
View File
@@ -58,7 +58,7 @@ async function relationalQuery(
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${filterQuery}
group by 1, 2
@@ -103,7 +103,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by session_id, visit_id
) as t;
@@ -127,7 +127,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by session_id, visit_id
) as t;
+3 -3
View File
@@ -33,7 +33,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${filterQuery}
group by time
order by 1
@@ -63,7 +63,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by time
order by time
@@ -78,7 +78,7 @@ async function clickhouseQuery(websiteId: string, filters: QueryFilters) {
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by time
order by time
-1
View File
@@ -24,7 +24,6 @@ export * from './pageviews/getPageviewExpandedMetrics';
export * from './pageviews/getPageviewMetrics';
export * from './pageviews/getPageviewStats';
export * from './performance/getPerformanceStats';
export * from './performance/savePerformance';
export * from './replays/getReplayChunks';
export * from './replays/getSessionReplays';
export * from './replays/saveRecording';
@@ -68,7 +68,7 @@ async function relationalQuery(
from website_event
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
group by visit_id
) x
on x.visit_id = website_event.visit_id
@@ -100,7 +100,7 @@ async function relationalQuery(
${entryExitQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${excludeDomain}
${filterQuery}
group by ${column}, website_event.session_id, website_event.visit_id
@@ -149,7 +149,7 @@ async function clickhouseQuery(
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
group by visit_id) x
ON x.visit_id = website_event.visit_id`;
}
@@ -177,7 +177,7 @@ async function clickhouseQuery(
${entryExitQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
and name != ''
${excludeDomain}
${filterQuery}
@@ -63,7 +63,7 @@ async function relationalQuery(
from website_event
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
order by visit_id, created_at ${order}
) x
on x.visit_id = website_event.visit_id
@@ -81,7 +81,7 @@ async function relationalQuery(
${entryExitQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${excludeDomain}
${filterQuery}
group by 1
@@ -127,7 +127,7 @@ async function clickhouseQuery(
from website_event
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
group by visit_id) x
ON x.visit_id = website_event.visit_id`;
}
@@ -141,7 +141,7 @@ async function clickhouseQuery(
${entryExitQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${excludeDomain}
${filterQuery}
group by x
@@ -180,7 +180,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${excludeDomain}
${filterQuery}
${groupByQuery}) as g
@@ -33,7 +33,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${filterQuery}
group by 1
order by 1
@@ -70,7 +70,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by t
) as g
@@ -90,7 +90,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by t
) as g
@@ -23,8 +23,11 @@ async function relationalQuery(
websiteId: string,
filters: QueryFilters,
): Promise<PerformanceStatsResult> {
const { rawQuery } = prisma;
const { startDate, endDate } = filters;
const { rawQuery, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({
...filters,
websiteId,
});
const result = await rawQuery(
`
@@ -35,11 +38,15 @@ async function relationalQuery(
percentile_cont(0.75) within group (order by fcp) as fcp,
percentile_cont(0.75) within group (order by ttfb) as ttfb,
count(*) as count
from performance
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
from website_event
${cohortQuery}
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type = 5
and website_event.created_at between {{startDate}} and {{endDate}}
${filterQuery}
`,
{ websiteId, startDate, endDate },
queryParams,
);
return result?.[0] || { lcp: 0, inp: 0, cls: 0, fcp: 0, ttfb: 0, count: 0 };
@@ -49,8 +56,8 @@ async function clickhouseQuery(
websiteId: string,
filters: QueryFilters,
): Promise<PerformanceStatsResult> {
const { rawQuery } = clickhouse;
const { startDate, endDate } = filters;
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId });
const result = await rawQuery<PerformanceStatsResult>(
`
@@ -61,11 +68,14 @@ async function clickhouseQuery(
quantile(0.75)(fcp) as fcp,
quantile(0.75)(ttfb) as ttfb,
count() as count
from website_performance
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
from website_event
${cohortQuery}
where website_event.website_id = {websiteId:UUID}
and website_event.event_type = 5
and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery}
`,
{ websiteId, startDate, endDate },
queryParams,
);
return result?.[0] || { lcp: 0, inp: 0, cls: 0, fcp: 0, ttfb: 0, count: 0 };
@@ -1,70 +0,0 @@
import clickhouse from '@/lib/clickhouse';
import { uuid } from '@/lib/crypto';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import kafka from '@/lib/kafka';
import prisma from '@/lib/prisma';
export interface SavePerformanceArgs {
websiteId: string;
sessionId: string;
visitId: string;
urlPath: string;
lcp?: number;
inp?: number;
cls?: number;
fcp?: number;
ttfb?: number;
createdAt: Date;
}
export async function savePerformance(args: SavePerformanceArgs) {
return runQuery({
[PRISMA]: () => relationalQuery(args),
[CLICKHOUSE]: () => clickhouseQuery(args),
});
}
async function relationalQuery(data: SavePerformanceArgs) {
const { websiteId, sessionId, visitId, urlPath, lcp, inp, cls, fcp, ttfb, createdAt } = data;
await prisma.client.performance.create({
data: {
id: uuid(),
websiteId,
sessionId,
visitId,
urlPath: urlPath?.substring(0, 500),
lcp,
inp,
cls,
fcp,
ttfb,
createdAt,
},
});
}
async function clickhouseQuery(data: SavePerformanceArgs) {
const { websiteId, sessionId, visitId, urlPath, lcp, inp, cls, fcp, ttfb, createdAt } = data;
const { insert, getUTCString } = clickhouse;
const { sendMessage } = kafka;
const message = {
website_id: websiteId,
session_id: sessionId,
visit_id: visitId,
url_path: urlPath?.substring(0, 500),
lcp: lcp ?? null,
inp: inp ?? null,
cls: cls ?? null,
fcp: fcp ?? null,
ttfb: ttfb ?? null,
created_at: getUTCString(createdAt),
};
if (kafka.enabled) {
await sendMessage('performance', message);
} else {
await insert('website_performance', [message]);
}
}
+40 -54
View File
@@ -13,7 +13,6 @@ export interface PerformanceParameters {
export interface PerformanceResult {
chart: { t: string; p50: number; p75: number; p95: number }[];
pages: { urlPath: string; p75: number; count: number }[];
summary: {
lcp: { p50: number; p75: number; p95: number };
inp: { p50: number; p75: number; p95: number };
@@ -39,7 +38,11 @@ async function relationalQuery(
filters: QueryFilters,
): Promise<PerformanceResult> {
const { startDate, endDate, unit = 'day', timezone = 'utc', metric = 'lcp' } = parameters;
const { getDateSQL, rawQuery } = prisma;
const { getDateSQL, rawQuery, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({
...filters,
websiteId,
});
const chart = await rawQuery(
`
@@ -48,29 +51,17 @@ async function relationalQuery(
percentile_cont(0.5) within group (order by ${metric}) as p50,
percentile_cont(0.75) within group (order by ${metric}) as p75,
percentile_cont(0.95) within group (order by ${metric}) as p95
from performance
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
from website_event
${cohortQuery}
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type = 5
and website_event.created_at between {{startDate}} and {{endDate}}
${filterQuery}
group by t
order by t
`,
{ websiteId, startDate, endDate },
);
const pages = await rawQuery(
`
select
url_path as "urlPath",
percentile_cont(0.75) within group (order by ${metric}) as p75,
count(*) as count
from performance
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
group by url_path
order by p75 desc
limit 100
`,
{ websiteId, startDate, endDate },
{ ...queryParams, startDate, endDate },
);
const summaryResult = await rawQuery(
@@ -92,11 +83,15 @@ async function relationalQuery(
percentile_cont(0.75) within group (order by ttfb) as ttfb_p75,
percentile_cont(0.95) within group (order by ttfb) as ttfb_p95,
count(*) as count
from performance
where website_id = {{websiteId::uuid}}
and created_at between {{startDate}} and {{endDate}}
from website_event
${cohortQuery}
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type = 5
and website_event.created_at between {{startDate}} and {{endDate}}
${filterQuery}
`,
{ websiteId, startDate, endDate },
{ ...queryParams, startDate, endDate },
).then(result => result?.[0]);
const summary = {
@@ -128,7 +123,7 @@ async function relationalQuery(
count: Number(summaryResult?.count || 0),
};
return { chart, pages, summary };
return { chart, summary };
}
async function clickhouseQuery(
@@ -137,38 +132,26 @@ async function clickhouseQuery(
filters: QueryFilters,
): Promise<PerformanceResult> {
const { startDate, endDate, unit = 'day', timezone = 'utc', metric = 'lcp' } = parameters;
const { getDateSQL, rawQuery } = clickhouse;
const { getDateSQL, rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId });
const chart = await rawQuery<{ t: string; p50: number; p75: number; p95: number }[]>(
`
select
${getDateSQL('created_at', unit, timezone)} t,
${getDateSQL('created_at', 'minute', timezone)} t,
quantile(0.5)(${metric}) as p50,
quantile(0.75)(${metric}) as p75,
quantile(0.95)(${metric}) as p95
from website_performance
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
from website_event
${cohortQuery}
where website_event.website_id = {websiteId:UUID}
and website_event.event_type = 5
and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery}
group by t
order by t
`,
{ websiteId, startDate, endDate },
);
const pages = await rawQuery<{ urlPath: string; p75: number; count: number }[]>(
`
select
url_path as "urlPath",
quantile(0.75)(${metric}) as p75,
count() as count
from website_performance
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
group by url_path
order by p75 desc
limit 100
`,
{ websiteId, startDate, endDate },
{ ...queryParams, startDate, endDate },
);
const summaryResult = await rawQuery<any>(
@@ -190,11 +173,14 @@ async function clickhouseQuery(
quantile(0.75)(ttfb) as ttfb_p75,
quantile(0.95)(ttfb) as ttfb_p95,
count() as count
from website_performance
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
from website_event
${cohortQuery}
where website_event.website_id = {websiteId:UUID}
and website_event.event_type = 5
and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery}
`,
{ websiteId, startDate, endDate },
{ ...queryParams, startDate, endDate },
).then(result => result?.[0]);
const summary = {
@@ -226,5 +212,5 @@ async function clickhouseQuery(
count: Number(summaryResult?.count || 0),
};
return { chart, pages, summary };
return { chart, summary };
}
@@ -0,0 +1,98 @@
import clickhouse from '@/lib/clickhouse';
import { CLICKHOUSE, PRISMA, runQuery } from '@/lib/db';
import prisma from '@/lib/prisma';
import type { QueryFilters } from '@/lib/types';
import type { PerformanceParameters } from './getPerformance';
export interface PerformanceMetricsData {
name: string;
p50: number;
p75: number;
p95: number;
count: number;
}
export async function getPerformanceMetrics(
...args: [
websiteId: string,
parameters: PerformanceParameters,
filters: QueryFilters,
column: string,
limit?: number,
]
) {
return runQuery({
[PRISMA]: () => relationalQuery(...args),
[CLICKHOUSE]: () => clickhouseQuery(...args),
});
}
async function relationalQuery(
websiteId: string,
parameters: PerformanceParameters,
filters: QueryFilters,
column: string,
limit?: number,
): Promise<PerformanceMetricsData[]> {
const { startDate, endDate, metric = 'lcp' } = parameters;
const { rawQuery, parseFilters } = prisma;
const { filterQuery, joinSessionQuery, cohortQuery, queryParams } = parseFilters({
...filters,
websiteId,
});
return rawQuery(
`
select
${column} as "name",
percentile_cont(0.5) within group (order by ${metric}) as p50,
percentile_cont(0.75) within group (order by ${metric}) as p75,
percentile_cont(0.95) within group (order by ${metric}) as p95,
count(*) as count
from website_event
${cohortQuery}
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.event_type = 5
and website_event.created_at between {{startDate}} and {{endDate}}
${filterQuery}
group by ${column}
order by p75 desc
${limit ? `limit ${limit}` : ''}
`,
{ ...queryParams, startDate, endDate },
);
}
async function clickhouseQuery(
websiteId: string,
parameters: PerformanceParameters,
filters: QueryFilters,
column: string,
limit?: number,
): Promise<PerformanceMetricsData[]> {
const { startDate, endDate, metric = 'lcp' } = parameters;
const { rawQuery, parseFilters } = clickhouse;
const { filterQuery, cohortQuery, queryParams } = parseFilters({ ...filters, websiteId });
return rawQuery<PerformanceMetricsData[]>(
`
select
${column} as "name",
quantile(0.5)(${metric}) as p50,
quantile(0.75)(${metric}) as p75,
quantile(0.95)(${metric}) as p95,
count() as count
from website_event
${cohortQuery}
where website_event.website_id = {websiteId:UUID}
and website_event.event_type = 5
and website_event.created_at between {startDate:DateTime64} and {endDate:DateTime64}
${filterQuery}
group by ${column}
order by p75 desc
${limit ? `limit ${limit}` : ''}
`,
{ ...queryParams, startDate, endDate },
);
}
@@ -79,7 +79,7 @@ async function relationalQuery(
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${filterQuery}
group by name, website_event.session_id, website_event.visit_id
${includeCountry ? ', country' : ''}
@@ -138,7 +138,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
and name != ''
${filterQuery}
group by name, session_id, visit_id
@@ -57,7 +57,7 @@ async function relationalQuery(
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${filterQuery}
group by 1
${includeCountry ? ', 3' : ''}
@@ -101,7 +101,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by x
${includeCountry ? ', country' : ''}
@@ -120,7 +120,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by x
${includeCountry ? ', country' : ''}
+3 -3
View File
@@ -33,7 +33,7 @@ async function relationalQuery(websiteId: string, filters: QueryFilters) {
${joinSessionQuery}
where website_event.website_id = {{websiteId::uuid}}
and website_event.created_at between {{startDate}} and {{endDate}}
and website_event.event_type != 2
and website_event.event_type NOT IN (2, 5)
${filterQuery}
group by 1
order by 1
@@ -70,7 +70,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by t
) as g
@@ -90,7 +90,7 @@ async function clickhouseQuery(
${excludeBounceQuery}
where website_id = {websiteId:UUID}
and created_at between {startDate:DateTime64} and {endDate:DateTime64}
and event_type != 2
and event_type NOT IN (2, 5)
${filterQuery}
group by t
) as g