Skip to content

Commit

Permalink
fix: dont use settimeout for promise retries
Browse files Browse the repository at this point in the history
  • Loading branch information
Geczy committed Aug 26, 2023
1 parent 40fd151 commit bf1c90b
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 74 deletions.
1 change: 0 additions & 1 deletion packages/dota/src/dota/GSIHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ export class GSIHandler {
}

public async resetClientState() {
console.log('resetting all data')
await deleteRedisData(this.client)
this.mapBlocker.resetData()
this.resetPlayerData()
Expand Down
9 changes: 2 additions & 7 deletions packages/dota/src/dota/lib/getPlayers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cards, delayedGames } from '@dotabod/prisma/dist/mongo/index.js'
import { delayedGames } from '@dotabod/prisma/dist/mongo/index.js'
import { t } from 'i18next'

import Dota from '../../steam/index.js'
Expand All @@ -13,12 +13,7 @@ export async function getPlayers(
locale: string,
currentMatchId?: string,
players?: { heroid: number; accountid: number }[],
): Promise<{
matchPlayers: { heroid: number; accountid: number }[]
accountIds: number[]
cards: cards[]
gameMode?: number
}> {
) {
if (!currentMatchId) {
throw new CustomError(t('notPlaying', { emote: 'PauseChamp', lng: locale }))
}
Expand Down
60 changes: 20 additions & 40 deletions packages/dota/src/steam/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ import { isDev } from '../dota/lib/consts.js'
import { getAccountsFromMatch } from '../dota/lib/getAccountsFromMatch.js'
import { GCMatchData } from '../types.js'
import CustomError from '../utils/customError.js'
import { promiseTimeout } from '../utils/index.js'
import { retryCustom } from '../utils/index.js'
import { logger } from '../utils/logger.js'
import Mongo from './mongo.js'

export const mongoClient = await Mongo.connect()

// Fetches data from MongoDB
const fetchDataFromMongo = async (match_id: string) => {
return await mongoClient
Expand Down Expand Up @@ -91,21 +93,6 @@ interface steamUserDetails {
sha_sentryfile?: Buffer
}

const waitCustom = (time: number) => new Promise((resolve) => setTimeout(resolve, time || 0))
const retryCustom = async (cont: number, fn: () => Promise<any>, delay: number): Promise<any> => {
for (let i = 0; i < cont; i++) {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return await fn()
} catch (err) {
await waitCustom(delay)
}
}
return Promise.reject('Retry limit exceeded')
}

export const mongoClient = await Mongo.connect()

function hasSteamData(game?: delayedGames | null) {
const hasTeams = Array.isArray(game?.teams) && game?.teams.length === 2
const hasPlayers =
Expand Down Expand Up @@ -409,7 +396,7 @@ class Dota {
return Dota.instance
}

public async getCards(accounts: number[], refetchCards = false) {
public async getCards(accounts: number[], refetchCards = false): Promise<cards[]> {
const cardsFromDb = await mongoClient
.collection<cards>('cards')
.find({ account_id: { $in: accounts.filter((a) => !!a) } })
Expand All @@ -419,16 +406,12 @@ class Dota {
const cardsMap = new Map(cardsFromDb.map((card) => [card.account_id, card]))

const fetchAndUpdateCard = async (accountId: number) => {
if (!accountId) return

const fetchedCard: cards | undefined = await retryCustom(
10,
() => this.getCard(accountId),
100,
).catch(() => ({
rank_tier: -10,
leaderboard_rank: 0,
}))
const fetchedCard = accountId
? await retryCustom(10, () => this.getCard(accountId), 1000).catch(() => ({
rank_tier: -10,
leaderboard_rank: 0,
}))
: undefined

const card = {
...fetchedCard,
Expand All @@ -438,6 +421,8 @@ class Dota {
leaderboard_rank: fetchedCard?.leaderboard_rank ?? 0,
} as cards

if (!accountId) return card

if (fetchedCard?.rank_tier !== -10) {
await mongoClient
.collection<cards>('cards')
Expand All @@ -447,8 +432,7 @@ class Dota {
return card
}

const promises = accounts.map(async (accountId, i) => {
if (!accountId) return []
const promises = accounts.map(async (accountId) => {
const existingCard = cardsMap.get(accountId)
if (refetchCards || !existingCard || typeof existingCard.rank_tier !== 'number') {
return fetchAndUpdateCard(accountId)
Expand All @@ -460,21 +444,17 @@ class Dota {
}

public async getCard(account: number): Promise<cards> {
// @ts-expect-error no types exist
// @ts-expect-error no types exist for `loggedOn`
if (!this.dota2._gcReady || !this.steamClient.loggedOn) {
throw new CustomError('Error getting medal')
}

return promiseTimeout(
new Promise((resolve, reject) => {
this.dota2.requestProfileCard(account, (err: any, card: cards) => {
if (err) reject(err)
else resolve(card)
})
}),
1000,
'Error getting medal',
)
return new Promise((resolve, reject) => {
this.dota2.requestProfileCard(account, (err: any, card: cards) => {
if (err) reject(err)
else resolve(card)
})
})
}

public exit(): Promise<boolean> {
Expand Down
51 changes: 25 additions & 26 deletions packages/dota/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import CustomError from './customError.js'
import retry from 'retry'

export function steamID64toSteamID32(steamID64: string) {
if (!steamID64) return null
Expand All @@ -21,30 +21,29 @@ export function fmtMSS(totalSeconds: number) {
}

export const wait = (time: number) => new Promise((resolve) => setTimeout(resolve, time || 0))
export const retry = (cont: number, fn: () => Promise<any>, delay: number): Promise<any> =>
fn().catch((err) =>
cont > 0 ? wait(delay).then(() => retry(cont - 1, fn, delay)) : Promise.reject(err),
)

export const promiseTimeout = <T>(promise: Promise<T>, ms: number, reason: string): Promise<T> =>
new Promise((resolve, reject) => {
let timeoutCleared = false
const timeoutId = setTimeout(() => {
timeoutCleared = true
reject(new CustomError(reason))
}, ms)

promise
.then((result: T) => {
if (!timeoutCleared) {
clearTimeout(timeoutId)
resolve(result)
}
})
.catch((err: any) => {
if (!timeoutCleared) {
clearTimeout(timeoutId)
reject(err)

export const retryCustom = async <T>(
retries: number,
fn: () => Promise<T>,
minTimeout: number,
): Promise<T> => {
return new Promise((resolve, reject) => {
const operation = retry.operation({
retries,
minTimeout,
})

// eslint-disable-next-line @typescript-eslint/no-misused-promises
operation.attempt(async (currentAttempt) => {
console.log({ currentAttempt })
try {
const result = await fn()
resolve(result)
} catch (err: any) {
if (!operation.retry(err)) {
reject(operation.mainError())
}
})
}
})
})
}

0 comments on commit bf1c90b

Please sign in to comment.