From cf2c653242e767ada181892c3ed70d09d165ff97 Mon Sep 17 00:00:00 2001 From: Zisu Zhang Date: Sat, 20 Jan 2024 21:51:47 +0800 Subject: [PATCH] feat!: ranker consistent sync (#26) * feat!: using consistent sort for ranker sync Breaks current ranker implementations * chore: bump version * chore: add types to solution sync api --- .yarn/versions/6327b22c.yml | 2 + apps/server/src/db/contest.ts | 1 + apps/server/src/db/solution.ts | 8 ++++ apps/server/src/routes/runner/ranklist.ts | 47 ++++++++++++++++++----- 4 files changed, 49 insertions(+), 9 deletions(-) create mode 100644 .yarn/versions/6327b22c.yml diff --git a/.yarn/versions/6327b22c.yml b/.yarn/versions/6327b22c.yml new file mode 100644 index 0000000..1a06f8b --- /dev/null +++ b/.yarn/versions/6327b22c.yml @@ -0,0 +1,2 @@ +releases: + "@aoi-js/server": patch diff --git a/apps/server/src/db/contest.ts b/apps/server/src/db/contest.ts index 6d491e7..46cf322 100644 --- a/apps/server/src/db/contest.ts +++ b/apps/server/src/db/contest.ts @@ -35,6 +35,7 @@ export interface IContestParticipant { export const contestParticipants = db.collection('contestParticipants') await contestParticipants.createIndex({ userId: 1, contestId: 1 }, { unique: true }) +await contestParticipants.createIndex({ contestId: 1, updatedAt: 1, _id: 1 }) export interface IContestProblem { problemId: BSON.UUID diff --git a/apps/server/src/db/solution.ts b/apps/server/src/db/solution.ts index 4f0d34f..b2a28b6 100644 --- a/apps/server/src/db/solution.ts +++ b/apps/server/src/db/solution.ts @@ -45,3 +45,11 @@ await solutions.createIndex( ) await solutions.createIndex({ problemId: 1, submittedAt: -1 }) await solutions.createIndex({ contestId: 1, submittedAt: -1 }) +await solutions.createIndex( + { contestId: 1, completedAt: 1, _id: 1 }, + { + partialFilterExpression: { + state: SolutionState.COMPLETED + } + } +) diff --git a/apps/server/src/routes/runner/ranklist.ts b/apps/server/src/routes/runner/ranklist.ts index e28933a..8a8c114 100644 --- a/apps/server/src/routes/runner/ranklist.ts +++ b/apps/server/src/routes/runner/ranklist.ts @@ -1,4 +1,4 @@ -import { BSON } from 'mongodb' +import { BSON, UUID } from 'mongodb' import { ContestRanklistState, SolutionState, @@ -119,7 +119,8 @@ const runnerRanklistTaskRoutes = defineRoutes(async (s) => { schema: { description: 'Get participants for contest', querystring: Type.Object({ - since: Type.Number() + since: Type.Number(), + lastId: Type.UUID() }), response: { 200: Type.Array( @@ -158,9 +159,12 @@ const runnerRanklistTaskRoutes = defineRoutes(async (s) => { .find( { contestId: ctx._contestId, - updatedAt: { $gt: req.query.since } + $or: [ + { updatedAt: { $gt: req.query.since } }, + { updatedAt: req.query.since, _id: { $gt: new UUID(req.query.lastId) } } + ] }, - { limit: 50, sort: { updatedAt: 1 } } + { limit: 50, sort: { updatedAt: 1, _id: 1 } } ) .toArray() return list @@ -173,8 +177,29 @@ const runnerRanklistTaskRoutes = defineRoutes(async (s) => { schema: { description: 'Get solutions for contest', querystring: Type.Object({ - since: Type.Number() - }) + since: Type.Number(), + lastId: Type.UUID() + }), + response: { + 200: Type.Array( + Type.Object({ + _id: Type.UUID(), + problemId: Type.UUID(), + userId: Type.UUID(), + label: Type.String(), + problemDataHash: Type.String(), + state: Type.Integer(), + solutionDataHash: Type.String(), + score: Type.Number(), + metrics: Type.Record(Type.String(), Type.Number()), + status: Type.String(), + message: Type.String(), + createdAt: Type.Integer(), + submittedAt: Type.Integer(), + completedAt: Type.Integer() + }) + ) + } } }, async (req, rep) => { @@ -190,12 +215,16 @@ const runnerRanklistTaskRoutes = defineRoutes(async (s) => { { contestId: ctx._contestId, state: SolutionState.COMPLETED, - completedAt: { $gt: req.query.since } + $or: [ + { completedAt: { $gt: req.query.since } }, + { completedAt: req.query.since, _id: { $gt: new UUID(req.query.lastId) } } + ] }, - { limit: 50, projection: { taskId: 0 }, sort: { completedAt: 1 } } + { limit: 50, projection: { taskId: 0 }, sort: { completedAt: 1, _id: 1 } } ) .toArray() - return list + // Since state is COMPLETED, we can safely cast to the correct type + return list as Array<(typeof list)[number] & { submittedAt: number; completedAt: number }> } )