This commit is contained in:
おさむのひと 2024-12-22 13:58:15 +09:00 committed by GitHub
commit c59aa4f634
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 62 additions and 16 deletions

View File

@ -18,7 +18,7 @@
- Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 ) - Fix: ユーザーのプロフィール画面をアドレス入力などで直接表示した際に概要タブの描画に失敗する問題の修正( #15032 )
- Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正 - Fix: 起動前の疎通チェックが機能しなくなっていた問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/737) (Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/737)
- Fix: リードレプリカ設定時にレコードの追加・更新・削除を伴うクエリを発行した際はmasterードで実行されるように調整( #10897 )
## 2024.11.0 ## 2024.11.0

View File

@ -3,13 +3,20 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { FindOneOptions, InsertQueryBuilder, ObjectLiteral, Repository, SelectQueryBuilder, TypeORMError } from 'typeorm'; import {
import { DriverUtils } from 'typeorm/driver/DriverUtils.js'; FindOneOptions,
InsertQueryBuilder,
ObjectLiteral,
QueryRunner,
Repository,
SelectQueryBuilder,
} from 'typeorm';
import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js'; import { RelationCountLoader } from 'typeorm/query-builder/relation-count/RelationCountLoader.js';
import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js'; import { RelationIdLoader } from 'typeorm/query-builder/relation-id/RelationIdLoader.js';
import { RawSqlResultsToEntityTransformer } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js'; import {
import { ObjectUtils } from 'typeorm/util/ObjectUtils.js'; RawSqlResultsToEntityTransformer,
import { OrmUtils } from 'typeorm/util/OrmUtils.js'; } from 'typeorm/query-builder/transformer/RawSqlResultsToEntityTransformer.js';
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions.js';
import { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js'; import { MiAbuseReportNotificationRecipient } from '@/models/AbuseReportNotificationRecipient.js';
import { MiAccessToken } from '@/models/AccessToken.js'; import { MiAccessToken } from '@/models/AccessToken.js';
@ -83,7 +90,11 @@ import type { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialE
export interface MiRepository<T extends ObjectLiteral> { export interface MiRepository<T extends ObjectLiteral> {
createTableColumnNames(this: Repository<T> & MiRepository<T>): string[]; 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; selectAliasColumnNames(this: Repository<T> & MiRepository<T>, queryBuilder: InsertQueryBuilder<T>, builder: SelectQueryBuilder<T>): void;
} }
@ -92,6 +103,21 @@ export const miRepository = {
return this.metadata.columns.filter(column => column.isSelect && !column.isVirtual).map(column => column.databaseName); 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;
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); const queryBuilder = this.createQueryBuilder().insert().values(entity);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const mainAlias = queryBuilder.expressionMap.mainAlias!; const mainAlias = queryBuilder.expressionMap.mainAlias!;
@ -99,7 +125,9 @@ export const miRepository = {
mainAlias.name = 't'; mainAlias.name = 't';
const columnNames = this.createTableColumnNames(); const columnNames = this.createTableColumnNames();
queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2)); queryBuilder.returning(columnNames.reduce((a, c) => `${a}, ${queryBuilder.escape(c)}`, '').slice(2));
const builder = this.createQueryBuilder().addCommonTableExpression(queryBuilder, 'cte', { columnNames });
// ---- 共通テーブル式(CTE)から結果を取得 ----
const builder = this.createQueryBuilder(undefined, queryRunner).addCommonTableExpression(queryBuilder, 'cte', { columnNames });
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
builder.expressionMap.mainAlias!.tablePath = 'cte'; builder.expressionMap.mainAlias!.tablePath = 'cte';
this.selectAliasColumnNames(queryBuilder, builder); this.selectAliasColumnNames(queryBuilder, builder);
@ -197,7 +225,9 @@ export {
}; };
export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>; export type AbuseUserReportsRepository = Repository<MiAbuseUserReport> & MiRepository<MiAbuseUserReport>;
export type AbuseReportNotificationRecipientRepository = Repository<MiAbuseReportNotificationRecipient> & MiRepository<MiAbuseReportNotificationRecipient>; export type AbuseReportNotificationRecipientRepository =
Repository<MiAbuseReportNotificationRecipient>
& MiRepository<MiAbuseReportNotificationRecipient>;
export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>; export type AccessTokensRepository = Repository<MiAccessToken> & MiRepository<MiAccessToken>;
export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>; export type AdsRepository = Repository<MiAd> & MiRepository<MiAd>;
export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>; export type AnnouncementsRepository = Repository<MiAnnouncement> & MiRepository<MiAnnouncement>;

View File

@ -5,7 +5,7 @@
// https://github.com/typeorm/typeorm/issues/2400 // https://github.com/typeorm/typeorm/issues/2400
import pg from 'pg'; import pg from 'pg';
import { DataSource, Logger } from 'typeorm'; import { DataSource, Logger, type QueryRunner } from 'typeorm';
import * as highlight from 'cli-highlight'; import * as highlight from 'cli-highlight';
import { entities as charts } from '@/core/chart/entities.js'; import { entities as charts } from '@/core/chart/entities.js';
@ -90,6 +90,11 @@ export const dbLogger = new MisskeyLogger('db');
const sqlLogger = dbLogger.createSubLogger('sql', 'gray'); const sqlLogger = dbLogger.createSubLogger('sql', 'gray');
class MyCustomLogger implements Logger { class MyCustomLogger implements Logger {
constructor(
private printReplicationMode?: boolean,
) {
}
@bindThis @bindThis
private highlight(sql: string) { private highlight(sql: string) {
return highlight.highlight(sql, { return highlight.highlight(sql, {
@ -98,18 +103,29 @@ class MyCustomLogger implements Logger {
} }
@bindThis @bindThis
public logQuery(query: string, parameters?: any[]) { private appendPrefixIfNeeded(message: string, opts?: {
sqlLogger.info(this.highlight(query).substring(0, 100)); queryRunner?: QueryRunner;
}): string {
if (this.printReplicationMode && opts?.queryRunner) {
return `[${opts.queryRunner.getReplicationMode()}] ${message}`;
} else {
return message;
}
} }
@bindThis @bindThis
public logQueryError(error: string, query: string, parameters?: any[]) { public logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
sqlLogger.error(this.highlight(query)); sqlLogger.info(this.appendPrefixIfNeeded(this.highlight(query).substring(0, 100), { queryRunner }));
} }
@bindThis @bindThis
public logQuerySlow(time: number, query: string, parameters?: any[]) { public logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) {
sqlLogger.warn(this.highlight(query)); sqlLogger.error(this.appendPrefixIfNeeded(this.highlight(query), { queryRunner }));
}
@bindThis
public logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) {
sqlLogger.warn(this.appendPrefixIfNeeded(this.highlight(query), { queryRunner }));
} }
@bindThis @bindThis
@ -247,7 +263,7 @@ export function createPostgresDataSource(config: Config) {
}, },
} : false, } : false,
logging: log, logging: log,
logger: log ? new MyCustomLogger() : undefined, logger: log ? new MyCustomLogger(config.dbReplications) : undefined,
maxQueryExecutionTime: 300, maxQueryExecutionTime: 300,
entities: entities, entities: entities,
migrations: ['../../migration/*.js'], migrations: ['../../migration/*.js'],