Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(backend): Improve performance of FetchInstanceMetadata #11128

Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

### Server
- JSON.parse の回数を削減することで、ストリーミングのパフォーマンスを向上しました
- 連合の配送ジョブのパフォーマンスを向上(ロック機構の見直し、Redisキャッシュの活用)

## 13.13.2

Expand Down
5 changes: 0 additions & 5 deletions packages/backend/src/core/AppLockService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ export class AppLockService {
return this.lock(`ap-object:${uri}`, timeout);
}

@bindThis
public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`instance:${host}`, timeout);
}

@bindThis
public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`chart-insert:${lockKey}`, timeout);
Expand Down
45 changes: 27 additions & 18 deletions packages/backend/src/core/FetchInstanceMetadataService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ import { Inject, Injectable } from '@nestjs/common';
import { JSDOM } from 'jsdom';
import tinycolor from 'tinycolor2';
import type { Instance } from '@/models/entities/Instance.js';
import type { InstancesRepository } from '@/models/index.js';
import { AppLockService } from '@/core/AppLockService.js';
import type Logger from '@/logger.js';
import { DI } from '@/di-symbols.js';
import { LoggerService } from '@/core/LoggerService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import type { DOMWindow } from 'jsdom';
import * as Redis from 'ioredis';

type NodeInfo = {
openRegistrations?: unknown;
Expand All @@ -37,33 +36,43 @@ export class FetchInstanceMetadataService {
private logger: Logger;

constructor(
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,

private appLockService: AppLockService,
private httpRequestService: HttpRequestService,
private loggerService: LoggerService,
private federatedInstanceService: FederatedInstanceService,
@Inject(DI.redis)
private redisClient: Redis.Redis,
) {
this.logger = this.loggerService.getLogger('metadata', 'cyan');
}

@bindThis
public async tryLock(host: string): Promise<boolean> {
const mutex = await this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '1', 'GET');
return mutex !== '1';
}

@bindThis
public unlock(host: string): Promise<'OK'> {
return this.redisClient.set(`fetchInstanceMetadata:mutex:${host}`, '0');
}

@bindThis
public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> {
const unlock = await this.appLockService.getFetchInstanceMetadataLock(instance.host);

if (!force) {
const _instance = await this.instancesRepository.findOneBy({ host: instance.host });
const now = Date.now();
if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) {
unlock();
return;
const host = instance.host;
// Acquire mutex to ensure no parallel runs
if (!await this.tryLock(host)) return;
try {
if (!force) {
const _instance = await this.federatedInstanceService.fetch(host);
const now = Date.now();
if (_instance && _instance.infoUpdatedAt && (now - _instance.infoUpdatedAt.getTime() < 1000 * 60 * 60 * 24)) {
// unlock at the finally caluse
return;
}
}
}

this.logger.info(`Fetching metadata of ${instance.host} ...`);
this.logger.info(`Fetching metadata of ${instance.host} ...`);

try {
const [info, dom, manifest] = await Promise.all([
this.fetchNodeinfo(instance).catch(() => null),
this.fetchDom(instance).catch(() => null),
Expand Down Expand Up @@ -104,7 +113,7 @@ export class FetchInstanceMetadataService {
} catch (e) {
this.logger.error(`Failed to update metadata of ${instance.host}: ${e}`);
} finally {
unlock();
await this.unlock(host);
}
}

Expand Down
Loading