From 5ca9e5667c826e894f1407460bd33f5b4e280ffa Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Wed, 1 May 2024 10:09:44 +0100 Subject: [PATCH] Add an admin API endpoint to get info about the shared marqs queue --- apps/webapp/app/routes/admin.api.v1.marqs.ts | 31 ++++++++++++++++++++ apps/webapp/app/v3/marqs/index.server.ts | 29 ++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 apps/webapp/app/routes/admin.api.v1.marqs.ts diff --git a/apps/webapp/app/routes/admin.api.v1.marqs.ts b/apps/webapp/app/routes/admin.api.v1.marqs.ts new file mode 100644 index 000000000..14a9fd409 --- /dev/null +++ b/apps/webapp/app/routes/admin.api.v1.marqs.ts @@ -0,0 +1,31 @@ +import { LoaderFunctionArgs, json } from "@remix-run/server-runtime"; +import { prisma } from "~/db.server"; +import { authenticateApiRequestWithPersonalAccessToken } from "~/services/personalAccessToken.server"; +import { marqs } from "~/v3/marqs/index.server"; + +export async function loader({ request, params }: LoaderFunctionArgs) { + // Next authenticate the request + const authenticationResult = await authenticateApiRequestWithPersonalAccessToken(request); + + if (!authenticationResult) { + return json({ error: "Invalid or Missing API key" }, { status: 401 }); + } + + const user = await prisma.user.findUnique({ + where: { + id: authenticationResult.userId, + }, + }); + + if (!user) { + return json({ error: "Invalid or Missing API key" }, { status: 401 }); + } + + if (!user.admin) { + return json({ error: "You must be an admin to perform this action" }, { status: 403 }); + } + + const details = await marqs?.getSharedQueueDetails(); + + return json(details); +} diff --git a/apps/webapp/app/v3/marqs/index.server.ts b/apps/webapp/app/v3/marqs/index.server.ts index f4658326a..bfb3cd82d 100644 --- a/apps/webapp/app/v3/marqs/index.server.ts +++ b/apps/webapp/app/v3/marqs/index.server.ts @@ -283,6 +283,35 @@ export class MarQS { ); } + public async getSharedQueueDetails() { + const parentQueue = constants.SHARED_QUEUE; + + const { range, selectionId } = await this.queuePriorityStrategy.nextCandidateSelection( + parentQueue + ); + const queues = await this.#zrangeWithScores(parentQueue, range[0], range[1]); + + const queuesWithScores = await this.#calculateQueueScores(queues, (queue) => + this.#calculateMessageQueueCapacities(queue) + ); + + // We need to priority shuffle here to ensure all workers aren't just working on the highest priority queue + const choice = this.queuePriorityStrategy.chooseQueue( + queuesWithScores, + parentQueue, + selectionId + ); + + return { + selectionId, + queues, + queuesWithScores, + nextRange: range, + queueCount: queues.length, + queueChoice: choice, + }; + } + /** * Dequeue a message from the shared queue (this should be used in production environments) */