🚧 WIP: batched site status histories

This commit is contained in:
Fred KISSIE
2026-07-21 19:57:09 +01:00
parent 9cc3190e3a
commit 23181f4019
7 changed files with 122 additions and 59 deletions
+25 -9
View File
@@ -37,10 +37,11 @@ export async function getCachedStatusHistory(
tzOffsetMinutes
);
const cached = await cache.get<StatusHistoryResponse>(cacheKey);
if (cached !== undefined) {
return cached;
}
// if (cached !== undefined) {
// return cached;
// }
console.time(`[getCachedStatusHistory/${entityType}=${entityId}]`);
// Anchor to local midnight (UTC when tzOffsetMinutes is 0) so the query
// window aligns with stable calendar days for the requesting client
const todayMidnightSec = localMidnightSec(tzOffsetMinutes);
@@ -76,12 +77,14 @@ export async function getCachedStatusHistory(
const priorStatus = lastKnownEvent?.status ?? null;
console.time(`[computeBuckets/${entityType}=${entityId}]`);
const { buckets, totalDowntime } = computeBuckets(
events,
days,
priorStatus,
tzOffsetMinutes
);
console.timeEnd(`[computeBuckets/${entityType}=${entityId}]`);
const totalWindow = days * 86400;
const overallUptime =
totalWindow > 0
@@ -96,6 +99,7 @@ export async function getCachedStatusHistory(
totalDowntimeSeconds: totalDowntime
};
console.timeEnd(`[getCachedStatusHistory/${entityType}=${entityId}]`);
await cache.set(cacheKey, result, STATUS_HISTORY_CACHE_TTL);
return result;
}
@@ -299,7 +303,6 @@ export function computeBuckets(
status
});
}
return { buckets, totalDowntime };
}
@@ -311,7 +314,8 @@ export type BatchedStatusHistoryResponse = Record<
export async function getBatchedStatusHistory(
entityType: string,
entityIds: number[],
days: number
days: number,
tzOffsetMinutes: number = 0
): Promise<BatchedStatusHistoryResponse> {
// const cacheKey = statusHistoryCacheKey(entityType, entityId, days);
// const cached = await cache.get<StatusHistoryResponse>(cacheKey);
@@ -319,10 +323,13 @@ export async function getBatchedStatusHistory(
// return cached;
// }
// Anchor to UTC midnight so the query window aligns with stable calendar days
const utcToday = new Date();
utcToday.setUTCHours(0, 0, 0, 0);
const todayMidnightSec = Math.floor(utcToday.getTime() / 1000);
console.time(
`[getBatchedStatusHistory/${entityType}=(${entityIds.join(" ,")})]`
);
// Anchor to local midnight (UTC when tzOffsetMinutes is 0) so the query
// window aligns with stable calendar days for the requesting client
const todayMidnightSec = localMidnightSec(tzOffsetMinutes);
const startSec = todayMidnightSec - days * 86400;
const events = await logsDb
@@ -337,6 +344,10 @@ export async function getBatchedStatusHistory(
)
.orderBy(asc(statusHistory.timestamp));
console.log({
events
});
// Fetch the last known state before the window so that entities that
// haven't changed status recently still show the correct status rather
// than appearing as "no_data".
@@ -375,6 +386,7 @@ export async function getBatchedStatusHistory(
const result: BatchedStatusHistoryResponse = {};
console.time(`[computeBuckets/${entityType}=(${entityIds.join(" ,")})]`);
for (const entityId in eventStatusMap) {
const event = eventStatusMap[Number(entityId)];
const priorStatus = event.lastKnownEvent?.status ?? null;
@@ -401,7 +413,11 @@ export async function getBatchedStatusHistory(
totalDowntimeSeconds: totalDowntime
};
}
console.timeEnd(`[computeBuckets/${entityType}=(${entityIds.join(" ,")})]`);
console.timeEnd(
`[getBatchedStatusHistory/${entityType}=(${entityIds.join(" ,")})]`
);
// await cache.set(cacheKey, result, STATUS_HISTORY_CACHE_TTL);
return result;
}
+7
View File
@@ -319,6 +319,13 @@ authenticated.get(
site.getSiteStatusHistory
);
authenticated.get(
"/org/:orgId/site-status-histories",
verifyOrgAccess,
verifyUserHasAction(ActionsEnum.listSites),
site.getBatchedSiteStatusHistory
);
// Site Resource endpoints
authenticated.put(
"/org/:orgId/site-resource",
+16 -13
View File
@@ -1,15 +1,14 @@
import { Request, Response, NextFunction } from "express";
import { z } from "zod";
import response from "@server/lib/response";
import HttpCode from "@server/types/HttpCode";
import createHttpError from "http-errors";
import logger from "@server/logger";
import { fromError } from "zod-validation-error";
import {
getCachedStatusHistory,
statusHistoryQuerySchema,
StatusHistoryResponse
getBatchedStatusHistory,
type BatchedStatusHistoryResponse
} from "@server/lib/statusHistory";
import logger from "@server/logger";
import HttpCode from "@server/types/HttpCode";
import { NextFunction, Request, Response } from "express";
import createHttpError from "http-errors";
import { z } from "zod";
import { fromError } from "zod-validation-error";
const siteIdParamsSchema = z.object({
days: z
@@ -59,12 +58,16 @@ export async function getBatchedSiteStatusHistory(
}
const entityType = "site";
const entityId = parsedParams.data.siteId;
const { days } = parsedQuery.data;
const { days, siteIds, tzOffsetMinutes } = parsedQuery.data;
const data = await getCachedStatusHistory(entityType, entityId, days);
const data = await getBatchedStatusHistory(
entityType,
siteIds,
days,
tzOffsetMinutes
);
return response<StatusHistoryResponse>(res, {
return response<BatchedStatusHistoryResponse>(res, {
data,
success: true,
error: false,
+1
View File
@@ -1,5 +1,6 @@
export * from "./getSite";
export * from "./getStatusHistory";
export * from "./getBatchedStatusHistory";
export * from "./createSite";
export * from "./deleteSite";
export * from "./updateSite";