Skip to content

Commit

Permalink
perf(core): Improve performance of role query with many channels
Browse files Browse the repository at this point in the history
Fixes #2910. In local testing with 505 channels, executing the Admin UI
`GetActiveAdministrator` query, the average response time was 29.05s.

With this optimization the average response came down to 106ms.
  • Loading branch information
michaelbromley committed Jul 15, 2024
1 parent 05cfc9d commit fe3e455
Showing 1 changed file with 18 additions and 5 deletions.
23 changes: 18 additions & 5 deletions packages/core/src/service/services/role.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { unique } from '@vendure/common/lib/unique';

import { RequestContext } from '../../api/common/request-context';
import { RelationPaths } from '../../api/decorators/relations.decorator';
import { RequestContextCacheService } from '../../cache/request-context-cache.service';
import { getAllPermissionsMetadata } from '../../common/constants';
import {
EntityNotFoundError,
Expand Down Expand Up @@ -56,6 +57,7 @@ export class RoleService {
private listQueryBuilder: ListQueryBuilder,
private configService: ConfigService,
private eventBus: EventBus,
private requestContextCache: RequestContextCacheService,
) {}

async initRoles() {
Expand Down Expand Up @@ -206,13 +208,24 @@ export class RoleService {
ctx: RequestContext,
channelId: ID,
): Promise<Permission[]> {
if (ctx.activeUserId == null) {
const { activeUserId } = ctx;
if (activeUserId == null) {
return [];
}
const user = await this.connection.getEntityOrThrow(ctx, User, ctx.activeUserId, {
relations: ['roles', 'roles.channels'],
});
const userChannels = getUserChannelsPermissions(user);
// For apps with many channels, this is a performance bottleneck as it will be called
// for each channel in certain code paths such as the GetActiveAdministrator query in the
// admin ui. Caching the result prevents unbounded quadratic slowdown.
const userChannels = await this.requestContextCache.get(
ctx,
`RoleService.getActiveUserPermissionsOnChannel.user(${activeUserId})`,
async () => {
const user = await this.connection.getEntityOrThrow(ctx, User, activeUserId, {
relations: ['roles', 'roles.channels'],
});
return getUserChannelsPermissions(user);
},
);

const channel = userChannels.find(c => idsAreEqual(c.id, channelId));
if (!channel) {
return [];
Expand Down

0 comments on commit fe3e455

Please sign in to comment.