From f6383afe46e74f5f8b3fc074c09c2abe51026390 Mon Sep 17 00:00:00 2001 From: KOBA789 Date: Fri, 6 Oct 2023 21:49:35 +0900 Subject: [PATCH 01/12] Extend slow query threshold to reduce logs (MisskeyIO#171) --- packages/backend/src/postgres.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 6a2fae69a3..d9224cdbc9 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -240,7 +240,7 @@ export function createPostgresDataSource(config: Config) { } : false, logging: log, logger: log ? new MyCustomLogger() : undefined, - maxQueryExecutionTime: 300, + maxQueryExecutionTime: 10000, // 10s entities: entities, migrations: ['../../migration/*.js'], }); From b784983a8eede0fbb39dfa76512ac668e67950c4 Mon Sep 17 00:00:00 2001 From: nenohi Date: Fri, 6 Oct 2023 21:50:10 +0900 Subject: [PATCH 02/12] =?UTF-8?q?=E3=83=96=E3=83=A9=E3=83=BC=E3=82=92?= =?UTF-8?q?=E3=83=87=E3=83=95=E3=82=A9=E3=83=AB=E3=83=88=E3=81=A7false=20(?= =?UTF-8?q?MisskeyIO#170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/store.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index e7be2d56a5..9aba8017ff 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -217,11 +217,11 @@ export const defaultStore = markRaw(new Storage('base', { }, useBlurEffectForModal: { where: 'device', - default: !/mobile|iphone|android/.test(navigator.userAgent.toLowerCase()), // 循環参照するのでdevice-kind.tsは参照できない + default: false, }, useBlurEffect: { where: 'device', - default: !/mobile|iphone|android/.test(navigator.userAgent.toLowerCase()), // 循環参照するのでdevice-kind.tsは参照できない + default: false, }, showFixedPostForm: { where: 'device', From 37176668079459d007ada3f9f6d8cbfda39b78a6 Mon Sep 17 00:00:00 2001 From: riku6460 <17585784+riku6460@users.noreply.github.com> Date: Fri, 6 Oct 2023 21:57:37 +0900 Subject: [PATCH 03/12] =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=81=8B=E3=82=89=E5=88=87=E6=96=AD=E3=81=95=E3=82=8C=E3=81=9F?= =?UTF-8?q?=E5=BE=8C=20stream=20indicator=20=E3=82=92=E3=81=99=E3=81=90?= =?UTF-8?q?=E3=81=AB=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(MisskeyIO#172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit すぐに再接続されたら表示しないように --- packages/frontend/src/ui/_common_/stream-indicator.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue index abeb66c326..d252496829 100644 --- a/packages/frontend/src/ui/_common_/stream-indicator.vue +++ b/packages/frontend/src/ui/_common_/stream-indicator.vue @@ -24,12 +24,17 @@ import { defaultStore } from '@/store'; const zIndex = os.claimZIndex('high'); let hasDisconnected = $ref(false); +let timeoutId = $ref(); function onDisconnected() { - hasDisconnected = true; + window.clearTimeout(timeoutId); + timeoutId = window.setTimeout(() => { + hasDisconnected = true; + }, 1000 * 10); } function resetDisconnected() { + window.clearTimeout(timeoutId); hasDisconnected = false; } @@ -37,9 +42,12 @@ function reload() { location.reload(); } +useStream().on('_connected_', resetDisconnected); useStream().on('_disconnected_', onDisconnected); onUnmounted(() => { + window.clearTimeout(timeoutId); + useStream().off('_connected_', resetDisconnected); useStream().off('_disconnected_', onDisconnected); }); From 533bae1e6c43a9454e1e7f199c847290e4a31021 Mon Sep 17 00:00:00 2001 From: KOBA789 Date: Fri, 6 Oct 2023 22:41:59 +0900 Subject: [PATCH 04/12] Hot-fix: Write-back value in memory when it's available in redis (MisskeyIO#174) * Write-back value in memory when it's available in redis * Fix recursive cache * Fix bug in case that invalid data in redis cache --- packages/backend/src/misc/cache.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 046d4d6c2f..5b841fbaa1 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -149,7 +149,10 @@ export class RedisSingleCache { const cached = await this.redisClient.get(`singlecache:${this.name}`); if (cached == null) return undefined; - return this.fromRedisConverter(cached); + const parsed = this.fromRedisConverter(cached); + if (parsed == null) return undefined; + this.memoryCache.set(parsed); + return parsed; } @bindThis From a296a354605c2d1a5b6e70c930372818e7908b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:42:27 +0900 Subject: [PATCH 05/12] Bump up version to 13.14.2-io.8 (MisskeyIO#173) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d1de709ed..3815cbef91 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.14.2-io.7", + "version": "13.14.2-io.8", "codename": "nasubi", "repository": { "type": "git", From e695c60a1dbe692bd0d4b07cfcb0dbce96210234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sat, 7 Oct 2023 03:38:40 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E8=A8=80=E8=AA=9E?= =?UTF-8?q?=E3=81=8C=E8=A8=AD=E5=AE=9A=E3=81=95=E3=82=8C=E3=81=A6=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=A0=B4=E5=90=88=E3=80=81=E3=83=96=E3=83=A9=E3=82=A6?= =?UTF-8?q?=E3=82=B6=E3=81=AE=E8=A8=80=E8=AA=9E=E3=81=A8=E9=96=A2=E4=BF=82?= =?UTF-8?q?=E3=81=AA=E3=81=8F=E6=97=A5=E6=9C=AC=E8=AA=9E=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B=20(MisskeyIO#175)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/web/boot.js | 18 ++---------------- packages/frontend/src/config.ts | 2 +- packages/sw/src/scripts/lang.ts | 2 +- 3 files changed, 4 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/server/web/boot.js b/packages/backend/src/server/web/boot.js index 022dfab83c..3e31b5d5fa 100644 --- a/packages/backend/src/server/web/boot.js +++ b/packages/backend/src/server/web/boot.js @@ -34,17 +34,9 @@ //#region Detect language & fetch translations if (!localStorage.hasOwnProperty('locale')) { - const supportedLangs = LANGS; let lang = localStorage.getItem('lang'); - if (lang == null || !supportedLangs.includes(lang)) { - if (supportedLangs.includes(navigator.language)) { - lang = navigator.language; - } else { - lang = supportedLangs.find(x => x.split('-')[0] === navigator.language); - - // Fallback - if (lang == null) lang = 'en-US'; - } + if (lang == null || lang.toString == null || lang.toString() === 'null') { + lang = 'ja-JP'; } const metaRes = await window.fetch('/api/meta', { @@ -67,12 +59,6 @@ return; } - // for https://github.com/misskey-dev/misskey/issues/10202 - if (lang == null || lang.toString == null || lang.toString() === 'null') { - console.error('invalid lang value detected!!!', typeof lang, lang); - lang = 'en-US'; - } - const localRes = await window.fetch(`/assets/locales/${lang}.${v}.json`); if (localRes.status === 200) { localStorage.setItem('lang', lang); diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts index 9ce3f8fa15..898d5b8177 100644 --- a/packages/frontend/src/config.ts +++ b/packages/frontend/src/config.ts @@ -13,7 +13,7 @@ export const hostname = address.hostname; export const url = address.origin; export const apiUrl = url + '/api'; export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming'; -export const lang = miLocalStorage.getItem('lang') ?? 'en-US'; +export const lang = miLocalStorage.getItem('lang') ?? 'ja-JP'; export const langs = _LANGS_; const preParseLocale = miLocalStorage.getItem('locale'); export let locale = preParseLocale ? JSON.parse(preParseLocale) : null; diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts index dbe25130b4..c8ce36531a 100644 --- a/packages/sw/src/scripts/lang.ts +++ b/packages/sw/src/scripts/lang.ts @@ -13,7 +13,7 @@ class SwLang { public cacheName = `mk-cache-${_VERSION_}`; public lang: Promise = get('lang').then(async prelang => { - if (!prelang) return 'en-US'; + if (!prelang) return 'ja-JP'; return prelang; }); From 6e46c977e8573d4c3ac5f1297321497e57c030be Mon Sep 17 00:00:00 2001 From: Hidekazu Kobayashi Date: Sat, 7 Oct 2023 05:52:04 +0000 Subject: [PATCH 07/12] Implement DebounceLoader --- packages/backend/src/misc/loader.ts | 49 +++++++++++++ packages/backend/test/unit/misc/loader.ts | 88 +++++++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 packages/backend/src/misc/loader.ts create mode 100644 packages/backend/test/unit/misc/loader.ts diff --git a/packages/backend/src/misc/loader.ts b/packages/backend/src/misc/loader.ts new file mode 100644 index 0000000000..49aac5d883 --- /dev/null +++ b/packages/backend/src/misc/loader.ts @@ -0,0 +1,49 @@ +export type FetchFunction = (key: K) => Promise; +type ResolveReject = Parameters>[0]>; +type ResolverPair = { + resolve: ResolveReject[0]; + reject: ResolveReject[1]; +}; +export class DebounceLoader { + private resolverMap = new Map>(); + private promiseMap = new Map>(); + private resolvedPromise = Promise.resolve(); + constructor(private loadFn: FetchFunction) {} + + public load(key: K): Promise { + const promise = this.promiseMap.get(key); + if (typeof promise !== 'undefined') { + return promise; + } + + const isFirst = this.promiseMap.size === 0; + const newPromise = new Promise((resolve, reject) => { + this.resolverMap.set(key, { resolve, reject }); + }); + this.promiseMap.set(key, newPromise); + + if (isFirst) { + this.enqueueDebouncedLoadJob(); + } + + return newPromise; + } + + private runDebouncedLoad(): void { + const resolvers = [...this.resolverMap]; + this.resolverMap.clear(); + this.promiseMap.clear(); + + for (const [key, { resolve, reject }] of resolvers) { + this.loadFn(key).then(resolve, reject); + } + } + + private enqueueDebouncedLoadJob(): void { + this.resolvedPromise.then(() => { + process.nextTick(() => { + this.runDebouncedLoad(); + }); + }); + } +} diff --git a/packages/backend/test/unit/misc/loader.ts b/packages/backend/test/unit/misc/loader.ts new file mode 100644 index 0000000000..fa37950951 --- /dev/null +++ b/packages/backend/test/unit/misc/loader.ts @@ -0,0 +1,88 @@ +import { DebounceLoader } from '@/misc/loader.js'; + +class Mock { + loadCountByKey = new Map(); + load = async (key: number): Promise => { + const count = this.loadCountByKey.get(key); + if (typeof count === 'undefined') { + this.loadCountByKey.set(key, 1); + } else { + this.loadCountByKey.set(key, count + 1); + } + return key * 2; + }; + reset() { + this.loadCountByKey.clear(); + } +} + +describe(DebounceLoader, () => { + describe('single request', () => { + it('loads once', async () => { + const mock = new Mock(); + const loader = new DebounceLoader(mock.load); + expect(await loader.load(7)).toBe(14); + expect(mock.loadCountByKey.size).toBe(1); + expect(mock.loadCountByKey.get(7)).toBe(1); + }); + }); + + describe('two duplicated requests at same time', () => { + it('loads once', async () => { + const mock = new Mock(); + const loader = new DebounceLoader(mock.load); + const [v1, v2] = await Promise.all([ + loader.load(7), + loader.load(7), + ]); + expect(v1).toBe(14); + expect(v2).toBe(14); + expect(mock.loadCountByKey.size).toBe(1); + expect(mock.loadCountByKey.get(7)).toBe(1); + }); + }); + + describe('two different requests at same time', () => { + it('loads twice', async () => { + const mock = new Mock(); + const loader = new DebounceLoader(mock.load); + const [v1, v2] = await Promise.all([ + loader.load(7), + loader.load(13), + ]); + expect(v1).toBe(14); + expect(v2).toBe(26); + expect(mock.loadCountByKey.size).toBe(2); + expect(mock.loadCountByKey.get(7)).toBe(1); + expect(mock.loadCountByKey.get(13)).toBe(1); + }); + }); + + describe('non-continuous same two requests', () => { + it('loads twice', async () => { + const mock = new Mock(); + const loader = new DebounceLoader(mock.load); + expect(await loader.load(7)).toBe(14); + expect(mock.loadCountByKey.size).toBe(1); + expect(mock.loadCountByKey.get(7)).toBe(1); + mock.reset(); + expect(await loader.load(7)).toBe(14); + expect(mock.loadCountByKey.size).toBe(1); + expect(mock.loadCountByKey.get(7)).toBe(1); + }); + }); + + describe('non-continuous different two requests', () => { + it('loads twice', async () => { + const mock = new Mock(); + const loader = new DebounceLoader(mock.load); + expect(await loader.load(7)).toBe(14); + expect(mock.loadCountByKey.size).toBe(1); + expect(mock.loadCountByKey.get(7)).toBe(1); + mock.reset(); + expect(await loader.load(13)).toBe(26); + expect(mock.loadCountByKey.size).toBe(1); + expect(mock.loadCountByKey.get(13)).toBe(1); + }); + }); +}); From b70ae7a6145dae62847f50ecdd36bdb3f684a07f Mon Sep 17 00:00:00 2001 From: Hidekazu Kobayashi Date: Sat, 7 Oct 2023 05:55:36 +0000 Subject: [PATCH 08/12] Debounce notes.findOneOrFail in NoteEntityService.pack --- .../backend/src/core/entities/NoteEntityService.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index cad850b860..987c9a3016 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -26,6 +26,7 @@ import type { } from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { isNotNull } from '@/misc/is-not-null.js'; +import { DebounceLoader } from '@/misc/loader.js'; import type { OnModuleInit } from '@nestjs/common'; import type { CustomEmojiService } from '../CustomEmojiService.js'; import type { ReactionService } from '../ReactionService.js'; @@ -38,6 +39,7 @@ export class NoteEntityService implements OnModuleInit { private driveFileEntityService: DriveFileEntityService; private customEmojiService: CustomEmojiService; private reactionService: ReactionService; + private noteLoader = new DebounceLoader(this.findNoteOrFail); constructor( private moduleRef: ModuleRef, @@ -304,7 +306,7 @@ export class NoteEntityService implements OnModuleInit { }, options); const meId = me ? me.id : null; - const note = typeof src === 'object' ? src : await this.notesRepository.findOneOrFail({ where: { id: src }, relations: ['user'] }); + const note = typeof src === 'object' ? src : await this.noteLoader.load(src); const host = note.userHost; let text = note.text; @@ -483,4 +485,12 @@ export class NoteEntityService implements OnModuleInit { return await query.getCount(); } + + @bindThis + private async findNoteOrFail(id: string): Promise { + return await this.notesRepository.findOneOrFail({ + where: { id }, + relations: ["user"], + }); + } } From 61246e87793ac59875f727bdf73abd35c704fa00 Mon Sep 17 00:00:00 2001 From: riku6460 <17585784+riku6460@users.noreply.github.com> Date: Sat, 7 Oct 2023 20:26:16 +0900 Subject: [PATCH 09/12] =?UTF-8?q?=E9=80=A3=E5=90=88=E3=81=AA=E3=81=97?= =?UTF-8?q?=E3=82=A2=E3=83=B3=E3=82=B1=E3=83=BC=E3=83=88=E3=81=AEUpdate?= =?UTF-8?q?=E3=81=8C=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E3=81=AB=E9=85=8D?= =?UTF-8?q?=E4=BF=A1=E3=81=95=E3=82=8C=E3=81=A6=E3=81=97=E3=81=BE=E3=81=86?= =?UTF-8?q?=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(MisskeyIO#177)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: MeiMei <30769358+mei23@users.noreply.github.com> --- packages/backend/src/core/PollService.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/core/PollService.ts b/packages/backend/src/core/PollService.ts index 551c779769..2178bdb8e9 100644 --- a/packages/backend/src/core/PollService.ts +++ b/packages/backend/src/core/PollService.ts @@ -96,6 +96,8 @@ export class PollService { const note = await this.notesRepository.findOneBy({ id: noteId }); if (note == null) throw new Error('note not found'); + if (note.localOnly) return; + const user = await this.usersRepository.findOneBy({ id: note.userId }); if (user == null) throw new Error('note not found'); From 2b307bb3bb7517a531735854d6cbfb210cf8d337 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 8 Oct 2023 01:02:39 +0900 Subject: [PATCH 10/12] Bump up version to 13.14.2-io.9 (MisskeyIO#178) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3815cbef91..f1b108bcd7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.14.2-io.8", + "version": "13.14.2-io.9", "codename": "nasubi", "repository": { "type": "git", From 3bbac6f5d197b8ce422422bc2b9572524b5082fc Mon Sep 17 00:00:00 2001 From: riku6460 <17585784+riku6460@users.noreply.github.com> Date: Sun, 8 Oct 2023 04:57:59 +0900 Subject: [PATCH 11/12] =?UTF-8?q?NoteEntityService=20=E3=81=AE=20pack=20?= =?UTF-8?q?=E5=86=85=E3=81=A7=20CustomEmojiService=20=E3=81=AE=20prefetchE?= =?UTF-8?q?mojis=20=E3=82=92=E5=91=BC=E3=81=B6=20(MisskeyIO#179)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit streaming から Note の pack を行う際に populateEmojis から大量にクエリが飛ぶのを回避する prefetchEmojis ではキャッシュ済みのものを除外しているため、packMany が呼ばれた場合でも大量にクエリが飛ぶことはない --- packages/backend/src/core/entities/NoteEntityService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 987c9a3016..3eaf671c68 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -324,6 +324,7 @@ export class NoteEntityService implements OnModuleInit { const reactionEmojiNames = Object.keys(note.reactions) .filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字のみ .map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', '')); + await this.customEmojiService.prefetchEmojis(this.aggregateNoteEmojis([note])); const packedFiles = options?._hint_?.packedFiles; const packed: Packed<'Note'> = await awaitAll({ From 211935640f589021d632ce0333ae0e1698b5111d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 8 Oct 2023 04:58:55 +0900 Subject: [PATCH 12/12] Bump up version to 13.14.2-io.10 (MisskeyIO#180) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f1b108bcd7..f2eace72d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "13.14.2-io.9", + "version": "13.14.2-io.10", "codename": "nasubi", "repository": { "type": "git",