fix: refactor two commands in saving grade to prevent race conditions

This commit is contained in:
2024-01-20 13:13:59 +01:00
parent b897c775a9
commit 6b63fd498a
14 changed files with 140 additions and 97 deletions

View File

@@ -1,4 +1,5 @@
import { Injectable, InternalServerErrorException, Logger } from '@nestjs/common'
import { Injectable, InternalServerErrorException, Logger, NotFoundException } from '@nestjs/common'
import { pick } from 'remeda'
import {
createPrismaOrderBy,
@@ -224,4 +225,73 @@ export class CardsRepository {
throw new InternalServerErrorException(e?.message)
}
}
private async getSmartRandomCard(cards: Array<CardWithGrade>): Promise<CardWithGrade> {
const selectionPool: Array<CardWithGrade> = []
cards.forEach(card => {
// Calculate the average grade for the card
const averageGrade =
card.grades.length === 0
? 0
: card.grades.reduce((acc, grade) => acc + grade.grade, 0) / card.grades.length
// Calculate weight for the card, higher weight for lower grade card
const weight = 6 - averageGrade
// Add the card to the selection pool `weight` times
for (let i = 0; i < weight; i++) {
selectionPool.push(card)
}
})
return selectionPool[Math.floor(Math.random() * selectionPool.length)]
}
private async getNotDuplicateRandomCard(
cards: Array<CardWithGrade>,
previousCardId: string
): Promise<CardWithGrade> {
const randomCard = await this.getSmartRandomCard(cards)
if (!randomCard) {
this.logger.error(`No cards found in deck}`, {
previousCardId,
randomCard,
cards,
})
throw new NotFoundException(`No cards found in deck`)
}
if (randomCard.id === previousCardId && cards.length !== 1) {
return this.getNotDuplicateRandomCard(cards, previousCardId)
}
return randomCard
}
async getRandomCardInDeck(deckId: string, userId: string, previousCardId: string) {
const cards = await this.findCardsByDeckIdWithGrade(userId, deckId)
if (!cards.length) {
throw new NotFoundException(`No cards found in deck with id ${deckId}`)
}
const smartRandomCard = await this.getNotDuplicateRandomCard(cards, previousCardId)
return {
...pick(smartRandomCard, [
'id',
'question',
'answer',
'deckId',
'questionImg',
'answerImg',
'questionVideo',
'answerVideo',
'created',
'updated',
'shots',
]),
grade: smartRandomCard.grades[0]?.grade || 0,
}
}
}