* Add query caching for lthe long running leaderboard queries -- 2hr ttl

This commit is contained in:
Anthony Mineo 2021-09-27 11:56:17 -04:00
parent 4df3a1b594
commit 86e13db165
4 changed files with 1964 additions and 2838 deletions

4684
app/api/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -25,32 +25,34 @@
"test:e2e": "jest --config ./test/jest-e2e.json" "test:e2e": "jest --config ./test/jest-e2e.json"
}, },
"dependencies": { "dependencies": {
"@nestjs/common": "^7.4.4", "@nestjs/common": "^8.0.7",
"@nestjs/config": "^0.5.0", "@nestjs/config": "^1.0.1",
"@nestjs/core": "^7.4.4", "@nestjs/core": "^8.0.7",
"@nestjs/mapped-types": "^0.1.0", "@nestjs/mapped-types": "^1.0.0",
"@nestjs/platform-express": "^7.4.4", "@nestjs/platform-express": "^8.0.7",
"@nestjs/swagger": "^4.5.12", "@nestjs/swagger": "^5.0.9",
"@nestjs/typeorm": "^7.1.4", "@nestjs/typeorm": "^8.0.2",
"cache-manager": "^3.4.4",
"class-transformer": "^0.3.1", "class-transformer": "^0.3.1",
"class-validator": "^0.12.2", "class-validator": "^0.12.2",
"joi": "^17.2.1", "joi": "^17.4.2",
"pg": "^8.3.2", "pg": "^8.7.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rxjs": "^6.6.3", "rxjs": "^7.3.0",
"swagger-ui-express": "^4.1.4", "swagger-ui-express": "^4.1.6",
"typeorm": "^0.2.26" "typeorm": "^0.2.37"
}, },
"devDependencies": { "devDependencies": {
"@nestjs/cli": "^7.0.0", "@nestjs/cli": "^8.1.1",
"@nestjs/schematics": "^7.1.2", "@nestjs/schematics": "^8.0.3",
"@nestjs/testing": "^7.4.4", "@nestjs/testing": "^8.0.7",
"@types/express": "^4.17.8", "@types/cache-manager": "^3.4.2",
"@types/hapi__joi": "^17.1.4", "@types/express": "^4.17.13",
"@types/jest": "^26.0.14", "@types/hapi__joi": "^17.1.7",
"@types/node": "^14.11.1", "@types/jest": "^27.0.2",
"@types/supertest": "^2.0.8", "@types/node": "^16.10.1",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^3.10.1", "@typescript-eslint/eslint-plugin": "^3.10.1",
"@typescript-eslint/parser": "^3.10.1", "@typescript-eslint/parser": "^3.10.1",
"eslint": "^7.9.0", "eslint": "^7.9.0",
@ -63,7 +65,7 @@
"ts-loader": "^8.0.3", "ts-loader": "^8.0.3",
"ts-node": "^9.0.0", "ts-node": "^9.0.0",
"tsconfig-paths": "^3.9.0", "tsconfig-paths": "^3.9.0",
"typescript": "^4.0.2" "typescript": "^4.4.3"
}, },
"jest": { "jest": {
"moduleFileExtensions": [ "moduleFileExtensions": [

View file

@ -1,4 +1,5 @@
import { Controller, Get, Query, Param } from '@nestjs/common'; import { Controller, Get, Query, Param, Inject, CACHE_MANAGER } from '@nestjs/common';
import { Cache } from 'cache-manager';
import { ApiOperation } from '@nestjs/swagger'; import { ApiOperation } from '@nestjs/swagger';
import { PlayersService } from './players.service'; import { PlayersService } from './players.service';
@ -7,10 +8,14 @@ import {
TopAccuracyQueryDto, TopAccuracyQueryDto,
TopWinsQueryDto, TopWinsQueryDto,
} from '../common/dto/top-players-query.dto'; } from '../common/dto/top-players-query.dto';
import { cache } from 'joi';
@Controller('players') @Controller('players')
export class PlayersController { export class PlayersController {
constructor(private readonly playerService: PlayersService) {} constructor(
private readonly playerService: PlayersService,
@Inject(CACHE_MANAGER) private cacheManager: Cache
) {}
// /players // /players
@Get() @Get()
@ -25,7 +30,7 @@ export class PlayersController {
tags: ['Player', 'Leaderboard'], tags: ['Player', 'Leaderboard'],
summary: 'Return a leaderboard of players for a specific accuracy stat', summary: 'Return a leaderboard of players for a specific accuracy stat',
}) })
findTopAccuracy(@Query() topPlayersQuery: TopAccuracyQueryDto) { async findTopAccuracy(@Query() topPlayersQuery: TopAccuracyQueryDto) {
const { const {
stat, stat,
gameType, gameType,
@ -34,27 +39,54 @@ export class PlayersController {
limit = 10, limit = 10,
timePeriod, timePeriod,
} = topPlayersQuery; } = topPlayersQuery;
return this.playerService.findTopAccuracy({
stat, const cacheKey =`topacc_${stat}_${gameType}_${minGames}_${minShots}_${limit}_${timePeriod}`;
gameType, const queryCache = await this.cacheManager.get(cacheKey);
minGames,
minShots, if(!queryCache){
limit, const results = await this.playerService.findTopAccuracy({
timePeriod, stat,
}); gameType,
minGames,
minShots,
limit,
timePeriod,
});
await this.cacheManager.set(cacheKey, results, { ttl: 3600 * 2 }); // 2 hours
return results
}
return queryCache
} }
@Get('top/wins') @Get('top/wins')
@ApiOperation({ @ApiOperation({
tags: ['Player', 'Leaderboard'], tags: ['Player', 'Leaderboard'],
summary: 'Return a leaderboard of players for win percentage', summary: 'Return a leaderboard of players for win percentage',
}) })
findTopWins(@Query() topPlayersQuery: TopWinsQueryDto) { async findTopWins(@Query() topPlayersQuery: TopWinsQueryDto) {
const { minGames = 100, limit = 10, timePeriod } = topPlayersQuery; const { minGames = 100, limit = 10, timePeriod } = topPlayersQuery;
return this.playerService.findTopWins({
minGames, const cacheKey =`topwins_${minGames}_${limit}_${timePeriod}`;
limit, const queryCache = await this.cacheManager.get(cacheKey);
timePeriod,
}); /*
If we don't have a cache ready, lets make one so the next hit is super fast
Cache ttl is in seconds
*/
if(!queryCache){
const results = await this.playerService.findTopWins({
minGames,
limit,
timePeriod,
});
await this.cacheManager.set(cacheKey, results, { ttl: 3600 * 2 }); // 2 hours
return results
}
return queryCache
} }
} }

View file

@ -1,4 +1,4 @@
import { Module } from '@nestjs/common'; import { Module, CacheModule } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
@ -9,7 +9,11 @@ import { Players } from './entities/Players';
import { GameDetail } from '../game/entities/GameDetail'; import { GameDetail } from '../game/entities/GameDetail';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([Players, GameDetail]), ConfigModule], imports: [
CacheModule.register(),
TypeOrmModule.forFeature([Players, GameDetail]),
ConfigModule
],
providers: [PlayersService], providers: [PlayersService],
controllers: [PlayersController], controllers: [PlayersController],
}) })