chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
import { type ClickHouse } from "@internal/clickhouse";
|
||||
import type { TaskRunStatus } from "@trigger.dev/database";
|
||||
import { QUEUED_STATUSES } from "~/components/runs/v3/TaskRunStatus";
|
||||
|
||||
export type DailyTaskActivity = Record<string, ({ day: string } & Record<TaskRunStatus, number>)[]>;
|
||||
export type CurrentRunningStats = Record<string, { queued: number; running: number }>;
|
||||
export type AverageDurations = Record<string, number>;
|
||||
|
||||
export interface EnvironmentMetricsRepository {
|
||||
getDailyTaskActivity(options: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<DailyTaskActivity>;
|
||||
|
||||
getCurrentRunningStats(options: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<CurrentRunningStats>;
|
||||
|
||||
getAverageDurations(options: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<AverageDurations>;
|
||||
}
|
||||
|
||||
export type ClickHouseEnvironmentMetricsRepositoryOptions = {
|
||||
clickhouse: ClickHouse;
|
||||
};
|
||||
|
||||
export class ClickHouseEnvironmentMetricsRepository implements EnvironmentMetricsRepository {
|
||||
constructor(private readonly options: ClickHouseEnvironmentMetricsRepositoryOptions) {}
|
||||
|
||||
public async getDailyTaskActivity({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
tasks,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<DailyTaskActivity> {
|
||||
if (tasks.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const [queryError, activity] = await this.options.clickhouse.taskRuns.getTaskActivity({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
});
|
||||
|
||||
if (queryError) {
|
||||
throw queryError;
|
||||
}
|
||||
|
||||
return fillInDailyTaskActivity(
|
||||
activity.map((a) => ({
|
||||
taskIdentifier: a.task_identifier,
|
||||
status: a.status as TaskRunStatus,
|
||||
day: new Date(a.day),
|
||||
count: BigInt(a.count),
|
||||
})),
|
||||
days
|
||||
);
|
||||
}
|
||||
|
||||
public async getCurrentRunningStats({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
tasks,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<CurrentRunningStats> {
|
||||
if (tasks.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const [queryError, stats] = await this.options.clickhouse.taskRuns.getCurrentRunningStats({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
});
|
||||
|
||||
if (queryError) {
|
||||
throw queryError;
|
||||
}
|
||||
|
||||
return fillInCurrentRunningStats(
|
||||
stats.map((s) => ({
|
||||
taskIdentifier: s.task_identifier,
|
||||
status: s.status as TaskRunStatus,
|
||||
count: BigInt(s.count),
|
||||
})),
|
||||
tasks
|
||||
);
|
||||
}
|
||||
|
||||
public async getAverageDurations({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
tasks,
|
||||
}: {
|
||||
organizationId: string;
|
||||
projectId: string;
|
||||
environmentId: string;
|
||||
days: number;
|
||||
tasks: string[];
|
||||
}): Promise<AverageDurations> {
|
||||
if (tasks.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const [queryError, durations] = await this.options.clickhouse.taskRuns.getAverageDurations({
|
||||
organizationId,
|
||||
projectId,
|
||||
environmentId,
|
||||
days,
|
||||
});
|
||||
|
||||
if (queryError) {
|
||||
throw queryError;
|
||||
}
|
||||
|
||||
return Object.fromEntries(durations.map((d) => [d.task_identifier, Number(d.duration)]));
|
||||
}
|
||||
}
|
||||
|
||||
type TaskActivityResults = Array<{
|
||||
taskIdentifier: string;
|
||||
status: TaskRunStatus;
|
||||
day: Date;
|
||||
count: bigint;
|
||||
}>;
|
||||
|
||||
function fillInDailyTaskActivity(activity: TaskActivityResults, days: number): DailyTaskActivity {
|
||||
//today with no time
|
||||
const today = new Date();
|
||||
today.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
return activity.reduce((acc, a) => {
|
||||
let existingTask = acc[a.taskIdentifier];
|
||||
|
||||
if (!existingTask) {
|
||||
existingTask = [];
|
||||
//populate the array with the past 7 days
|
||||
for (let i = days; i >= 0; i--) {
|
||||
const day = new Date(today);
|
||||
day.setUTCDate(today.getDate() - i);
|
||||
day.setUTCHours(0, 0, 0, 0);
|
||||
|
||||
existingTask.push({
|
||||
day: day.toISOString(),
|
||||
["COMPLETED_SUCCESSFULLY"]: 0,
|
||||
} as { day: string } & Record<TaskRunStatus, number>);
|
||||
}
|
||||
|
||||
acc[a.taskIdentifier] = existingTask;
|
||||
}
|
||||
|
||||
const dayString = a.day.toISOString();
|
||||
const day = existingTask.find((d) => d.day === dayString);
|
||||
|
||||
if (!day) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
day[a.status] = Number(a.count);
|
||||
|
||||
return acc;
|
||||
}, {} as DailyTaskActivity);
|
||||
}
|
||||
|
||||
type CurrentRunningStatsResults = Array<{
|
||||
taskIdentifier: string;
|
||||
status: TaskRunStatus;
|
||||
count: bigint;
|
||||
}>;
|
||||
|
||||
function fillInCurrentRunningStats(
|
||||
stats: CurrentRunningStatsResults,
|
||||
tasks: string[]
|
||||
): CurrentRunningStats {
|
||||
//create an object combining the queued and concurrency counts
|
||||
const result: Record<string, { queued: number; running: number }> = {};
|
||||
for (const task of tasks) {
|
||||
const queued = stats.filter(
|
||||
(q) => q.taskIdentifier === task && QUEUED_STATUSES.includes(q.status)
|
||||
);
|
||||
const queuedCount =
|
||||
queued.length === 0
|
||||
? 0
|
||||
: queued.reduce((acc, q) => {
|
||||
return acc + Number(q.count);
|
||||
}, 0);
|
||||
|
||||
const running = stats.filter((r) => r.taskIdentifier === task && r.status === "EXECUTING");
|
||||
const runningCount =
|
||||
running.length === 0
|
||||
? 0
|
||||
: running.reduce((acc, r) => {
|
||||
return acc + Number(r.count);
|
||||
}, 0);
|
||||
|
||||
result[task] = {
|
||||
queued: queuedCount,
|
||||
running: runningCount,
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user