mirror of
https://github.com/fosrl/pangolin.git
synced 2026-05-27 03:02:30 +00:00
Add caching to the hc and fix resource stuff
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
||||
Transaction
|
||||
} from "@server/db";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { invalidateStatusHistoryCache } from "@server/lib/statusHistory";
|
||||
import {
|
||||
fireResourceDegradedAlert,
|
||||
fireResourceHealthyAlert,
|
||||
@@ -61,8 +62,9 @@ export async function fireHealthCheckHealthyAlert(
|
||||
status: "healthy",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("health_check", healthCheckId);
|
||||
|
||||
await handleResource(orgId, healthCheckTargetId, trx);
|
||||
await handleResource(orgId, healthCheckTargetId, send, trx);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
@@ -124,8 +126,9 @@ export async function fireHealthCheckUnhealthyAlert(
|
||||
status: "unhealthy",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("health_check", healthCheckId);
|
||||
|
||||
await handleResource(orgId, healthCheckTargetId, trx);
|
||||
await handleResource(orgId, healthCheckTargetId, send, trx);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
@@ -176,8 +179,9 @@ export async function fireHealthCheckUnknownAlert(
|
||||
status: "unknown",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("health_check", healthCheckId);
|
||||
|
||||
await handleResource(orgId, healthCheckTargetId, trx);
|
||||
await handleResource(orgId, healthCheckTargetId, send, trx);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
@@ -190,11 +194,11 @@ export async function fireHealthCheckUnknownAlert(
|
||||
}
|
||||
}
|
||||
|
||||
async function handleResource(orgId: string, healthCheckTargetId?: number | null, trx: Transaction | typeof db = db) {
|
||||
async function handleResource(orgId: string, healthCheckTargetId?: number | null, send: boolean = true, trx: Transaction | typeof db = db) {
|
||||
if (!healthCheckTargetId) {
|
||||
return;
|
||||
}
|
||||
// we have resources lets get them
|
||||
// we have targets lets get them
|
||||
const [target] = await trx
|
||||
.select()
|
||||
.from(targets)
|
||||
@@ -204,6 +208,7 @@ async function handleResource(orgId: string, healthCheckTargetId?: number | null
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const [resource] = await trx
|
||||
.select()
|
||||
.from(resources)
|
||||
@@ -213,6 +218,7 @@ async function handleResource(orgId: string, healthCheckTargetId?: number | null
|
||||
if (!resource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const otherTargets = await trx
|
||||
.select({ hcHealth: targetHealthCheck.hcHealth })
|
||||
.from(targets)
|
||||
@@ -256,6 +262,7 @@ async function handleResource(orgId: string, healthCheckTargetId?: number | null
|
||||
resource.resourceId,
|
||||
resource.name,
|
||||
undefined,
|
||||
send,
|
||||
trx
|
||||
);
|
||||
} else if (health === "unhealthy") {
|
||||
@@ -264,6 +271,7 @@ async function handleResource(orgId: string, healthCheckTargetId?: number | null
|
||||
resource.resourceId,
|
||||
resource.name,
|
||||
undefined,
|
||||
send,
|
||||
trx
|
||||
);
|
||||
} else if (health === "healthy") {
|
||||
@@ -272,6 +280,7 @@ async function handleResource(orgId: string, healthCheckTargetId?: number | null
|
||||
resource.resourceId,
|
||||
resource.name,
|
||||
undefined,
|
||||
send,
|
||||
trx
|
||||
);
|
||||
} else if (health === "degraded") {
|
||||
@@ -280,6 +289,7 @@ async function handleResource(orgId: string, healthCheckTargetId?: number | null
|
||||
resource.resourceId,
|
||||
resource.name,
|
||||
undefined,
|
||||
send,
|
||||
trx
|
||||
);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import logger from "@server/logger";
|
||||
import { processAlerts } from "../processAlerts";
|
||||
import { db, statusHistory, Transaction } from "@server/db";
|
||||
import { invalidateStatusHistoryCache } from "@server/lib/statusHistory";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Public API
|
||||
@@ -35,6 +36,7 @@ export async function fireResourceHealthyAlert(
|
||||
resourceId: number,
|
||||
resourceName?: string | null,
|
||||
extra?: Record<string, unknown>,
|
||||
send: boolean = true,
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<void> {
|
||||
try {
|
||||
@@ -45,6 +47,11 @@ export async function fireResourceHealthyAlert(
|
||||
status: "healthy",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("resource", resourceId);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
}
|
||||
|
||||
await processAlerts({
|
||||
eventType: "resource_healthy",
|
||||
@@ -90,6 +97,7 @@ export async function fireResourceUnhealthyAlert(
|
||||
resourceId: number,
|
||||
resourceName?: string | null,
|
||||
extra?: Record<string, unknown>,
|
||||
send: boolean = true,
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<void> {
|
||||
try {
|
||||
@@ -100,6 +108,11 @@ export async function fireResourceUnhealthyAlert(
|
||||
status: "unhealthy",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("resource", resourceId);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
}
|
||||
|
||||
await processAlerts({
|
||||
eventType: "resource_unhealthy",
|
||||
@@ -145,6 +158,7 @@ export async function fireResourceDegradedAlert(
|
||||
resourceId: number,
|
||||
resourceName?: string | null,
|
||||
extra?: Record<string, unknown>,
|
||||
send: boolean = true,
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<void> {
|
||||
try {
|
||||
@@ -155,6 +169,11 @@ export async function fireResourceDegradedAlert(
|
||||
status: "degraded",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("resource", resourceId);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
}
|
||||
|
||||
await processAlerts({
|
||||
eventType: "resource_degraded",
|
||||
@@ -200,6 +219,7 @@ export async function fireResourceUnknownAlert(
|
||||
resourceId: number,
|
||||
resourceName?: string | null,
|
||||
extra?: Record<string, unknown>,
|
||||
send: boolean = true,
|
||||
trx: Transaction | typeof db = db
|
||||
): Promise<void> {
|
||||
try {
|
||||
@@ -210,6 +230,11 @@ export async function fireResourceUnknownAlert(
|
||||
status: "unknown",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("resource", resourceId);
|
||||
|
||||
if (!send) {
|
||||
return;
|
||||
}
|
||||
|
||||
await processAlerts({
|
||||
eventType: "resource_toggle",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
import logger from "@server/logger";
|
||||
import { processAlerts } from "../processAlerts";
|
||||
import { db, sites, statusHistory, targetHealthCheck, Transaction } from "@server/db";
|
||||
import { invalidateStatusHistoryCache } from "@server/lib/statusHistory";
|
||||
import { and, eq, inArray } from "drizzle-orm";
|
||||
import { fireHealthCheckUnhealthyAlert } from "./healthCheckEvents";
|
||||
|
||||
@@ -47,6 +48,7 @@ export async function fireSiteOnlineAlert(
|
||||
status: "online",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("site", siteId);
|
||||
|
||||
await processAlerts({
|
||||
eventType: "site_online",
|
||||
@@ -102,6 +104,7 @@ export async function fireSiteOfflineAlert(
|
||||
status: "offline",
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
await invalidateStatusHistoryCache("site", siteId);
|
||||
|
||||
const unhealthyHealthChecks = await trx
|
||||
.update(targetHealthCheck)
|
||||
|
||||
@@ -13,15 +13,13 @@
|
||||
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { z } from "zod";
|
||||
import { db, statusHistory } from "@server/db";
|
||||
import { and, eq, gte, asc } from "drizzle-orm";
|
||||
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 {
|
||||
computeBuckets,
|
||||
getCachedStatusHistory,
|
||||
statusHistoryQuerySchema,
|
||||
StatusHistoryResponse
|
||||
} from "@server/lib/statusHistory";
|
||||
@@ -59,39 +57,10 @@ export async function getHealthCheckStatusHistory(
|
||||
const entityId = parsedParams.data.healthCheckId;
|
||||
const { days } = parsedQuery.data;
|
||||
|
||||
const nowSec = Math.floor(Date.now() / 1000);
|
||||
const startSec = nowSec - days * 86400;
|
||||
|
||||
const events = await db
|
||||
.select()
|
||||
.from(statusHistory)
|
||||
.where(
|
||||
and(
|
||||
eq(statusHistory.entityType, entityType),
|
||||
eq(statusHistory.entityId, entityId),
|
||||
gte(statusHistory.timestamp, startSec)
|
||||
)
|
||||
)
|
||||
.orderBy(asc(statusHistory.timestamp));
|
||||
|
||||
const { buckets, totalDowntime } = computeBuckets(events, days);
|
||||
const totalWindow = days * 86400;
|
||||
const overallUptime =
|
||||
totalWindow > 0
|
||||
? Math.max(
|
||||
0,
|
||||
((totalWindow - totalDowntime) / totalWindow) * 100
|
||||
)
|
||||
: 100;
|
||||
const data = await getCachedStatusHistory(entityType, entityId, days);
|
||||
|
||||
return response<StatusHistoryResponse>(res, {
|
||||
data: {
|
||||
entityType,
|
||||
entityId,
|
||||
days: buckets,
|
||||
overallUptimePercent: Math.round(overallUptime * 100) / 100,
|
||||
totalDowntimeSeconds: totalDowntime
|
||||
},
|
||||
data,
|
||||
success: true,
|
||||
error: false,
|
||||
message: "Status history retrieved successfully",
|
||||
@@ -103,4 +72,4 @@ export async function getHealthCheckStatusHistory(
|
||||
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "An error occurred")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user