summaryrefslogtreecommitdiff
path: root/packages/backend
diff options
context:
space:
mode:
authorrenovate[bot] <29139614+renovate[bot]@users.noreply.github.com>2025-11-30 09:58:06 +0900
committerGitHub <noreply@github.com>2025-11-30 09:58:06 +0900
commit0455187a689b45575121851e6998a3d13615567c (patch)
tree37c179d0bc9e9385673d922247c4dd826f4f0850 /packages/backend
parentBump version to 2025.11.2-alpha.0 (diff)
downloadmisskey-0455187a689b45575121851e6998a3d13615567c.tar.gz
misskey-0455187a689b45575121851e6998a3d13615567c.tar.bz2
misskey-0455187a689b45575121851e6998a3d13615567c.zip
fix(deps): update [backend] update dependencies (major) (#16099)
* fix(deps): update [backend] update dependencies * update approve builds * update minimum node version for testing * remove types/bcryptjs * fix(backend): remove removed type previously exported from file-type * migrate webauthnservice * Update Changelog * update deps (MisskeyIO#889) - メンテナンスされないredis-lockを自前実装に変更 - 既にロックされている場合のリトライ間隔を調整 * use main redis for lock * spdx * tweak max retries * [ci skip] dedupe * attempt to fix test * attempt to fix test * Revert "attempt to fix test" This reverts commit c508318627e4ffab030e1acf5182f58cc2eb51d8. * temporarily roll back simonjs/fake-timers to v11.3.1 * Revert "temporarily roll back simonjs/fake-timers to v11.3.1" This reverts commit 54f1fc3d7917089e05b20fc5b86435e3f5e5b040. * migrate sinonjs/fake-timers * update deps / migrate jest 30 * fix test * fix: update node.js min version to 20.18.1 * fix: rollback nsfwjs to 4.2.0 * fix * attempt to fix test * attempt to fix test * attempt to fix test * attempt to fix test * revert jest 30 related changes * update deps * fix test * fix: rollback nsfwjs to 4.2.0 * fix: rollback sharp to 0.33 * update deps * fix: rollback sharp-read-bmp to 1.2.0 * fix: rollback nsfwjs to 4.2.0 * recreate lockfile * update deps * fix: rollback sharp-read-bmp to 1.2.0 * fix: rollback jsdom, parse5 * fix: rollback jsdom types * fix [ci skip] * run pnpm dedupe * update deps * run pnpm dedupe [ci skip] * Update Changelog [ci skip] --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com> Co-authored-by: あわわわとーにゅ <17376330+u1-liquid@users.noreply.github.com> Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
Diffstat (limited to 'packages/backend')
-rw-r--r--packages/backend/package.json89
-rw-r--r--packages/backend/src/@types/redis-lock.d.ts13
-rw-r--r--packages/backend/src/core/AppLockService.ts44
-rw-r--r--packages/backend/src/core/CoreModule.ts6
-rw-r--r--packages/backend/src/core/WebAuthnService.ts1
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts14
-rw-r--r--packages/backend/src/core/activitypub/models/ApNoteService.ts9
-rw-r--r--packages/backend/src/core/chart/charts/active-users.ts9
-rw-r--r--packages/backend/src/core/chart/charts/ap-request.ts9
-rw-r--r--packages/backend/src/core/chart/charts/drive.ts9
-rw-r--r--packages/backend/src/core/chart/charts/federation.ts9
-rw-r--r--packages/backend/src/core/chart/charts/instance.ts9
-rw-r--r--packages/backend/src/core/chart/charts/notes.ts9
-rw-r--r--packages/backend/src/core/chart/charts/per-user-drive.ts9
-rw-r--r--packages/backend/src/core/chart/charts/per-user-following.ts9
-rw-r--r--packages/backend/src/core/chart/charts/per-user-notes.ts9
-rw-r--r--packages/backend/src/core/chart/charts/per-user-pv.ts9
-rw-r--r--packages/backend/src/core/chart/charts/per-user-reactions.ts9
-rw-r--r--packages/backend/src/core/chart/charts/test-grouped.ts9
-rw-r--r--packages/backend/src/core/chart/charts/test-intersection.ts9
-rw-r--r--packages/backend/src/core/chart/charts/test-unique.ts9
-rw-r--r--packages/backend/src/core/chart/charts/test.ts9
-rw-r--r--packages/backend/src/core/chart/charts/users.ts9
-rw-r--r--packages/backend/src/misc/distributed-lock.ts49
-rw-r--r--packages/backend/test/jest.setup.ts1
-rw-r--r--packages/backend/test/unit/AnnouncementService.ts4
-rw-r--r--packages/backend/test/unit/CaptchaService.ts2
-rw-r--r--packages/backend/test/unit/DriveService.ts2
-rw-r--r--packages/backend/test/unit/FileInfoService.ts6
-rw-r--r--packages/backend/test/unit/RelayService.ts4
-rw-r--r--packages/backend/test/unit/RoleService.ts6
-rw-r--r--packages/backend/test/unit/S3Service.ts4
-rw-r--r--packages/backend/test/unit/SigninWithPasskeyApiService.ts4
-rw-r--r--packages/backend/test/unit/chart.ts19
-rw-r--r--packages/backend/test/unit/queue/processors/CheckModeratorsActivityProcessorService.ts2
35 files changed, 233 insertions, 190 deletions
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 4a6754c1d9..9feef3951d 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -40,17 +40,17 @@
},
"optionalDependencies": {
"@swc/core-android-arm64": "1.3.11",
- "@swc/core-darwin-arm64": "1.15.2",
- "@swc/core-darwin-x64": "1.15.2",
+ "@swc/core-darwin-arm64": "1.15.3",
+ "@swc/core-darwin-x64": "1.15.3",
"@swc/core-freebsd-x64": "1.3.11",
- "@swc/core-linux-arm-gnueabihf": "1.15.2",
- "@swc/core-linux-arm64-gnu": "1.15.2",
- "@swc/core-linux-arm64-musl": "1.15.2",
- "@swc/core-linux-x64-gnu": "1.15.2",
- "@swc/core-linux-x64-musl": "1.15.2",
- "@swc/core-win32-arm64-msvc": "1.15.2",
- "@swc/core-win32-ia32-msvc": "1.15.2",
- "@swc/core-win32-x64-msvc": "1.15.2",
+ "@swc/core-linux-arm-gnueabihf": "1.15.3",
+ "@swc/core-linux-arm64-gnu": "1.15.3",
+ "@swc/core-linux-arm64-musl": "1.15.3",
+ "@swc/core-linux-x64-gnu": "1.15.3",
+ "@swc/core-linux-x64-musl": "1.15.3",
+ "@swc/core-win32-arm64-msvc": "1.15.3",
+ "@swc/core-win32-ia32-msvc": "1.15.3",
+ "@swc/core-win32-x64-msvc": "1.15.3",
"@tensorflow/tfjs": "4.22.0",
"@tensorflow/tfjs-node": "4.22.0",
"bufferutil": "4.0.9",
@@ -70,17 +70,17 @@
"utf-8-validate": "6.0.5"
},
"dependencies": {
- "@aws-sdk/client-s3": "3.936.0",
- "@aws-sdk/lib-storage": "3.936.0",
+ "@aws-sdk/client-s3": "3.937.0",
+ "@aws-sdk/lib-storage": "3.937.0",
"@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.3",
"@fastify/cookie": "11.0.2",
- "@fastify/cors": "10.1.0",
+ "@fastify/cors": "11.1.0",
"@fastify/express": "4.0.2",
- "@fastify/http-proxy": "10.0.2",
+ "@fastify/http-proxy": "11.3.0",
"@fastify/multipart": "9.3.0",
"@fastify/static": "8.3.0",
- "@fastify/view": "10.0.2",
+ "@fastify/view": "11.1.1",
"@misskey-dev/sharp-read-bmp": "1.2.0",
"@misskey-dev/summaly": "5.2.5",
"@napi-rs/canvas": "0.1.82",
@@ -90,33 +90,33 @@
"@peertube/http-signature": "1.7.0",
"@sentry/node": "10.26.0",
"@sentry/profiling-node": "10.26.0",
- "@simplewebauthn/server": "12.0.0",
- "@sinonjs/fake-timers": "11.3.1",
- "@smithy/node-http-handler": "2.5.0",
+ "@simplewebauthn/server": "13.2.2",
+ "@sinonjs/fake-timers": "15.0.0",
+ "@smithy/node-http-handler": "4.4.5",
"@swc/cli": "0.7.9",
- "@swc/core": "1.15.2",
+ "@swc/core": "1.15.3",
"@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3",
"accepts": "1.3.8",
"ajv": "8.17.1",
"archiver": "7.0.1",
"async-mutex": "0.5.0",
- "bcryptjs": "2.4.3",
+ "bcryptjs": "3.0.3",
"blurhash": "2.0.5",
- "body-parser": "1.20.3",
- "bullmq": "5.63.2",
+ "body-parser": "2.2.0",
+ "bullmq": "5.64.1",
"cacheable-lookup": "7.0.0",
- "cbor": "9.0.2",
+ "cbor": "10.0.11",
"chalk": "5.6.2",
"chalk-template": "1.1.2",
"chokidar": "4.0.3",
- "color-convert": "2.0.1",
- "content-disposition": "0.5.4",
- "date-fns": "2.30.0",
+ "color-convert": "3.1.3",
+ "content-disposition": "1.0.1",
+ "date-fns": "4.1.0",
"deep-email-validator": "0.1.21",
"fastify": "5.6.2",
"fastify-raw-body": "5.0.0",
- "feed": "4.2.2",
+ "feed": "5.1.0",
"file-type": "21.1.1",
"fluent-ffmpeg": "2.1.3",
"form-data": "4.0.5",
@@ -126,16 +126,16 @@
"ioredis": "5.8.2",
"ip-cidr": "4.0.2",
"ipaddr.js": "2.2.0",
- "is-svg": "5.1.0",
+ "is-svg": "6.1.0",
"js-yaml": "4.1.1",
"json5": "2.2.3",
- "jsonld": "8.3.3",
+ "jsonld": "9.0.0",
"jsrsasign": "11.1.0",
"juice": "11.0.3",
"meilisearch": "0.54.0",
"mfm-js": "0.25.0",
"microformats-parser": "2.0.4",
- "mime-types": "2.1.35",
+ "mime-types": "3.0.2",
"misskey-js": "workspace:*",
"misskey-reversi": "workspace:*",
"ms": "3.0.0-canary.202508261828",
@@ -151,7 +151,7 @@
"os-utils": "0.0.14",
"otpauth": "9.4.1",
"pg": "8.16.3",
- "pkce-challenge": "4.1.0",
+ "pkce-challenge": "5.0.0",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"pug": "3.0.3",
@@ -160,13 +160,12 @@
"ratelimiter": "3.4.1",
"re2": "1.22.3",
"redis-info": "3.1.0",
- "redis-lock": "0.1.4",
"reflect-metadata": "0.2.2",
"rename": "1.0.4",
"rss-parser": "3.13.0",
"rxjs": "7.8.2",
"sanitize-html": "2.17.0",
- "secure-json-parse": "3.0.2",
+ "secure-json-parse": "4.1.0",
"semver": "7.7.3",
"sharp": "0.33.5",
"slacc": "0.0.10",
@@ -179,7 +178,7 @@
"tsconfig-paths": "4.2.0",
"typeorm": "0.3.27",
"typescript": "5.9.3",
- "ulid": "2.4.0",
+ "ulid": "3.0.1",
"vary": "1.1.2",
"web-push": "3.6.7",
"ws": "8.18.3",
@@ -187,13 +186,12 @@
},
"devDependencies": {
"@jest/globals": "29.7.0",
- "@nestjs/platform-express": "10.4.20",
+ "@nestjs/platform-express": "11.1.9",
"@sentry/vue": "10.26.0",
"@simplewebauthn/types": "12.0.0",
"@swc/jest": "0.2.39",
"@types/accepts": "1.3.7",
- "@types/archiver": "6.0.4",
- "@types/bcryptjs": "2.4.6",
+ "@types/archiver": "7.0.0",
"@types/body-parser": "1.19.6",
"@types/color-convert": "2.0.4",
"@types/content-disposition": "0.5.9",
@@ -203,10 +201,10 @@
"@types/js-yaml": "4.0.9",
"@types/jsonld": "1.5.15",
"@types/jsrsasign": "10.5.15",
- "@types/mime-types": "2.1.4",
- "@types/ms": "0.7.34",
+ "@types/mime-types": "3.0.1",
+ "@types/ms": "2.1.0",
"@types/node": "24.10.1",
- "@types/nodemailer": "6.4.21",
+ "@types/nodemailer": "7.0.4",
"@types/oauth": "0.9.6",
"@types/oauth2orize": "1.11.5",
"@types/oauth2orize-pkce": "0.1.2",
@@ -219,7 +217,7 @@
"@types/sanitize-html": "2.16.0",
"@types/semver": "7.7.1",
"@types/simple-oauth2": "5.0.7",
- "@types/sinonjs__fake-timers": "8.1.5",
+ "@types/sinonjs__fake-timers": "15.0.1",
"@types/supertest": "6.0.3",
"@types/tinycolor2": "1.4.6",
"@types/tmp": "0.2.6",
@@ -229,14 +227,15 @@
"@typescript-eslint/eslint-plugin": "8.47.0",
"@typescript-eslint/parser": "8.47.0",
"aws-sdk-client-mock": "4.1.0",
- "cross-env": "7.0.3",
+ "cross-env": "10.1.0",
"eslint-plugin-import": "2.32.0",
- "execa": "8.0.1",
- "fkill": "9.0.0",
+ "execa": "9.6.0",
+ "fkill": "10.0.1",
"jest": "29.7.0",
"jest-mock": "29.7.0",
+ "jest-util": "29.7.0",
"nodemon": "3.1.11",
- "pid-port": "1.0.2",
+ "pid-port": "2.0.0",
"simple-oauth2": "5.1.0",
"supertest": "7.1.4"
}
diff --git a/packages/backend/src/@types/redis-lock.d.ts b/packages/backend/src/@types/redis-lock.d.ts
deleted file mode 100644
index b037cde5ee..0000000000
--- a/packages/backend/src/@types/redis-lock.d.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-declare module 'redis-lock' {
- import type Redis from 'ioredis';
-
- type Lock = (lockName: string, timeout?: number, taskToPerform?: () => Promise<void>) => void;
- function redisLock(client: Redis.Redis, retryDelay: number): Lock;
-
- export = redisLock;
-}
diff --git a/packages/backend/src/core/AppLockService.ts b/packages/backend/src/core/AppLockService.ts
deleted file mode 100644
index bd2749cb87..0000000000
--- a/packages/backend/src/core/AppLockService.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { promisify } from 'node:util';
-import { Inject, Injectable } from '@nestjs/common';
-import redisLock from 'redis-lock';
-import * as Redis from 'ioredis';
-import { DI } from '@/di-symbols.js';
-import { bindThis } from '@/decorators.js';
-
-/**
- * Retry delay (ms) for lock acquisition
- */
-const retryDelay = 100;
-
-@Injectable()
-export class AppLockService {
- private lock: (key: string, timeout?: number, _?: (() => Promise<void>) | undefined) => Promise<() => void>;
-
- constructor(
- @Inject(DI.redis)
- private redisClient: Redis.Redis,
- ) {
- this.lock = promisify(redisLock(this.redisClient, retryDelay));
- }
-
- /**
- * Get AP Object lock
- * @param uri AP object ID
- * @param timeout Lock timeout (ms), The timeout releases previous lock.
- * @returns Unlock function
- */
- @bindThis
- public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> {
- return this.lock(`ap-object:${uri}`, timeout);
- }
-
- @bindThis
- public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> {
- return this.lock(`chart-insert:${lockKey}`, timeout);
- }
-}
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index 8c8d22c77d..87575ca59a 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -21,7 +21,6 @@ import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
import { AnnouncementService } from './AnnouncementService.js';
import { AntennaService } from './AntennaService.js';
-import { AppLockService } from './AppLockService.js';
import { AchievementService } from './AchievementService.js';
import { AvatarDecorationService } from './AvatarDecorationService.js';
import { CaptchaService } from './CaptchaService.js';
@@ -166,7 +165,6 @@ const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useEx
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
const $AnnouncementService: Provider = { provide: 'AnnouncementService', useExisting: AnnouncementService };
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
-const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
const $AchievementService: Provider = { provide: 'AchievementService', useExisting: AchievementService };
const $AvatarDecorationService: Provider = { provide: 'AvatarDecorationService', useExisting: AvatarDecorationService };
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
@@ -320,7 +318,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AiService,
AnnouncementService,
AntennaService,
- AppLockService,
AchievementService,
AvatarDecorationService,
CaptchaService,
@@ -470,7 +467,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AiService,
$AnnouncementService,
$AntennaService,
- $AppLockService,
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
@@ -621,7 +617,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
AiService,
AnnouncementService,
AntennaService,
- AppLockService,
AchievementService,
AvatarDecorationService,
CaptchaService,
@@ -770,7 +765,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$AiService,
$AnnouncementService,
$AntennaService,
- $AppLockService,
$AchievementService,
$AvatarDecorationService,
$CaptchaService,
diff --git a/packages/backend/src/core/WebAuthnService.ts b/packages/backend/src/core/WebAuthnService.ts
index 372e1e2ab7..31c8d67c60 100644
--- a/packages/backend/src/core/WebAuthnService.ts
+++ b/packages/backend/src/core/WebAuthnService.ts
@@ -66,7 +66,6 @@ export class WebAuthnService {
userID: isoUint8Array.fromUTF8String(userId),
userName: userName,
userDisplayName: userDisplayName,
- attestationType: 'indirect',
excludeCredentials: keys.map(key => (<{ id: string; transports?: AuthenticatorTransportFuture[]; }>{
id: key.id,
transports: key.transports ?? undefined,
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index e88f60b806..81637580e3 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -5,6 +5,7 @@
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';
@@ -14,8 +15,8 @@ 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 { AppLockService } from '@/core/AppLockService.js';
import type Logger from '@/logger.js';
import { IdService } from '@/core/IdService.js';
import { StatusError } from '@/misc/status-error.js';
@@ -48,8 +49,8 @@ export class ApInboxService {
@Inject(DI.config)
private config: Config,
- @Inject(DI.meta)
- private meta: MiMeta,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -76,7 +77,6 @@ export class ApInboxService {
private userBlockingService: UserBlockingService,
private noteCreateService: NoteCreateService,
private noteDeleteService: NoteDeleteService,
- private appLockService: AppLockService,
private apResolverService: ApResolverService,
private apDbResolverService: ApDbResolverService,
private apLoggerService: ApLoggerService,
@@ -311,7 +311,7 @@ export class ApInboxService {
// アナウンス先が許可されているかチェック
if (!this.utilityService.isFederationAllowedUri(uri)) return;
- const unlock = await this.appLockService.getApLock(uri);
+ const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
// 既に同じURIを持つものが登録されていないかチェック
@@ -438,7 +438,7 @@ export class ApInboxService {
}
}
- const unlock = await this.appLockService.getApLock(uri);
+ const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
const exist = await this.apNoteService.fetchNote(note);
@@ -522,7 +522,7 @@ export class ApInboxService {
private async deleteNote(actor: MiRemoteUser, uri: string): Promise<string> {
this.logger.info(`Deleting the Note: ${uri}`);
- const unlock = await this.appLockService.getApLock(uri);
+ const unlock = await acquireApObjectLock(this.redisClient, 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 8abacd293f..214d32f67f 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -5,14 +5,15 @@
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 { AppLockService } from '@/core/AppLockService.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import { NoteCreateService } from '@/core/NoteCreateService.js';
import type Logger from '@/logger.js';
@@ -48,6 +49,9 @@ export class ApNoteService {
@Inject(DI.meta)
private meta: MiMeta,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.pollsRepository)
private pollsRepository: PollsRepository,
@@ -67,7 +71,6 @@ export class ApNoteService {
private apMentionService: ApMentionService,
private apImageService: ApImageService,
private apQuestionService: ApQuestionService,
- private appLockService: AppLockService,
private pollService: PollService,
private noteCreateService: NoteCreateService,
private apDbResolverService: ApDbResolverService,
@@ -354,7 +357,7 @@ export class ApNoteService {
throw new StatusError('blocked host', 451);
}
- const unlock = await this.appLockService.getApLock(uri);
+ const unlock = await acquireApObjectLock(this.redisClient, uri);
try {
//#region このサーバーに既に登録されていたらそれを返す
diff --git a/packages/backend/src/core/chart/charts/active-users.ts b/packages/backend/src/core/chart/charts/active-users.ts
index 05905f3782..7b9840af87 100644
--- a/packages/backend/src/core/chart/charts/active-users.ts
+++ b/packages/backend/src/core/chart/charts/active-users.ts
@@ -5,11 +5,12 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import { AppLockService } from '@/core/AppLockService.js';
+import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/active-users.js';
@@ -28,11 +29,13 @@ export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-d
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
private chartLoggerService: ChartLoggerService,
private idService: IdService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/ap-request.ts b/packages/backend/src/core/chart/charts/ap-request.ts
index 04e771a95b..ed790de7b5 100644
--- a/packages/backend/src/core/chart/charts/ap-request.ts
+++ b/packages/backend/src/core/chart/charts/ap-request.ts
@@ -5,9 +5,10 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import { AppLockService } from '@/core/AppLockService.js';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/ap-request.js';
@@ -22,10 +23,12 @@ export default class ApRequestChart extends Chart<typeof schema> { // eslint-dis
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/drive.ts b/packages/backend/src/core/chart/charts/drive.ts
index 613e074a9f..782873809a 100644
--- a/packages/backend/src/core/chart/charts/drive.ts
+++ b/packages/backend/src/core/chart/charts/drive.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { MiDriveFile } from '@/models/DriveFile.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/drive.js';
@@ -23,10 +24,12 @@ export default class DriveChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts
index c9b43cc66d..b7a7f640b8 100644
--- a/packages/backend/src/core/chart/charts/federation.ts
+++ b/packages/backend/src/core/chart/charts/federation.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { FollowingsRepository, InstancesRepository, MiMeta } from '@/models/_.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/federation.js';
@@ -26,16 +27,18 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di
@Inject(DI.meta)
private meta: MiMeta,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
@Inject(DI.instancesRepository)
private instancesRepository: InstancesRepository,
- private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/instance.ts b/packages/backend/src/core/chart/charts/instance.ts
index 97f3bc6f2b..b1657e0a0b 100644
--- a/packages/backend/src/core/chart/charts/instance.ts
+++ b/packages/backend/src/core/chart/charts/instance.ts
@@ -5,13 +5,14 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { DriveFilesRepository, FollowingsRepository, UsersRepository, NotesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
import type { MiNote } from '@/models/Note.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UtilityService } from '@/core/UtilityService.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/instance.js';
@@ -26,6 +27,9 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
@Inject(DI.db)
private db: DataSource,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@@ -39,10 +43,9 @@ export default class InstanceChart extends Chart<typeof schema> { // eslint-disa
private followingsRepository: FollowingsRepository,
private utilityService: UtilityService,
- private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/notes.ts b/packages/backend/src/core/chart/charts/notes.ts
index f763b5fffa..aa64e2329a 100644
--- a/packages/backend/src/core/chart/charts/notes.ts
+++ b/packages/backend/src/core/chart/charts/notes.ts
@@ -5,11 +5,12 @@
import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { NotesRepository } from '@/models/_.js';
import type { MiNote } from '@/models/Note.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/notes.js';
@@ -24,13 +25,15 @@ export default class NotesChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db)
private db: DataSource,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
- private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/per-user-drive.ts b/packages/backend/src/core/chart/charts/per-user-drive.ts
index 404964d8b7..f7e92aecea 100644
--- a/packages/backend/src/core/chart/charts/per-user-drive.ts
+++ b/packages/backend/src/core/chart/charts/per-user-drive.ts
@@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { DriveFilesRepository } from '@/models/_.js';
import type { MiDriveFile } from '@/models/DriveFile.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-drive.js';
@@ -25,14 +26,16 @@ export default class PerUserDriveChart extends Chart<typeof schema> { // eslint-
@Inject(DI.db)
private db: DataSource,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,
- private appLockService: AppLockService,
private driveFileEntityService: DriveFileEntityService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/per-user-following.ts b/packages/backend/src/core/chart/charts/per-user-following.ts
index 588ac638de..ea431a5131 100644
--- a/packages/backend/src/core/chart/charts/per-user-following.ts
+++ b/packages/backend/src/core/chart/charts/per-user-following.ts
@@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { FollowingsRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-following.js';
@@ -25,14 +26,16 @@ export default class PerUserFollowingChart extends Chart<typeof schema> { // esl
@Inject(DI.db)
private db: DataSource,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.followingsRepository)
private followingsRepository: FollowingsRepository,
- private appLockService: AppLockService,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/per-user-notes.ts b/packages/backend/src/core/chart/charts/per-user-notes.ts
index e4900772bb..824d60042d 100644
--- a/packages/backend/src/core/chart/charts/per-user-notes.ts
+++ b/packages/backend/src/core/chart/charts/per-user-notes.ts
@@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import type { NotesRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-notes.js';
@@ -25,13 +26,15 @@ export default class PerUserNotesChart extends Chart<typeof schema> { // eslint-
@Inject(DI.db)
private db: DataSource,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
- private appLockService: AppLockService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/per-user-pv.ts b/packages/backend/src/core/chart/charts/per-user-pv.ts
index 31708fefa8..b3e1b2cea1 100644
--- a/packages/backend/src/core/chart/charts/per-user-pv.ts
+++ b/packages/backend/src/core/chart/charts/per-user-pv.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-pv.js';
@@ -23,10 +24,12 @@ export default class PerUserPvChart extends Chart<typeof schema> { // eslint-dis
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/per-user-reactions.ts b/packages/backend/src/core/chart/charts/per-user-reactions.ts
index c29c4d2870..7bc1d9e7fa 100644
--- a/packages/backend/src/core/chart/charts/per-user-reactions.ts
+++ b/packages/backend/src/core/chart/charts/per-user-reactions.ts
@@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
import type { MiNote } from '@/models/Note.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/per-user-reactions.js';
@@ -25,11 +26,13 @@ export default class PerUserReactionsChart extends Chart<typeof schema> { // esl
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/test-grouped.ts b/packages/backend/src/core/chart/charts/test-grouped.ts
index 7a2844f4ed..8dd1a5d996 100644
--- a/packages/backend/src/core/chart/charts/test-grouped.ts
+++ b/packages/backend/src/core/chart/charts/test-grouped.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import { AppLockService } from '@/core/AppLockService.js';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test-grouped.js';
import type { KVs } from '../core.js';
@@ -24,10 +25,12 @@ export default class TestGroupedChart extends Chart<typeof schema> { // eslint-d
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
logger: Logger,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema, true);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema, true);
}
protected async tickMajor(group: string): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/test-intersection.ts b/packages/backend/src/core/chart/charts/test-intersection.ts
index b8d0556c9f..23b8649cce 100644
--- a/packages/backend/src/core/chart/charts/test-intersection.ts
+++ b/packages/backend/src/core/chart/charts/test-intersection.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import { AppLockService } from '@/core/AppLockService.js';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test-intersection.js';
import type { KVs } from '../core.js';
@@ -22,10 +23,12 @@ export default class TestIntersectionChart extends Chart<typeof schema> { // esl
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
logger: Logger,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/test-unique.ts b/packages/backend/src/core/chart/charts/test-unique.ts
index f94e008059..b84dd419ba 100644
--- a/packages/backend/src/core/chart/charts/test-unique.ts
+++ b/packages/backend/src/core/chart/charts/test-unique.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import { AppLockService } from '@/core/AppLockService.js';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test-unique.js';
import type { KVs } from '../core.js';
@@ -22,10 +23,12 @@ export default class TestUniqueChart extends Chart<typeof schema> { // eslint-di
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
logger: Logger,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/test.ts b/packages/backend/src/core/chart/charts/test.ts
index a90dc8f99b..0e95ce9239 100644
--- a/packages/backend/src/core/chart/charts/test.ts
+++ b/packages/backend/src/core/chart/charts/test.ts
@@ -5,10 +5,11 @@
import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm';
-import { AppLockService } from '@/core/AppLockService.js';
+import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { name, schema } from './entities/test.js';
import type { KVs } from '../core.js';
@@ -24,10 +25,12 @@ export default class TestChart extends Chart<typeof schema> { // eslint-disable-
@Inject(DI.db)
private db: DataSource,
- private appLockService: AppLockService,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
logger: Logger,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/core/chart/charts/users.ts b/packages/backend/src/core/chart/charts/users.ts
index d148fc629b..4471c1df23 100644
--- a/packages/backend/src/core/chart/charts/users.ts
+++ b/packages/backend/src/core/chart/charts/users.ts
@@ -5,12 +5,13 @@
import { Injectable, Inject } from '@nestjs/common';
import { Not, IsNull, DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import type { MiUser } from '@/models/User.js';
-import { AppLockService } from '@/core/AppLockService.js';
import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import type { UsersRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
+import { acquireChartInsertLock } from '@/misc/distributed-lock.js';
import Chart from '../core.js';
import { ChartLoggerService } from '../ChartLoggerService.js';
import { name, schema } from './entities/users.js';
@@ -25,14 +26,16 @@ export default class UsersChart extends Chart<typeof schema> { // eslint-disable
@Inject(DI.db)
private db: DataSource,
+ @Inject(DI.redis)
+ private redisClient: Redis.Redis,
+
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
- private appLockService: AppLockService,
private userEntityService: UserEntityService,
private chartLoggerService: ChartLoggerService,
) {
- super(db, (k) => appLockService.getChartInsertLock(k), chartLoggerService.logger, name, schema);
+ super(db, (k) => acquireChartInsertLock(redisClient, k), chartLoggerService.logger, name, schema);
}
protected async tickMajor(): Promise<Partial<KVs<typeof schema>>> {
diff --git a/packages/backend/src/misc/distributed-lock.ts b/packages/backend/src/misc/distributed-lock.ts
new file mode 100644
index 0000000000..93bd741f62
--- /dev/null
+++ b/packages/backend/src/misc/distributed-lock.ts
@@ -0,0 +1,49 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Redis from 'ioredis';
+
+export async function acquireDistributedLock(
+ redis: Redis.Redis,
+ name: string,
+ timeout: number,
+ maxRetries: number,
+ retryInterval: number,
+): Promise<() => Promise<void>> {
+ const lockKey = `lock:${name}`;
+ const identifier = Math.random().toString(36).slice(2);
+
+ let retries = 0;
+ while (retries < maxRetries) {
+ const result = await redis.set(lockKey, identifier, 'PX', timeout, 'NX');
+ if (result === 'OK') {
+ return async () => {
+ const currentIdentifier = await redis.get(lockKey);
+ if (currentIdentifier === identifier) {
+ await redis.del(lockKey);
+ }
+ };
+ }
+
+ await new Promise(resolve => setTimeout(resolve, retryInterval));
+ retries++;
+ }
+
+ throw new Error(`Failed to acquire lock ${name}`);
+}
+
+export function acquireApObjectLock(
+ redis: Redis.Redis,
+ uri: string,
+): Promise<() => Promise<void>> {
+ return acquireDistributedLock(redis, `ap-object:${uri}`, 30 * 1000, 50, 100);
+}
+
+export function acquireChartInsertLock(
+ redis: Redis.Redis,
+ name: string,
+): Promise<() => Promise<void>> {
+ return acquireDistributedLock(redis, `chart-insert:${name}`, 30 * 1000, 50, 500);
+}
diff --git a/packages/backend/test/jest.setup.ts b/packages/backend/test/jest.setup.ts
index 7c6dd6a55f..9185f58acb 100644
--- a/packages/backend/test/jest.setup.ts
+++ b/packages/backend/test/jest.setup.ts
@@ -9,3 +9,4 @@ beforeAll(async () => {
await initTestDb(false);
await sendEnvResetRequest();
});
+
diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts
index 0b24f109f8..b3f7f426fe 100644
--- a/packages/backend/test/unit/AnnouncementService.ts
+++ b/packages/backend/test/unit/AnnouncementService.ts
@@ -26,7 +26,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
import { secureRndstr } from '@/misc/secure-rndstr.js';
import type { TestingModule } from '@nestjs/testing';
-import type { MockFunctionMetadata } from 'jest-mock';
+import type { MockMetadata } from 'jest-mock';
const moduleMocker = new ModuleMocker(global);
@@ -84,7 +84,7 @@ describe('AnnouncementService', () => {
log: jest.fn(),
};
} else if (typeof token === 'function') {
- const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
+ const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}
diff --git a/packages/backend/test/unit/CaptchaService.ts b/packages/backend/test/unit/CaptchaService.ts
index 51b70b05a1..24bb81118e 100644
--- a/packages/backend/test/unit/CaptchaService.ts
+++ b/packages/backend/test/unit/CaptchaService.ts
@@ -446,7 +446,7 @@ describe('CaptchaService', () => {
if (!res.success) {
expect(res.error.code).toBe(code);
}
- expect(metaService.update).not.toBeCalled();
+ expect(metaService.update).not.toHaveBeenCalled();
}
describe('invalidParameters', () => {
diff --git a/packages/backend/test/unit/DriveService.ts b/packages/backend/test/unit/DriveService.ts
index 964c65ccaa..48b108fbba 100644
--- a/packages/backend/test/unit/DriveService.ts
+++ b/packages/backend/test/unit/DriveService.ts
@@ -53,7 +53,7 @@ describe('DriveService', () => {
s3Mock.on(DeleteObjectCommand)
.rejects(new InvalidObjectState({ $metadata: {}, message: '' }));
- await expect(driveService.deleteObjectStorageFile('unexpected')).rejects.toThrowError(Error);
+ await expect(driveService.deleteObjectStorageFile('unexpected')).rejects.toThrow(Error);
});
test('delete a file with no valid key', async () => {
diff --git a/packages/backend/test/unit/FileInfoService.ts b/packages/backend/test/unit/FileInfoService.ts
index 29bd03a201..28a2a971f4 100644
--- a/packages/backend/test/unit/FileInfoService.ts
+++ b/packages/backend/test/unit/FileInfoService.ts
@@ -17,7 +17,7 @@ import { FileInfo, FileInfoService } from '@/core/FileInfoService.js';
import { AiService } from '@/core/AiService.js';
import { LoggerService } from '@/core/LoggerService.js';
import type { TestingModule } from '@nestjs/testing';
-import type { MockFunctionMetadata } from 'jest-mock';
+import type { MockMetadata } from 'jest-mock';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@@ -34,7 +34,7 @@ describe('FileInfoService', () => {
delete fi.sensitive;
delete fi.blurhash;
delete fi.porn;
-
+
return fi;
}
@@ -54,7 +54,7 @@ describe('FileInfoService', () => {
// return { };
//}
if (typeof token === 'function') {
- const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
+ const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}
diff --git a/packages/backend/test/unit/RelayService.ts b/packages/backend/test/unit/RelayService.ts
index 074430dd31..bee580d0c7 100644
--- a/packages/backend/test/unit/RelayService.ts
+++ b/packages/backend/test/unit/RelayService.ts
@@ -9,7 +9,7 @@ import { jest } from '@jest/globals';
import { Test } from '@nestjs/testing';
import { ModuleMocker } from 'jest-mock';
import type { TestingModule } from '@nestjs/testing';
-import type { MockFunctionMetadata } from 'jest-mock';
+import type { MockMetadata } from 'jest-mock';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { IdService } from '@/core/IdService.js';
@@ -45,7 +45,7 @@ describe('RelayService', () => {
return { deliver: jest.fn() };
}
if (typeof token === 'function') {
- const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
+ const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}
diff --git a/packages/backend/test/unit/RoleService.ts b/packages/backend/test/unit/RoleService.ts
index 71090c8be6..9b17b1fbb9 100644
--- a/packages/backend/test/unit/RoleService.ts
+++ b/packages/backend/test/unit/RoleService.ts
@@ -11,7 +11,7 @@ import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import * as lolex from '@sinonjs/fake-timers';
import type { TestingModule } from '@nestjs/testing';
-import type { MockFunctionMetadata } from 'jest-mock';
+import type { MockMetadata } from 'jest-mock';
import { GlobalModule } from '@/GlobalModule.js';
import { RoleService } from '@/core/RoleService.js';
import {
@@ -104,6 +104,8 @@ describe('RoleService', () => {
beforeEach(async () => {
clock = lolex.install({
+ // https://github.com/sinonjs/sinon/issues/2620
+ toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(),
shouldClearNativeTimers: true,
});
@@ -135,7 +137,7 @@ describe('RoleService', () => {
return { fetch: jest.fn() };
}
if (typeof token === 'function') {
- const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
+ const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}
diff --git a/packages/backend/test/unit/S3Service.ts b/packages/backend/test/unit/S3Service.ts
index 151f3b826a..6e7e5a8b59 100644
--- a/packages/backend/test/unit/S3Service.ts
+++ b/packages/backend/test/unit/S3Service.ts
@@ -72,7 +72,7 @@ describe('S3Service', () => {
Bucket: 'fake',
Key: 'fake',
Body: 'x',
- })).rejects.toThrowError(Error);
+ })).rejects.toThrow(Error);
});
test('upload a large file error', async () => {
@@ -82,7 +82,7 @@ describe('S3Service', () => {
Bucket: 'fake',
Key: 'fake',
Body: 'x'.repeat(8 * 1024 * 1024 + 1), // デフォルトpartSizeにしている 8 * 1024 * 1024 を越えるサイズ
- })).rejects.toThrowError(Error);
+ })).rejects.toThrow(Error);
});
});
});
diff --git a/packages/backend/test/unit/SigninWithPasskeyApiService.ts b/packages/backend/test/unit/SigninWithPasskeyApiService.ts
index 0687ed8437..8ef46024ac 100644
--- a/packages/backend/test/unit/SigninWithPasskeyApiService.ts
+++ b/packages/backend/test/unit/SigninWithPasskeyApiService.ts
@@ -9,7 +9,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { FastifyReply, FastifyRequest } from 'fastify';
import { AuthenticationResponseJSON } from '@simplewebauthn/types';
import { HttpHeader } from 'fastify/types/utils.js';
-import { MockFunctionMetadata, ModuleMocker } from 'jest-mock';
+import { MockMetadata, ModuleMocker } from 'jest-mock';
import { MiUser } from '@/models/User.js';
import { MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/_.js';
import { IdService } from '@/core/IdService.js';
@@ -95,7 +95,7 @@ describe('SigninWithPasskeyApiService', () => {
],
}).useMocker((token) => {
if (typeof token === 'function') {
- const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
+ const mockMetadata = moduleMocker.getMetadata(token) as MockMetadata<any, any>;
const Mock = moduleMocker.generateFromMetadata(mockMetadata);
return new Mock();
}
diff --git a/packages/backend/test/unit/chart.ts b/packages/backend/test/unit/chart.ts
index 9dedd3a79d..364a2c2fbd 100644
--- a/packages/backend/test/unit/chart.ts
+++ b/packages/backend/test/unit/chart.ts
@@ -9,6 +9,7 @@ import * as assert from 'assert';
import { jest } from '@jest/globals';
import * as lolex from '@sinonjs/fake-timers';
import { DataSource } from 'typeorm';
+import * as Redis from 'ioredis';
import TestChart from '@/core/chart/charts/test.js';
import TestGroupedChart from '@/core/chart/charts/test-grouped.js';
import TestUniqueChart from '@/core/chart/charts/test-unique.js';
@@ -18,16 +19,16 @@ import { entity as TestGroupedChartEntity } from '@/core/chart/charts/entities/t
import { entity as TestUniqueChartEntity } from '@/core/chart/charts/entities/test-unique.js';
import { entity as TestIntersectionChartEntity } from '@/core/chart/charts/entities/test-intersection.js';
import { loadConfig } from '@/config.js';
-import type { AppLockService } from '@/core/AppLockService.js';
import Logger from '@/logger.js';
describe('Chart', () => {
const config = loadConfig();
- const appLockService = {
- getChartInsertLock: () => () => Promise.resolve(() => {}),
- } as unknown as jest.Mocked<AppLockService>;
let db: DataSource | undefined;
+ let redisClient = {
+ set: () => Promise.resolve('OK'),
+ get: () => Promise.resolve(null),
+ } as unknown as jest.Mocked<Redis.Redis>;
let testChart: TestChart;
let testGroupedChart: TestGroupedChart;
@@ -64,12 +65,14 @@ describe('Chart', () => {
await db.initialize();
const logger = new Logger('chart'); // TODO: モックにする
- testChart = new TestChart(db, appLockService, logger);
- testGroupedChart = new TestGroupedChart(db, appLockService, logger);
- testUniqueChart = new TestUniqueChart(db, appLockService, logger);
- testIntersectionChart = new TestIntersectionChart(db, appLockService, logger);
+ testChart = new TestChart(db, redisClient, logger);
+ testGroupedChart = new TestGroupedChart(db, redisClient, logger);
+ testUniqueChart = new TestUniqueChart(db, redisClient, logger);
+ testIntersectionChart = new TestIntersectionChart(db, redisClient, logger);
clock = lolex.install({
+ // https://github.com/sinonjs/sinon/issues/2620
+ toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(Date.UTC(2000, 0, 1, 0, 0, 0)),
shouldClearNativeTimers: true,
});
diff --git a/packages/backend/test/unit/queue/processors/CheckModeratorsActivityProcessorService.ts b/packages/backend/test/unit/queue/processors/CheckModeratorsActivityProcessorService.ts
index 211846eef2..01a36c9fef 100644
--- a/packages/backend/test/unit/queue/processors/CheckModeratorsActivityProcessorService.ts
+++ b/packages/backend/test/unit/queue/processors/CheckModeratorsActivityProcessorService.ts
@@ -141,6 +141,8 @@ describe('CheckModeratorsActivityProcessorService', () => {
beforeEach(async () => {
clock = lolex.install({
+ // https://github.com/sinonjs/sinon/issues/2620
+ toFake: Object.keys(lolex.timers).filter((key) => !['nextTick', 'queueMicrotask'].includes(key)) as lolex.FakeMethod[],
now: new Date(baseDate),
shouldClearNativeTimers: true,
});