fix(backend): DBレプリケーションを利用する環境でクエリーが失敗する問題を修正 (#16842)
* fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正 (MisskeyIO#1123) * Update Changelog --------- Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com>
This commit is contained in:
parent
42706970f2
commit
f801d1cf0b
|
|
@ -20,6 +20,8 @@
|
||||||
- Fix: DeepL APIのAPIキー指定方式変更に対応
|
- Fix: DeepL APIのAPIキー指定方式変更に対応
|
||||||
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1096)
|
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1096)
|
||||||
- 内部実装の変更にて対応可能な更新です。Misskey側の設定方法に変更はありません。
|
- 内部実装の変更にて対応可能な更新です。Misskey側の設定方法に変更はありません。
|
||||||
|
- Fix: DBレプリケーションを利用する環境でクエリーが失敗する問題を修正
|
||||||
|
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/1123)
|
||||||
|
|
||||||
## 2025.11.0
|
## 2025.11.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,18 +5,9 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FindOneOptions,
|
FindOneOptions,
|
||||||
InsertQueryBuilder,
|
|
||||||
ObjectLiteral,
|
ObjectLiteral,
|
||||||
QueryRunner,
|
|
||||||
Repository,
|
Repository,
|
||||||
SelectQueryBuilder,
|
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
|
|
||||||
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 { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
|
import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
|
||||||
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
|
||||||
import { MiAccessToken } from '@/models/AccessToken.js';
|
import { MiAccessToken } from '@/models/AccessToken.js';
|
||||||
|
|
@ -96,66 +87,12 @@ import { MiWebhook } from '@/models/Webhook.js';
|
||||||
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity.js';
|
||||||
|
|
||||||
export interface MiRepository<T extends ObjectLiteral> {
|
export interface MiRepository<T extends ObjectLiteral> {
|
||||||
createTableColumnNames(this: Repository<T> & MiRepository<T>): string[];
|
|
||||||
|
|
||||||
insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
|
insertOne(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>): Promise<T>;
|
||||||
|
|
||||||
insertOneImpl(this: Repository<T> & MiRepository<T>, entity: QueryDeepPartialEntity<T>, findOptions?: Pick<FindOneOptions<T>, 'relations'>, queryRunner?: QueryRunner): Promise<T>;
|
|
||||||
|
|
||||||
selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const miRepository = {
|
export const miRepository = {
|
||||||
createTableColumnNames() {
|
|
||||||
return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName);
|
|
||||||
},
|
|
||||||
async insertOne(entity, findOptions?) {
|
async insertOne(entity, findOptions?) {
|
||||||
const opt = this.manager.connection.options as PostgresConnectionOptions;
|
return await this.insert(entity).then(x => this.findOneOrFail({ where: x.identifiers[0], ...findOptions }));
|
||||||
if (opt.replication) {
|
|
||||||
const queryRunner = this.manager.connection.createQueryRunner('master');
|
|
||||||
try {
|
|
||||||
return this.insertOneImpl(entity, findOptions, queryRunner);
|
|
||||||
} finally {
|
|
||||||
await queryRunner.release();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return this.insertOneImpl(entity, findOptions);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async insertOneImpl(entity, findOptions?, queryRunner?) {
|
|
||||||
// ---- insert + returningの結果を共通テーブル式(CTE)に保持するクエリを生成 ----
|
|
||||||
|
|
||||||
const queryBuilder = this.createQueryBuilder().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.createTableColumnNames();
|
|
||||||
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
|
|
||||||
|
|
||||||
// ---- 共通テーブル式(CTE)から結果を取得 ----
|
|
||||||
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);
|
|
||||||
if (findOptions) {
|
|
||||||
builder.setFindOptions(findOptions);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
return result[0];
|
|
||||||
},
|
|
||||||
selectAliasColumnNames(queryBuilder, builder) {
|
|
||||||
let selectOrAddSelect = (selection: string, selectionAliasName?: string) => {
|
|
||||||
selectOrAddSelect = (selection, selectionAliasName) => builder.addSelect(selection, selectionAliasName);
|
|
||||||
return builder.select(selection, selectionAliasName);
|
|
||||||
};
|
|
||||||
for (const columnName of this.createTableColumnNames()) {
|
|
||||||
selectOrAddSelect(`${builder.alias}.${columnName}`, `${builder.alias}_${columnName}`);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
} satisfies MiRepository<ObjectLiteral>;
|
} satisfies MiRepository<ObjectLiteral>;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue