perf(backend): avoid N+1 selects from user table when packing many entities (#13911)
* perf(backend): avoid N+1 selects from `user` table when packing many entities * perf(backend): use `packMany` instead of mapping to `pack`
This commit is contained in:
parent
97be1a53ad
commit
514a65e453
24 changed files with 268 additions and 87 deletions
|
|
@ -28,13 +28,15 @@ export class ReversiGameEntityService {
|
|||
@bindThis
|
||||
public async packDetail(
|
||||
src: MiReversiGame['id'] | MiReversiGame,
|
||||
hint?: {
|
||||
packedUser1?: Packed<'UserLite'>,
|
||||
packedUser2?: Packed<'UserLite'>,
|
||||
},
|
||||
): Promise<Packed<'ReversiGameDetailed'>> {
|
||||
const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const users = await Promise.all([
|
||||
this.userEntityService.pack(game.user1 ?? game.user1Id),
|
||||
this.userEntityService.pack(game.user2 ?? game.user2Id),
|
||||
]);
|
||||
const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
|
||||
const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
|
||||
|
||||
return await awaitAll({
|
||||
id: game.id,
|
||||
|
|
@ -49,10 +51,10 @@ export class ReversiGameEntityService {
|
|||
user2Ready: game.user2Ready,
|
||||
user1Id: game.user1Id,
|
||||
user2Id: game.user2Id,
|
||||
user1: users[0],
|
||||
user2: users[1],
|
||||
user1,
|
||||
user2,
|
||||
winnerId: game.winnerId,
|
||||
winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
|
||||
winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
|
||||
surrenderedUserId: game.surrenderedUserId,
|
||||
timeoutUserId: game.timeoutUserId,
|
||||
black: game.black,
|
||||
|
|
@ -68,22 +70,35 @@ export class ReversiGameEntityService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public packDetailMany(
|
||||
xs: MiReversiGame[],
|
||||
public async packDetailMany(
|
||||
games: MiReversiGame[],
|
||||
) {
|
||||
return Promise.all(xs.map(x => this.packDetail(x)));
|
||||
const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
|
||||
const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
|
||||
const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
games.map(game => {
|
||||
return this.packDetail(game, {
|
||||
packedUser1: _userMap.get(game.user1Id),
|
||||
packedUser2: _userMap.get(game.user2Id),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packLite(
|
||||
src: MiReversiGame['id'] | MiReversiGame,
|
||||
hint?: {
|
||||
packedUser1?: Packed<'UserLite'>,
|
||||
packedUser2?: Packed<'UserLite'>,
|
||||
},
|
||||
): Promise<Packed<'ReversiGameLite'>> {
|
||||
const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const users = await Promise.all([
|
||||
this.userEntityService.pack(game.user1 ?? game.user1Id),
|
||||
this.userEntityService.pack(game.user2 ?? game.user2Id),
|
||||
]);
|
||||
const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
|
||||
const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
|
||||
|
||||
return await awaitAll({
|
||||
id: game.id,
|
||||
|
|
@ -94,10 +109,10 @@ export class ReversiGameEntityService {
|
|||
isEnded: game.isEnded,
|
||||
user1Id: game.user1Id,
|
||||
user2Id: game.user2Id,
|
||||
user1: users[0],
|
||||
user2: users[1],
|
||||
user1,
|
||||
user2,
|
||||
winnerId: game.winnerId,
|
||||
winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
|
||||
winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
|
||||
surrenderedUserId: game.surrenderedUserId,
|
||||
timeoutUserId: game.timeoutUserId,
|
||||
black: game.black,
|
||||
|
|
@ -111,10 +126,21 @@ export class ReversiGameEntityService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public packLiteMany(
|
||||
xs: MiReversiGame[],
|
||||
public async packLiteMany(
|
||||
games: MiReversiGame[],
|
||||
) {
|
||||
return Promise.all(xs.map(x => this.packLite(x)));
|
||||
const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
|
||||
const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
|
||||
const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
|
||||
.then(users => new Map(users.map(u => [u.id, u])));
|
||||
return Promise.all(
|
||||
games.map(game => {
|
||||
return this.packLite(game, {
|
||||
packedUser1: _userMap.get(game.user1Id),
|
||||
packedUser2: _userMap.get(game.user2Id),
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue