From fcc48640800f6dcf4eb31d438609d00059b654bd Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 07:56:25 +0900 Subject: [PATCH 01/11] perf(backend): reduce needless populateMyReaction calls --- packages/backend/src/core/entities/NoteEntityService.ts | 2 +- packages/backend/src/server/api/stream/channels/channel.ts | 6 ++++-- .../src/server/api/stream/channels/global-timeline.ts | 6 ++++-- packages/backend/src/server/api/stream/channels/hashtag.ts | 6 ++++-- .../backend/src/server/api/stream/channels/home-timeline.ts | 6 ++++-- .../src/server/api/stream/channels/hybrid-timeline.ts | 6 ++++-- .../src/server/api/stream/channels/local-timeline.ts | 6 ++++-- .../backend/src/server/api/stream/channels/user-list.ts | 6 ++++-- 8 files changed, 29 insertions(+), 15 deletions(-) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 7cb7e4b913..9a59da4349 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -357,7 +357,7 @@ export class NoteEntityService implements OnModuleInit { poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, - ...(meId ? { + ...(meId && Object.keys(note.reactions).length > 0 ? { myReaction: this.populateMyReaction(note.id, meId, options?._hint_), } : {}), } : {}), diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index e4c34e00ce..251350ddaa 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -46,8 +46,10 @@ class ChannelChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index c499d1787e..9c623abf76 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -72,8 +72,10 @@ class GlobalTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 2cfe9572d3..247145d8c5 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -51,8 +51,10 @@ class HashtagChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 3ccf4af66d..eed5c699d9 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -74,8 +74,10 @@ class HomeTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 760fab60a4..d9b5cafc85 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -88,8 +88,10 @@ class HybridTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index a211041134..2424b468df 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -71,8 +71,10 @@ class LocalTimelineChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index b73cedaa8b..c3b4c9d742 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -103,8 +103,10 @@ class UserListChannel extends Channel { if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; if (this.user && note.renoteId && !note.text) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + if (note.renote && Object.keys(note.renote.reactions).length > 0) { + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + note.renote!.myReaction = myRenoteReaction; + } } this.connection.cacheNote(note); From 2dfbf97db4fd18632cbbee5d1ec5fa83e29f9978 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 07:59:58 +0900 Subject: [PATCH 02/11] refactor --- packages/backend/src/server/api/stream/channels/channel.ts | 2 +- .../backend/src/server/api/stream/channels/global-timeline.ts | 2 +- packages/backend/src/server/api/stream/channels/hashtag.ts | 2 +- .../backend/src/server/api/stream/channels/home-timeline.ts | 2 +- .../backend/src/server/api/stream/channels/hybrid-timeline.ts | 2 +- .../backend/src/server/api/stream/channels/local-timeline.ts | 2 +- packages/backend/src/server/api/stream/channels/user-list.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 251350ddaa..4d85e1ceec 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -48,7 +48,7 @@ class ChannelChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 9c623abf76..18b8e8245b 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -74,7 +74,7 @@ class GlobalTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index 247145d8c5..fb9226413b 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -53,7 +53,7 @@ class HashtagChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index eed5c699d9..6914841534 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -76,7 +76,7 @@ class HomeTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index d9b5cafc85..c353aaac0f 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -90,7 +90,7 @@ class HybridTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 2424b468df..0823799d2c 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -73,7 +73,7 @@ class LocalTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index c3b4c9d742..a5a9d189b4 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -105,7 +105,7 @@ class UserListChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); - note.renote!.myReaction = myRenoteReaction; + note.renote.myReaction = myRenoteReaction; } } From 4d1d25e02f9863ede37b6c981f3c2418b14cf3a7 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 08:07:22 +0900 Subject: [PATCH 03/11] perf(backend): improve my reaction population performance --- packages/backend/src/core/entities/NoteEntityService.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 9a59da4349..66dbb66167 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -177,10 +177,9 @@ export class NoteEntityService implements OnModuleInit { const reaction = _hint_.myReactions.get(noteId); if (reaction) { return this.reactionService.convertLegacyReaction(reaction.reaction); - } else if (reaction === null) { + } else { return undefined; } - // 実装上抜けがあるだけかもしれないので、「ヒントに含まれてなかったら(=undefinedなら)return」のようにはしない } // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない @@ -387,11 +386,11 @@ export class NoteEntityService implements OnModuleInit { const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない const oldId = this.idService.gen(Date.now() - 2000); - const targets = [...notes.filter(n => n.id < oldId).map(n => n.id), ...renoteIds]; - const myReactions = await this.noteReactionsRepository.findBy({ + const targets = [...notes.filter(n => (n.id < oldId) && (Object.keys(n.reactions).length > 0)).map(n => n.id), ...renoteIds]; + const myReactions = targets.length > 0 ? await this.noteReactionsRepository.findBy({ userId: meId, noteId: In(targets), - }); + }) : []; for (const target of targets) { myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); From 1671575d5d3d081c83f172f3439884010aafeb59 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 09:20:19 +0900 Subject: [PATCH 04/11] =?UTF-8?q?perf(backend):=20=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=AE=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E6=83=85=E5=A0=B1=E3=82=92=E3=82=AD=E3=83=A3=E3=83=83?= =?UTF-8?q?=E3=82=B7=E3=83=A5=E3=81=99=E3=82=8B=E3=81=93=E3=81=A8=E3=81=A7?= =?UTF-8?q?DB=E3=81=B8=E3=81=AE=E3=82=AF=E3=82=A8=E3=83=AA=E3=82=92?= =?UTF-8?q?=E5=89=8A=E6=B8=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...673894459-note-reactionAndUserPairCache.js | 17 +++++ .../backend/src/core/NoteCreateService.ts | 2 +- packages/backend/src/core/ReactionService.ts | 4 + .../src/core/entities/NoteEntityService.ts | 75 +++++++++++++++---- packages/backend/src/models/Note.ts | 5 ++ .../backend/src/models/json-schema/note.ts | 8 ++ .../src/server/api/stream/channels/channel.ts | 2 +- .../api/stream/channels/global-timeline.ts | 2 +- .../src/server/api/stream/channels/hashtag.ts | 2 +- .../api/stream/channels/home-timeline.ts | 2 +- .../api/stream/channels/hybrid-timeline.ts | 3 +- .../api/stream/channels/local-timeline.ts | 2 +- .../server/api/stream/channels/user-list.ts | 2 +- 13 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js diff --git a/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js b/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js new file mode 100644 index 0000000000..fe0ea282d2 --- /dev/null +++ b/packages/backend/migration/1697673894459-note-reactionAndUserPairCache.js @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + + +export class NoteReactionAndUserPairCache1697673894459 { + name = 'NoteReactionAndUserPairCache1697673894459' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "reactionAndUserPairCache" character varying(1024) array NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "reactionAndUserPairCache"`); + } +} diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 30d7f3e76a..2c00418c47 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -584,7 +584,7 @@ export class NoteCreateService implements OnApplicationShutdown { } // Pack the note - const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true }); + const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true, withReactionAndUserPairCache: true }); this.globalEventService.publishNotesStream(noteObj); diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 1458e2b173..edf433025d 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -187,6 +187,9 @@ export class ReactionService { await this.notesRepository.createQueryBuilder().update() .set({ reactions: () => sql, + ...(note.reactionAndUserPairCache.length < 10 ? { + reactionAndUserPairCache: () => `array_append("reactionAndUserPairCache", '${user.id}:${reaction}')`, + } : {}), }) .where('id = :id', { id: note.id }) .execute(); @@ -293,6 +296,7 @@ export class ReactionService { await this.notesRepository.createQueryBuilder().update() .set({ reactions: () => sql, + reactionAndUserPairCache: () => `array_remove("reactionAndUserPairCache", '${user.id}:${exist.reaction}')`, }) .where('id = :id', { id: note.id }) .execute(); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 66dbb66167..b46b5528a5 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -170,26 +170,37 @@ export class NoteEntityService implements OnModuleInit { } @bindThis - public async populateMyReaction(noteId: MiNote['id'], meId: MiUser['id'], _hint_?: { - myReactions: Map; + public async populateMyReaction(note: { id: MiNote['id']; reactions: MiNote['reactions']; reactionAndUserPairCache?: MiNote['reactionAndUserPairCache']; }, meId: MiUser['id'], _hint_?: { + myReactions: Map; }) { if (_hint_?.myReactions) { - const reaction = _hint_.myReactions.get(noteId); + const reaction = _hint_.myReactions.get(note.id); if (reaction) { - return this.reactionService.convertLegacyReaction(reaction.reaction); + return this.reactionService.convertLegacyReaction(reaction); + } else { + return undefined; + } + } + + const reactionsCount = Object.values(note.reactions).reduce((a, b) => a + b, 0); + if (reactionsCount === 0) return undefined; + if (note.reactionAndUserPairCache && reactionsCount <= note.reactionAndUserPairCache.length) { + const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId)); + if (pair) { + return this.reactionService.convertLegacyReaction(pair.split(':')[1]); } else { return undefined; } } // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない - if (this.idService.parse(noteId).date.getTime() + 2000 > Date.now()) { + if (this.idService.parse(note.id).date.getTime() + 2000 > Date.now()) { return undefined; } const reaction = await this.noteReactionsRepository.findOneBy({ userId: meId, - noteId: noteId, + noteId: note.id, }); if (reaction) { @@ -275,8 +286,9 @@ export class NoteEntityService implements OnModuleInit { options?: { detail?: boolean; skipHide?: boolean; + withReactionAndUserPairCache?: boolean; _hint_?: { - myReactions: Map; + myReactions: Map; packedFiles: Map | null>; }; }, @@ -284,6 +296,7 @@ export class NoteEntityService implements OnModuleInit { const opts = Object.assign({ detail: true, skipHide: false, + withReactionAndUserPairCache: false, }, options); const meId = me ? me.id : null; @@ -324,6 +337,7 @@ export class NoteEntityService implements OnModuleInit { repliesCount: note.repliesCount, reactions: this.reactionService.convertLegacyReactions(note.reactions), reactionEmojis: this.customEmojiService.populateEmojis(reactionEmojiNames, host), + reactionAndUserPairCache: opts.withReactionAndUserPairCache ? note.reactionAndUserPairCache : undefined, emojis: host != null ? this.customEmojiService.populateEmojis(note.emojis, host) : undefined, tags: note.tags.length > 0 ? note.tags : undefined, fileIds: note.fileIds, @@ -346,18 +360,20 @@ export class NoteEntityService implements OnModuleInit { reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, { detail: false, + withReactionAndUserPairCache: opts.withReactionAndUserPairCache, _hint_: options?._hint_, }) : undefined, renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, { detail: true, + withReactionAndUserPairCache: opts.withReactionAndUserPairCache, _hint_: options?._hint_, }) : undefined, poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, ...(meId && Object.keys(note.reactions).length > 0 ? { - myReaction: this.populateMyReaction(note.id, meId, options?._hint_), + myReaction: this.populateMyReaction(note, meId, options?._hint_), } : {}), } : {}), }); @@ -381,19 +397,48 @@ export class NoteEntityService implements OnModuleInit { if (notes.length === 0) return []; const meId = me ? me.id : null; - const myReactionsMap = new Map(); + const myReactionsMap = new Map(); if (meId) { - const renoteIds = notes.filter(n => n.renoteId != null).map(n => n.renoteId!); + const idsNeedFetchMyReaction = new Set(); + // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない const oldId = this.idService.gen(Date.now() - 2000); - const targets = [...notes.filter(n => (n.id < oldId) && (Object.keys(n.reactions).length > 0)).map(n => n.id), ...renoteIds]; - const myReactions = targets.length > 0 ? await this.noteReactionsRepository.findBy({ + + for (const note of notes) { + if (note.renote && (note.text == null && note.fileIds.length === 0)) { // pure renote + const reactionsCount = Object.values(note.renote.reactions).reduce((a, b) => a + b, 0); + if (reactionsCount === 0) { + myReactionsMap.set(note.renote.id, null); + } else if (reactionsCount <= note.renote.reactionAndUserPairCache.length) { + const pair = note.renote.reactionAndUserPairCache.find(p => p.startsWith(meId)); + myReactionsMap.set(note.renote.id, pair ? pair.split(':')[1] : null); + } else { + idsNeedFetchMyReaction.add(note.renote.id); + } + } else { + if (note.id < oldId) { + const reactionsCount = Object.values(note.reactions).reduce((a, b) => a + b, 0); + if (reactionsCount === 0) { + myReactionsMap.set(note.id, null); + } else if (reactionsCount <= note.reactionAndUserPairCache.length) { + const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId)); + myReactionsMap.set(note.id, pair ? pair.split(':')[1] : null); + } else { + idsNeedFetchMyReaction.add(note.id); + } + } else { + myReactionsMap.set(note.id, null); + } + } + } + + const myReactions = idsNeedFetchMyReaction.size > 0 ? await this.noteReactionsRepository.findBy({ userId: meId, - noteId: In(targets), + noteId: In(Array.from(idsNeedFetchMyReaction)), }) : []; - for (const target of targets) { - myReactionsMap.set(target, myReactions.find(reaction => reaction.noteId === target) ?? null); + for (const id of idsNeedFetchMyReaction) { + myReactionsMap.set(id, myReactions.find(reaction => reaction.noteId === id)?.reaction ?? null); } } diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index ac7f57d5d6..a4358b9ba6 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -164,6 +164,11 @@ export class MiNote { }) public mentionedRemoteUsers: string; + @Column('varchar', { + length: 1024, array: true, default: '{}', + }) + public reactionAndUserPairCache: string[]; + @Column('varchar', { length: 128, array: true, default: '{}', }) diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 2caf0d0c3d..38c0054b55 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -174,6 +174,14 @@ export const packedNoteSchema = { type: 'string', optional: true, nullable: false, }, + reactionAndUserPairCache: { + type: 'array', + optional: true, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, myReaction: { type: 'object', diff --git a/packages/backend/src/server/api/stream/channels/channel.ts b/packages/backend/src/server/api/stream/channels/channel.ts index 4d85e1ceec..57034231a3 100644 --- a/packages/backend/src/server/api/stream/channels/channel.ts +++ b/packages/backend/src/server/api/stream/channels/channel.ts @@ -47,7 +47,7 @@ class ChannelChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 18b8e8245b..553c44071f 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -73,7 +73,7 @@ class GlobalTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/hashtag.ts b/packages/backend/src/server/api/stream/channels/hashtag.ts index fb9226413b..f30b29cfd6 100644 --- a/packages/backend/src/server/api/stream/channels/hashtag.ts +++ b/packages/backend/src/server/api/stream/channels/hashtag.ts @@ -52,7 +52,7 @@ class HashtagChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index 6914841534..2b235b9822 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -75,7 +75,7 @@ class HomeTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index c353aaac0f..4e90912084 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -89,7 +89,8 @@ class HybridTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + console.log(note.renote.reactionAndUserPairCache); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index 0823799d2c..9dd05b9b08 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -72,7 +72,7 @@ class LocalTimelineChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } diff --git a/packages/backend/src/server/api/stream/channels/user-list.ts b/packages/backend/src/server/api/stream/channels/user-list.ts index a5a9d189b4..6d83d4cb5b 100644 --- a/packages/backend/src/server/api/stream/channels/user-list.ts +++ b/packages/backend/src/server/api/stream/channels/user-list.ts @@ -104,7 +104,7 @@ class UserListChannel extends Channel { if (this.user && note.renoteId && !note.text) { if (note.renote && Object.keys(note.renote.reactions).length > 0) { - const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renoteId, this.user.id); + const myRenoteReaction = await this.noteEntityService.populateMyReaction(note.renote, this.user.id); note.renote.myReaction = myRenoteReaction; } } From f9549e1f1b924e75270bf5b75aee1a399fc9a2a0 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 11:17:59 +0900 Subject: [PATCH 05/11] fix(backend): fix of 1671575d5d --- packages/backend/src/core/ReactionService.ts | 4 ++-- packages/backend/src/core/entities/NoteEntityService.ts | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index edf433025d..f3f2b04dd6 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -188,7 +188,7 @@ export class ReactionService { .set({ reactions: () => sql, ...(note.reactionAndUserPairCache.length < 10 ? { - reactionAndUserPairCache: () => `array_append("reactionAndUserPairCache", '${user.id}:${reaction}')`, + reactionAndUserPairCache: () => `array_append("reactionAndUserPairCache", '${user.id}/${reaction}')`, } : {}), }) .where('id = :id', { id: note.id }) @@ -296,7 +296,7 @@ export class ReactionService { await this.notesRepository.createQueryBuilder().update() .set({ reactions: () => sql, - reactionAndUserPairCache: () => `array_remove("reactionAndUserPairCache", '${user.id}:${exist.reaction}')`, + reactionAndUserPairCache: () => `array_remove("reactionAndUserPairCache", '${user.id}/${exist.reaction}')`, }) .where('id = :id', { id: note.id }) .execute(); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index b46b5528a5..c100b92ee7 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -187,7 +187,7 @@ export class NoteEntityService implements OnModuleInit { if (note.reactionAndUserPairCache && reactionsCount <= note.reactionAndUserPairCache.length) { const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId)); if (pair) { - return this.reactionService.convertLegacyReaction(pair.split(':')[1]); + return this.reactionService.convertLegacyReaction(pair.split('/')[1]); } else { return undefined; } @@ -411,7 +411,7 @@ export class NoteEntityService implements OnModuleInit { myReactionsMap.set(note.renote.id, null); } else if (reactionsCount <= note.renote.reactionAndUserPairCache.length) { const pair = note.renote.reactionAndUserPairCache.find(p => p.startsWith(meId)); - myReactionsMap.set(note.renote.id, pair ? pair.split(':')[1] : null); + myReactionsMap.set(note.renote.id, pair ? pair.split('/')[1] : null); } else { idsNeedFetchMyReaction.add(note.renote.id); } @@ -422,7 +422,7 @@ export class NoteEntityService implements OnModuleInit { myReactionsMap.set(note.id, null); } else if (reactionsCount <= note.reactionAndUserPairCache.length) { const pair = note.reactionAndUserPairCache.find(p => p.startsWith(meId)); - myReactionsMap.set(note.id, pair ? pair.split(':')[1] : null); + myReactionsMap.set(note.id, pair ? pair.split('/')[1] : null); } else { idsNeedFetchMyReaction.add(note.id); } From 428d39a460f6672b675d751dfb0457538dee536a Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 11:18:17 +0900 Subject: [PATCH 06/11] chore: disable debug log of fastify --- packages/backend/src/server/ServerService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts index e598b91e51..757cf21615 100644 --- a/packages/backend/src/server/ServerService.ts +++ b/packages/backend/src/server/ServerService.ts @@ -73,7 +73,7 @@ export class ServerService implements OnApplicationShutdown { public async launch(): Promise { const fastify = Fastify({ trustProxy: true, - logger: !['production', 'test'].includes(process.env.NODE_ENV ?? ''), + logger: false, }); this.#fastify = fastify; From ec45db7870c0e157e9069bd2bceb1329c136a9fa Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 11:19:42 +0900 Subject: [PATCH 07/11] refactor and perf tweak --- packages/backend/src/core/ReactionService.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index f3f2b04dd6..4233b8d4c3 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -30,6 +30,7 @@ import { RoleService } from '@/core/RoleService.js'; import { FeaturedService } from '@/core/FeaturedService.js'; const FALLBACK = '❤'; +const PER_NOTE_REACTION_USER_PAIR_CACHE_MAX = 16; const legacies: Record = { 'like': '👍', @@ -187,7 +188,7 @@ export class ReactionService { await this.notesRepository.createQueryBuilder().update() .set({ reactions: () => sql, - ...(note.reactionAndUserPairCache.length < 10 ? { + ...(note.reactionAndUserPairCache.length < PER_NOTE_REACTION_USER_PAIR_CACHE_MAX ? { reactionAndUserPairCache: () => `array_append("reactionAndUserPairCache", '${user.id}/${reaction}')`, } : {}), }) From 30efd932a5828def2cd394e65d333fdbdb447231 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 11:42:17 +0900 Subject: [PATCH 08/11] =?UTF-8?q?enhance:=20nyaize=E3=81=AF=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=82=A2=E3=83=B3=E3=83=88=E3=81=A7=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E6=99=82=E3=81=AB=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolve #12030 --- CHANGELOG.md | 3 ++- .../backend/src/core/NoteCreateService.ts | 23 +------------------ .../global/MkMisskeyFlavoredMarkdown.ts | 16 ++++++++----- packages/frontend/src/scripts/nyaize.ts | 20 ++++++++++++++++ packages/misskey-js/etc/misskey-js.api.md | 6 +++-- packages/misskey-js/src/entities.ts | 2 ++ 6 files changed, 39 insertions(+), 31 deletions(-) create mode 100644 packages/frontend/src/scripts/nyaize.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index ee2d39cdef..8f895f8009 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,8 @@ - Enhance: ストリーミングAPIのパフォーマンスを向上 - Fix: users/notesでDBから参照した際にチャンネル投稿のみ取得される問題を修正 - Fix: コントロールパネルの設定項目が正しく保存できない問題を修正 -- Change: nyaizeはAPIレスポンス時ではなく投稿時に一度だけ非可逆的に行われるようになりました +- Change: ユーザーのisCatがtrueでも、サーバーではnyaizeが行われなくなりました + - isCatな場合、クライアントでnyaize処理を行うことを推奨します ## 2023.10.1 ### General diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 2c00418c47..f5cfe03122 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -227,8 +227,6 @@ export class NoteCreateService implements OnApplicationShutdown { isBot: MiUser['isBot']; isCat: MiUser['isCat']; }, data: Option, silent = false): Promise { - let patsedText: mfm.MfmNode[] | null = null; - // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { @@ -315,25 +313,6 @@ export class NoteCreateService implements OnApplicationShutdown { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); } data.text = data.text.trim(); - - if (user.isCat) { - patsedText = mfm.parse(data.text); - function nyaizeNode(node: mfm.MfmNode) { - if (node.type === 'quote') return; - if (node.type === 'text') { - node.props.text = nyaize(node.props.text); - } - if (node.children) { - for (const child of node.children) { - nyaizeNode(child); - } - } - } - for (const node of patsedText) { - nyaizeNode(node); - } - data.text = mfm.toString(patsedText); - } } else { data.text = null; } @@ -344,7 +323,7 @@ export class NoteCreateService implements OnApplicationShutdown { // Parse MFM if needed if (!tags || !emojis || !mentionedUsers) { - const tokens = patsedText ?? (data.text ? mfm.parse(data.text)! : []); + const tokens = (data.text ? mfm.parse(data.text)! : []); const cwTokens = data.cw ? mfm.parse(data.cw)! : []; const choiceTokens = data.poll && data.poll.choices ? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 2ae3fc89c8..ea3655f6bb 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -17,6 +17,7 @@ import MkSparkle from '@/components/MkSparkle.vue'; import MkA from '@/components/global/MkA.vue'; import { host } from '@/config.js'; import { defaultStore } from '@/store.js'; +import { nyaize } from '@/scripts/nyaize.js'; const QUOTE_STYLE = ` display: block; @@ -55,10 +56,13 @@ export default function(props: { * @param ast MFM AST * @param scale How times large the text is */ - const genEl = (ast: mfm.MfmNode[], scale: number) => ast.map((token): VNode | string | (VNode | string)[] => { + const genEl = (ast: mfm.MfmNode[], scale: number, disableNyaize = false) => ast.map((token): VNode | string | (VNode | string)[] => { switch (token.type) { case 'text': { - const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n'); + let text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n'); + if (!disableNyaize && props.author.isCat) { + text = nyaize(text); + } if (!props.plain) { const res: (VNode | string)[] = []; @@ -260,7 +264,7 @@ export default function(props: { key: Math.random(), url: token.props.url, rel: 'nofollow noopener', - }, genEl(token.children, scale))]; + }, genEl(token.children, scale, true))]; } case 'mention': { @@ -299,11 +303,11 @@ export default function(props: { if (!props.nowrap) { return [h('div', { style: QUOTE_STYLE, - }, genEl(token.children, scale))]; + }, genEl(token.children, scale, true))]; } else { return [h('span', { style: QUOTE_STYLE, - }, genEl(token.children, scale))]; + }, genEl(token.children, scale, true))]; } } @@ -358,7 +362,7 @@ export default function(props: { } case 'plain': { - return [h('span', genEl(token.children, scale))]; + return [h('span', genEl(token.children, scale, true))]; } default: { diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts new file mode 100644 index 0000000000..0ac77e1006 --- /dev/null +++ b/packages/frontend/src/scripts/nyaize.ts @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export function nyaize(text: string): string { + return text + // ja-JP + .replaceAll('な', 'にゃ').replaceAll('ナ', 'ニャ').replaceAll('ナ', 'ニャ') + // en-US + .replace(/(?<=n)a/gi, x => x === 'A' ? 'YA' : 'ya') + .replace(/(?<=morn)ing/gi, x => x === 'ING' ? 'YAN' : 'yan') + .replace(/(?<=every)one/gi, x => x === 'ONE' ? 'NYAN' : 'nyan') + // ko-KR + .replace(/[나-낳]/g, match => String.fromCharCode( + match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0), + )) + .replace(/(다$)|(다(?=\.))|(다(?= ))|(다(?=!))|(다(?=\?))/gm, '다냥') + .replace(/(야(?=\?))|(야$)|(야(?= ))/gm, '냥'); +} diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 895f34689b..54bbfae145 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2977,6 +2977,8 @@ type UserLite = { faviconUrl: Instance['faviconUrl']; themeColor: Instance['themeColor']; }; + isCat?: boolean; + isBot?: boolean; }; // @public (undocumented) @@ -2987,8 +2989,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts -// src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:603:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:109:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts +// src/entities.ts:605:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index e05412abb9..659045bd6e 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -28,6 +28,8 @@ export type UserLite = { faviconUrl: Instance['faviconUrl']; themeColor: Instance['themeColor']; }; + isCat?: boolean; + isBot?: boolean; }; export type UserDetailed = UserLite & { From 5891adc5cfbdd02a091d8a60b44b4ac4216d5756 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 11:42:52 +0900 Subject: [PATCH 09/11] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f895f8009..d0db7ed654 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ - Enhance: TLの返信表示オプションを記憶するように ### Server +- Enhance: タイムライン取得時のパフォーマンスを向上 - Enhance: ストリーミングAPIのパフォーマンスを向上 - Fix: users/notesでDBから参照した際にチャンネル投稿のみ取得される問題を修正 - Fix: コントロールパネルの設定項目が正しく保存できない問題を修正 From f85a6559158e0227203771614fbad4a44c5a8668 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 11:43:28 +0900 Subject: [PATCH 10/11] 2023.10.2-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8808b71d72..a60c641e60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.2-beta.1", + "version": "2023.10.2-beta.2", "codename": "nasubi", "repository": { "type": "git", From 431d8c7802df0df8863211fd797a2eb073be7229 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 19 Oct 2023 16:22:19 +0900 Subject: [PATCH 11/11] =?UTF-8?q?fix(backend):=20Redis=E3=81=8C=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=81=AE=E3=81=A8=E3=81=8D=E3=81=ABhybrid-timeline?= =?UTF-8?q?=E3=81=ABwithReplies=20=3D=20true=E3=81=A7=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=BB=E3=82=B9=E3=81=99=E3=82=8B=E3=81=A8SQL=E3=81=AE?= =?UTF-8?q?=E3=82=B7=E3=83=B3=E3=82=BF=E3=83=83=E3=82=AF=E3=82=B9=E3=82=A8?= =?UTF-8?q?=E3=83=A9=E3=83=BC=E3=81=AB=E3=81=AA=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix #12064 --- .../backend/src/server/api/endpoints/notes/hybrid-timeline.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 9986c6e436..f8a7a6a8b7 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -123,6 +123,8 @@ export default class extends Endpoint { // eslint- noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); + shouldFallbackToDb = shouldFallbackToDb || (noteIds.length === 0); + if (!shouldFallbackToDb) { const query = this.notesRepository.createQueryBuilder('note') .where('note.id IN (:...noteIds)', { noteIds: noteIds })