From 89140c552f28c65f6bfcf7e664779693159553e0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 30 Nov 2025 00:40:02 +0000 Subject: [PATCH] Restore AppLockService and wrap acquireApObjectLock Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com> --- packages/backend/src/core/AppLockService.ts | 39 +++++++++++++++++++ packages/backend/src/core/CoreModule.ts | 6 +++ .../src/core/activitypub/ApInboxService.ts | 13 +++---- .../core/activitypub/models/ApNoteService.ts | 9 ++--- 4 files changed, 53 insertions(+), 14 deletions(-) create mode 100644 packages/backend/src/core/AppLockService.ts diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts new file mode 100644 index 0000000000..cdc5fe9da8 --- /dev/null +++ b/packages/backend/src/core/AppLockService.ts @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import { DI } from '@/di-symbols.js'; +import { acquireApObjectLock, acquireChartInsertLock } from '@/misc/distributed-lock.js'; +import { bindThis } from '@/decorators.js'; + +@Injectable() +export class AppLockService { + constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, + ) { + } + + /** + * Get AP Object lock + * @param uri AP object ID + * @returns Unlock function + */ + @bindThis + public getApLock(uri: string): Promise<() => Promise> { + return acquireApObjectLock(this.redisClient, uri); + } + + /** + * Get chart insert lock + * @param lockKey Lock key + * @returns Unlock function + */ + @bindThis + public getChartInsertLock(lockKey: string): Promise<() => Promise> { + return acquireChartInsertLock(this.redisClient, lockKey); + } +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 87575ca59a..3fb1f0e6fa 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -19,6 +19,7 @@ import { ChannelMutingService } from '@/core/ChannelMutingService.js'; import { AccountMoveService } from './AccountMoveService.js'; import { AccountUpdateService } from './AccountUpdateService.js'; import { AiService } from './AiService.js'; +import { AppLockService } from './AppLockService.js'; import { AnnouncementService } from './AnnouncementService.js'; import { AntennaService } from './AntennaService.js'; import { AchievementService } from './AchievementService.js'; @@ -163,6 +164,7 @@ const $AbuseReportNotificationService: Provider = { provide: 'AbuseReportNotific const $AccountMoveService: Provider = { provide: 'AccountMoveService', useExisting: AccountMoveService }; const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService }; const $AiService: Provider = { provide: 'AiService', useExisting: AiService }; +const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService }; const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService }; const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService }; const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService }; @@ -316,6 +318,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting AccountMoveService, AccountUpdateService, AiService, + AppLockService, AnnouncementService, AntennaService, AchievementService, @@ -465,6 +468,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $AccountMoveService, $AccountUpdateService, $AiService, + $AppLockService, $AnnouncementService, $AntennaService, $AchievementService, @@ -615,6 +619,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting AccountMoveService, AccountUpdateService, AiService, + AppLockService, AnnouncementService, AntennaService, AchievementService, @@ -763,6 +768,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $AccountMoveService, $AccountUpdateService, $AiService, + $AppLockService, $AnnouncementService, $AntennaService, $AchievementService, diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 81637580e3..1a0ef14108 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -5,7 +5,6 @@ import { Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { UserFollowingService } from '@/core/UserFollowingService.js'; @@ -15,7 +14,6 @@ import { NotePiningService } from '@/core/NotePiningService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; -import { acquireApObjectLock } from '@/misc/distributed-lock.js'; import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; import type Logger from '@/logger.js'; import { IdService } from '@/core/IdService.js'; @@ -30,6 +28,7 @@ import type { MiRemoteUser } from '@/models/User.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { AbuseReportService } from '@/core/AbuseReportService.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { AppLockService } from '@/core/AppLockService.js'; import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { ApNoteService } from './models/ApNoteService.js'; import { ApLoggerService } from './ApLoggerService.js'; @@ -49,9 +48,6 @@ export class ApInboxService { @Inject(DI.config) private config: Config, - @Inject(DI.redis) - private redisClient: Redis.Redis, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -85,6 +81,7 @@ export class ApInboxService { private apQuestionService: ApQuestionService, private queueService: QueueService, private globalEventService: GlobalEventService, + private appLockService: AppLockService, ) { this.logger = this.apLoggerService.logger; } @@ -311,7 +308,7 @@ export class ApInboxService { // アナウンス先が許可されているかチェック if (!this.utilityService.isFederationAllowedUri(uri)) return; - const unlock = await acquireApObjectLock(this.redisClient, uri); + const unlock = await this.appLockService.getApLock(uri); try { // 既に同じURIを持つものが登録されていないかチェック @@ -438,7 +435,7 @@ export class ApInboxService { } } - const unlock = await acquireApObjectLock(this.redisClient, uri); + const unlock = await this.appLockService.getApLock(uri); try { const exist = await this.apNoteService.fetchNote(note); @@ -522,7 +519,7 @@ export class ApInboxService { private async deleteNote(actor: MiRemoteUser, uri: string): Promise { this.logger.info(`Deleting the Note: ${uri}`); - const unlock = await acquireApObjectLock(this.redisClient, uri); + const unlock = await this.appLockService.getApLock(uri); try { const note = await this.apDbResolverService.getNoteFromApId(uri); diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 214d32f67f..689aaeedc4 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -5,13 +5,11 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; -import * as Redis from 'ioredis'; import { DI } from '@/di-symbols.js'; import type { PollsRepository, EmojisRepository, MiMeta } from '@/models/_.js'; import type { Config } from '@/config.js'; import type { MiRemoteUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; -import { acquireApObjectLock } from '@/misc/distributed-lock.js'; import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; import type { MiEmoji } from '@/models/Emoji.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; @@ -24,6 +22,7 @@ import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { checkHttps } from '@/misc/check-https.js'; import { IdentifiableError } from '@/misc/identifiable-error.js'; +import { AppLockService } from '@/core/AppLockService.js'; import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApMfmService } from '../ApMfmService.js'; @@ -49,9 +48,6 @@ export class ApNoteService { @Inject(DI.meta) private meta: MiMeta, - @Inject(DI.redis) - private redisClient: Redis.Redis, - @Inject(DI.pollsRepository) private pollsRepository: PollsRepository, @@ -75,6 +71,7 @@ export class ApNoteService { private noteCreateService: NoteCreateService, private apDbResolverService: ApDbResolverService, private apLoggerService: ApLoggerService, + private appLockService: AppLockService, ) { this.logger = this.apLoggerService.logger; } @@ -357,7 +354,7 @@ export class ApNoteService { throw new StatusError('blocked host', 451); } - const unlock = await acquireApObjectLock(this.redisClient, uri); + const unlock = await this.appLockService.getApLock(uri); try { //#region このサーバーに既に登録されていたらそれを返す