perf(server): index messageChannel/calendarChannel for per-workspace sync crons (#20678)
## Summary The messaging/calendar import crons each iterate every active workspace and execute one `find` per workspace against `core."messageChannel"` / `core."calendarChannel"` with the shape: ``` WHERE "workspaceId" = $1 AND "isSyncEnabled" = true AND "syncStage" = $2 [AND "type" <> $3] ``` There is currently no index supporting that shape, so the planner does a seq scan on each table for every iteration. On prod-eu (RDS Performance Insights, `rds-prod-eu-one`), these two queries are the top two by load — together ~12 AAS, ~12 calls/sec — and have been the primary contributor to the sustained 100% CPU since active workspace count grew. This PR adds composite indexes on `(workspaceId, isSyncEnabled, syncStage)` for both tables as an instance migration in 2.6.0.
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
const MESSAGE_CHANNEL_INDEX_NAME =
|
||||
'IDX_MESSAGE_CHANNEL_WORKSPACE_ID_SYNC_ENABLED_SYNC_STAGE';
|
||||
const CALENDAR_CHANNEL_INDEX_NAME =
|
||||
'IDX_CALENDAR_CHANNEL_WORKSPACE_ID_SYNC_ENABLED_SYNC_STAGE';
|
||||
|
||||
@RegisteredInstanceCommand('2.6.0', 1798000010000)
|
||||
export class AddChannelSyncStageIndexesFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX IF NOT EXISTS "${MESSAGE_CHANNEL_INDEX_NAME}" ON "core"."messageChannel" ("workspaceId", "isSyncEnabled", "syncStage")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX IF NOT EXISTS "${CALENDAR_CHANNEL_INDEX_NAME}" ON "core"."calendarChannel" ("workspaceId", "isSyncEnabled", "syncStage")`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."${CALENDAR_CHANNEL_INDEX_NAME}"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."${MESSAGE_CHANNEL_INDEX_NAME}"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+2
@@ -48,6 +48,7 @@ import { EncryptTotpSecretsSlowInstanceCommand } from 'src/database/commands/upg
|
||||
import { AddSubFieldNameToViewSortFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-fast-1778502963794-add-sub-field-name-to-view-sort';
|
||||
import { DropPostgresCredentialsTableFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-fast-1798500000000-drop-postgres-credentials-table';
|
||||
import { AddRelationTargetFieldMetadataIdToViewFilterFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1798000005000-add-relation-target-field-metadata-id-to-view-filter';
|
||||
import { AddChannelSyncStageIndexesFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1798000010000-add-channel-sync-stage-indexes';
|
||||
|
||||
export const INSTANCE_COMMANDS = [
|
||||
AddViewFieldGroupIdIndexOnViewFieldFastInstanceCommand,
|
||||
@@ -98,4 +99,5 @@ export const INSTANCE_COMMANDS = [
|
||||
AddSubFieldNameToViewSortFastInstanceCommand,
|
||||
DropPostgresCredentialsTableFastInstanceCommand,
|
||||
AddRelationTargetFieldMetadataIdToViewFilterFastInstanceCommand,
|
||||
AddChannelSyncStageIndexesFastInstanceCommand,
|
||||
];
|
||||
|
||||
+6
@@ -4,6 +4,7 @@ import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
@@ -35,6 +36,11 @@ registerEnumType(CalendarChannelContactAutoCreationPolicy, {
|
||||
});
|
||||
|
||||
@Entity({ name: 'calendarChannel', schema: 'core' })
|
||||
@Index('IDX_CALENDAR_CHANNEL_WORKSPACE_ID_SYNC_ENABLED_SYNC_STAGE', [
|
||||
'workspaceId',
|
||||
'isSyncEnabled',
|
||||
'syncStage',
|
||||
])
|
||||
export class CalendarChannelEntity extends WorkspaceRelatedEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
+6
@@ -4,6 +4,7 @@ import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
@@ -47,6 +48,11 @@ registerEnumType(MessageChannelPendingGroupEmailsAction, {
|
||||
});
|
||||
|
||||
@Entity({ name: 'messageChannel', schema: 'core' })
|
||||
@Index('IDX_MESSAGE_CHANNEL_WORKSPACE_ID_SYNC_ENABLED_SYNC_STAGE', [
|
||||
'workspaceId',
|
||||
'isSyncEnabled',
|
||||
'syncStage',
|
||||
])
|
||||
export class MessageChannelEntity extends WorkspaceRelatedEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user