Merge 00b09924a6
into d522d1bf26
This commit is contained in:
commit
e5a084ec15
|
@ -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, type QueryRunner } from 'typeorm';
|
import { DataSource, EntityManager, 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';
|
||||||
import { Config } from '@/config.js';
|
import { Config } from '@/config.js';
|
||||||
|
@ -262,6 +262,33 @@ export const entities = [
|
||||||
|
|
||||||
const log = process.env.NODE_ENV !== 'production';
|
const log = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute query with extended timeout.
|
||||||
|
* This can be used to execute long-running queries like deleting many rows CASCADE-ly.
|
||||||
|
*
|
||||||
|
* TODO: consider remove this function when we removed CASCADE delete.
|
||||||
|
*/
|
||||||
|
export async function extendTimeoutQuery(dataSource: DataSource, query: (manager: EntityManager) => Promise<void>): Promise<void> {
|
||||||
|
const queryRunner = dataSource.createQueryRunner('master');
|
||||||
|
const manager = dataSource.createEntityManager(queryRunner);
|
||||||
|
const extendedTimeout = 1000 * 100; // 100 sec for now. How long should it be?
|
||||||
|
try {
|
||||||
|
await queryRunner.connect();
|
||||||
|
try {
|
||||||
|
// it looks postgres doesn't support statement_timeout with placeholder
|
||||||
|
// await manager.query(`set statement_timeout to $1`, [extendedTimeout]);
|
||||||
|
await manager.query(`set statement_timeout to ${extendedTimeout}`);
|
||||||
|
await query(manager);
|
||||||
|
} finally {
|
||||||
|
await manager.query(`set statement_timeout to ${default_statement_timeout}`);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
await queryRunner.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const default_statement_timeout = 1000 * 10;
|
||||||
|
|
||||||
export function createPostgresDataSource(config: Config) {
|
export function createPostgresDataSource(config: Config) {
|
||||||
return new DataSource({
|
return new DataSource({
|
||||||
type: 'postgres',
|
type: 'postgres',
|
||||||
|
@ -271,7 +298,7 @@ export function createPostgresDataSource(config: Config) {
|
||||||
password: config.db.pass,
|
password: config.db.pass,
|
||||||
database: config.db.db,
|
database: config.db.db,
|
||||||
extra: {
|
extra: {
|
||||||
statement_timeout: 1000 * 10,
|
statement_timeout: default_statement_timeout,
|
||||||
...config.db.extra,
|
...config.db.extra,
|
||||||
},
|
},
|
||||||
...(config.dbReplications ? {
|
...(config.dbReplications ? {
|
||||||
|
|
|
@ -4,16 +4,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { Inject, Injectable } from '@nestjs/common';
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { MoreThan } from 'typeorm';
|
import { DataSource, MoreThan } from 'typeorm';
|
||||||
import { DI } from '@/di-symbols.js';
|
import { DI } from '@/di-symbols.js';
|
||||||
import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
import type { DriveFilesRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
|
||||||
|
import { MiUser } from '@/models/User.js';
|
||||||
import type Logger from '@/logger.js';
|
import type Logger from '@/logger.js';
|
||||||
import { DriveService } from '@/core/DriveService.js';
|
import { DriveService } from '@/core/DriveService.js';
|
||||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||||
import type { MiNote } from '@/models/Note.js';
|
import { MiNote } from '@/models/Note.js';
|
||||||
import { EmailService } from '@/core/EmailService.js';
|
import { EmailService } from '@/core/EmailService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import { SearchService } from '@/core/SearchService.js';
|
import { SearchService } from '@/core/SearchService.js';
|
||||||
|
import { extendTimeoutQuery } from '@/postgres.js';
|
||||||
import { QueueLoggerService } from '../QueueLoggerService.js';
|
import { QueueLoggerService } from '../QueueLoggerService.js';
|
||||||
import type * as Bull from 'bullmq';
|
import type * as Bull from 'bullmq';
|
||||||
import type { DbUserDeleteJobData } from '../types.js';
|
import type { DbUserDeleteJobData } from '../types.js';
|
||||||
|
@ -23,6 +25,9 @@ export class DeleteAccountProcessorService {
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(DI.db)
|
||||||
|
private db: DataSource,
|
||||||
|
|
||||||
@Inject(DI.usersRepository)
|
@Inject(DI.usersRepository)
|
||||||
private usersRepository: UsersRepository,
|
private usersRepository: UsersRepository,
|
||||||
|
|
||||||
|
@ -73,7 +78,9 @@ export class DeleteAccountProcessorService {
|
||||||
|
|
||||||
cursor = notes.at(-1)?.id ?? null;
|
cursor = notes.at(-1)?.id ?? null;
|
||||||
|
|
||||||
await this.notesRepository.delete(notes.map(note => note.id));
|
await extendTimeoutQuery(this.db, async (manager) => {
|
||||||
|
await manager.delete(MiNote, notes.map(note => note.id));
|
||||||
|
});
|
||||||
|
|
||||||
for (const note of notes) {
|
for (const note of notes) {
|
||||||
await this.searchService.unindexNote(note);
|
await this.searchService.unindexNote(note);
|
||||||
|
@ -125,7 +132,9 @@ export class DeleteAccountProcessorService {
|
||||||
if (job.data.soft) {
|
if (job.data.soft) {
|
||||||
// nop
|
// nop
|
||||||
} else {
|
} else {
|
||||||
await this.usersRepository.delete(job.data.user.id);
|
await extendTimeoutQuery(this.db, async (manager) => {
|
||||||
|
await manager.delete(MiUser, job.data.user.id);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Account deleted';
|
return 'Account deleted';
|
||||||
|
|
Loading…
Reference in New Issue