From b233444ed6c7b39071cd23015c5d0e476857f8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sat, 1 Jun 2024 16:12:02 +0900 Subject: [PATCH 1/4] fix(backend): explicitly set query runner on insertOne --- packages/backend/src/models/_.ts | 39 ++++++++++++++++++++++++-------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index 2e6a41586e45..eccd1a70e48e 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -3,13 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, Repository, SelectQueryBuilder, TypeORMError } from 'typeorm'; -import { DriverUtils } from 'typeorm/driver/DriverUtils.js'; +import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, QueryRunner, ReplicationMode, Repository, SelectQueryBuilder } from 'typeorm'; import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js'; import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js'; import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js'; -import { ObjectUtils } from 'typeorm/util/ObjectUtils.js'; -import { OrmUtils } from 'typeorm/util/OrmUtils.js'; import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { MiAccessToken } from '@/models/AccessToken.js'; import { MiAd } from '@/models/Ad.js'; @@ -79,11 +76,16 @@ import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; import { MiReversiGame } from '@/models/ReversiGame.js'; import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js'; +interface AsyncDisposableReference extends AsyncDisposable { + readonly value: T; +} + export interface MiRepository { createTableColumnNames(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder): string[]; createTableColumnNamesWithPrimaryKey(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder): string[]; insertOne(this: Repository & MiRepository, entity: QueryDeepPartialEntity, findOptions?: Pick, 'relations'>): Promise; selectAliasColumnNames(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder, builder: SelectQueryBuilder): void; + useQueryRunner(this: Repository & MiRepository, mode: ReplicationMode): AsyncDisposableReference; } export const miRepository = { @@ -110,14 +112,16 @@ export const miRepository = { return columnNames; }, async insertOne(entity, findOptions?) { - const queryBuilder = this.createQueryBuilder().insert().values(entity); + await using queryRunnerADR = this.useQueryRunner('master'); + const queryRunner = queryRunnerADR.value; + const queryBuilder = this.createQueryBuilder(undefined, queryRunner).insert().values(entity); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const mainAlias = queryBuilder.expressionMap.mainAlias!; const name = mainAlias.name; mainAlias.name = 't'; const columnNames = this.createTableColumnNamesWithPrimaryKey(queryBuilder); queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2)); - const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames }); + const builder = this.createQueryBuilder(undefined, queryRunner).addCommonTableExpression(queryBuilder, 'cte', { columnNames }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion builder.expressionMap.mainAlias!.tablePath = 'cte'; this.selectAliasColumnNames(queryBuilder, builder); @@ -126,9 +130,9 @@ export const miRepository = { } const raw = await builder.execute(); mainAlias.name = name; - const relationId = await new RelationIdLoader(builder.connection, this.queryRunner, builder.expressionMap.relationIdAttributes).load(raw); - const relationCount = await new RelationCountLoader(builder.connection, this.queryRunner, builder.expressionMap.relationCountAttributes).load(raw); - const result = new RawSqlResultsToEntityTransformer(builder.expressionMap, builder.connection.driver, relationId, relationCount, this.queryRunner).transform(raw, mainAlias); + const relationId = await new RelationIdLoader(builder.connection, queryRunner, builder.expressionMap.relationIdAttributes).load(raw); + const relationCount = await new RelationCountLoader(builder.connection, queryRunner, builder.expressionMap.relationCountAttributes).load(raw); + const result = new RawSqlResultsToEntityTransformer(builder.expressionMap, builder.connection.driver, relationId, relationCount, queryRunner).transform(raw, mainAlias); return result[0]; }, selectAliasColumnNames(queryBuilder, builder) { @@ -140,6 +144,23 @@ export const miRepository = { selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`); } }, + useQueryRunner(mode) { + if (this.queryRunner?.getReplicationMode() === mode) { + return { + value: this.queryRunner, + [Symbol.asyncDispose]() { + return Promise.resolve(); + }, + }; + } + const queryRunner = this.manager.connection.createQueryRunner(mode); + return { + value: queryRunner, + [Symbol.asyncDispose]() { + return queryRunner.release(); + }, + }; + }, } satisfies MiRepository; export { From 623eb410bb6dddb7292780ac6e1bd5a78a882478 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sat, 1 Jun 2024 16:28:07 +0900 Subject: [PATCH 2/4] test(backend): polyfill erm --- packages/backend/test/jest.setup.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts index 861bc6db669b..29a67da711a1 100644 --- a/packages/backend/test/jest.setup.ts +++ b/packages/backend/test/jest.setup.ts @@ -5,6 +5,13 @@ import { initTestDb, sendEnvResetRequest } from './utils.js'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +Symbol.dispose ??= Symbol('Symbol.dispose'); +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); + beforeAll(async () => { await Promise.all([ initTestDb(false), From 93665155657198ea011a714057ba703010b4c479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sat, 1 Jun 2024 16:39:34 +0900 Subject: [PATCH 3/4] test(backend): polyfill position --- packages/backend/test/jest.setup.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts index 29a67da711a1..fdb2f685c9f7 100644 --- a/packages/backend/test/jest.setup.ts +++ b/packages/backend/test/jest.setup.ts @@ -5,14 +5,14 @@ import { initTestDb, sendEnvResetRequest } from './utils.js'; -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error -Symbol.dispose ??= Symbol('Symbol.dispose'); -// eslint-disable-next-line @typescript-eslint/ban-ts-comment -// @ts-expect-error -Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); - beforeAll(async () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + Symbol.dispose ??= Symbol('Symbol.dispose'); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); + await Promise.all([ initTestDb(false), sendEnvResetRequest(), From d02198417b7ec38cbaf8f92904592b43a2ce8a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Acid=20Chicken=20=28=E7=A1=AB=E9=85=B8=E9=B6=8F=29?= Date: Sat, 1 Jun 2024 16:51:07 +0900 Subject: [PATCH 4/4] chore(backend): polyfill position --- packages/backend/src/models/_.ts | 8 ++++++++ packages/backend/test/jest.setup.ts | 7 ------- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index eccd1a70e48e..8f8e5c3cce7e 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -80,6 +80,14 @@ interface AsyncDisposableReference extends AsyncDisposable { readonly value: T; } +// SEEALSO: +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +Symbol.dispose ??= Symbol('Symbol.dispose'); +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-expect-error +Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); + export interface MiRepository { createTableColumnNames(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder): string[]; createTableColumnNamesWithPrimaryKey(this: Repository & MiRepository, queryBuilder: InsertQueryBuilder): string[]; diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts index fdb2f685c9f7..861bc6db669b 100644 --- a/packages/backend/test/jest.setup.ts +++ b/packages/backend/test/jest.setup.ts @@ -6,13 +6,6 @@ import { initTestDb, sendEnvResetRequest } from './utils.js'; beforeAll(async () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - Symbol.dispose ??= Symbol('Symbol.dispose'); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - Symbol.asyncDispose ??= Symbol('Symbol.asyncDispose'); - await Promise.all([ initTestDb(false), sendEnvResetRequest(),