Compare commits

...

2 Commits

Author SHA1 Message Date
Florian Metz
e63e1270aa chore: release v0.0.22 2024-09-15 02:25:38 +02:00
Florian Metz
f730e71bbf chore: test 2024-09-15 02:25:10 +02:00
7 changed files with 77 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "@premid/api-master",
"type": "module",
"version": "0.0.21",
"version": "0.0.22",
"private": true,
"description": "PreMiD's api master",
"license": "MPL-2.0",

View File

@@ -0,0 +1,40 @@
import { redis } from "../index.js";
import { activePresenceGauge } from "../tracing.js";
//* Track previously recorded services
const previousServices = new Set<string>();
//* Function to update the gauge with per-service counts
export async function updateActivePresenceGauge() {
const pattern = "pmd-api.heartbeatUpdates.*.*";
let cursor: string = "0";
const serviceCounts = new Map<string, number>();
do {
const [newCursor, keys] = await redis.scan(cursor, "MATCH", pattern, "COUNT", 1000); //* Use SCAN with COUNT for memory efficiency
cursor = newCursor;
for (const key of keys) {
const parts = key.split(".");
const service = parts[parts.length - 1]!;
const hash = await redis.hgetall(key);
const version = hash.version; //* Get version from hash
serviceCounts.set(`${service}:${version}`, (serviceCounts.get(`${service}:${version}`) || 0) + 1);
}
} while (cursor !== "0");
// Set current counts and remove from previousServices
serviceCounts.forEach((count, serviceVersion) => {
const [service, version] = serviceVersion.split(":");
activePresenceGauge.record(count, { service, version }); //* Include version in labels
previousServices.delete(serviceVersion);
});
// Set gauge to 0 for services that are no longer active
previousServices.forEach((serviceVersion) => {
const [service, version] = serviceVersion.split(":");
activePresenceGauge.record(0, { service, version });
});
// Update the set of previous services
serviceCounts.forEach((_, serviceVersion) => previousServices.add(serviceVersion));
}

View File

@@ -5,12 +5,13 @@ import { clearOldSessions } from "./functions/clearOldSessions.js";
import createRedis from "./functions/createRedis.js";
import { setCounter } from "./functions/setCounter.js";
import "./tracing.js";
import { updateActivePresenceGauge } from "./functions/updateActivePresenceGauge.js"; //* Added import
export const redis = createRedis();
export const mainLog = debug("api-master");
debug("Starting cron job to clear old sessions");
debug("Starting cron jobs");
void new CronJob(
// Every 5 seconds
@@ -31,3 +32,13 @@ void new CronJob(
undefined,
true,
);
void new CronJob(
// Every 5 seconds
"*/5 * * * * *",
() => {
updateActivePresenceGauge();
},
undefined,
true,
);

View File

@@ -15,4 +15,10 @@ export const counter = meter.createUpDownCounter("active_activites", {
valueType: ValueType.INT,
});
// * Replace Observable Gauge with regular Gauge
export const activePresenceGauge = meter.createGauge("active_presence_names", {
description: "Number of active presence names per service",
valueType: ValueType.INT,
});
prometheusExporter.startServer();

View File

@@ -47,7 +47,7 @@ export default async function createServer() {
maxDepthPlugin(),
maxDirectivesPlugin(),
maxTokensPlugin(),
useSentry(),
/* useSentry(), */
],
schema: createSchema<FastifyContext>({
resolvers,

View File

@@ -1,9 +1,10 @@
import { type } from "arktype";
import type { MutationResolvers } from "../../../../generated/graphql-v5.js";
import { redis } from "../../../../functions/createServer.js";
const heartbeatSchema = type({
identifier: "string.uuid & string.lower",
presences: {
presence: {
service: "string.trim",
version: "string.semver",
language: "string.trim",
@@ -25,13 +26,21 @@ const mutation: MutationResolvers["heartbeat"] = async (_parent, input) => {
if (out instanceof type.errors)
throw new Error(out.summary);
// ! Disabled for now
/* await redis.setex(
`pmd-api.heartbeatUpdates.${data.identifier}`,
// 5 minutes
300,
JSON.stringify(data)
); */
// * Use Redis Hash with 'service' in the key to store heartbeat data
const redisKey = `pmd-api.heartbeatUpdates.${out.identifier}.${out.presence.service}`;
await redis.hset(redisKey, {
service: out.presence.service,
version: out.presence.version,
language: out.presence.language,
since: out.presence.since.toString(),
extension_version: out.extension.version,
extension_language: out.extension.language,
extension_connected_app: out.extension.connected?.app?.toString() || "",
extension_connected_discord: out.extension.connected?.discord?.toString() || "",
});
await redis.expire(redisKey, 5);
// * End the custom metric or adjust as needed
return {
__typename: "HeartbeatResult",

View File

@@ -3,8 +3,6 @@ import process from "node:process";
import * as Sentry from "@sentry/node";
import { connect } from "mongoose";
import "./tracing.js";
// eslint-disable-next-line perfectionist/sort-imports
import createServer from "./functions/createServer.js";
// TODO SETUP SENTRY