diff --git a/CHANGELOG.md b/CHANGELOG.md index aba43b9e3a..7bb1a3e8bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -770,7 +770,7 @@ - Fix: カスタム絵文字の画像読み込みに失敗した際はテキストではなくダミー画像を表示 #13487 ### Server -- +- Fix: FTT有効かつDBフォールバック有効時、STLのようにタイムラインのソースが複数だとFTTとDBのフォールバック間で取得されないノートがある問題 ## 2024.3.0 diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 6253f792ed..4499dd0ccc 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -38,6 +38,7 @@ type TimelineOptions = { excludePureRenotes: boolean; ignoreAuthorFromUserSuspension?: boolean; dbFallback: (untilId: string | null, sinceId: string | null, limit: number) => Promise, + preventEmptyTimelineDbFallback?: boolean; }; @Injectable() @@ -71,12 +72,20 @@ export class FanoutTimelineEndpointService { const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId); - // TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい - const redisResultIds = Array.from(new Set(redisResult.flat(1))).sort(idCompare); + // オプション無効時、取得したredisResultのうち、2つ以上ソースがあり、1つでも空であればDBにフォールバックする + let shouldFallbackToDb = ps.useDbFallback && + (ps.preventEmptyTimelineDbFallback !== true && redisResult.length > 1 && redisResult.some(ids => ids.length === 0)); + + // 取得したresultの中で最古のIDのうち、最も新しいものを取得 + const fttThresholdId = redisResult.map(ids => ids[0]).sort()[0]; + + // TODO: いい感じにgetMulti内でソート済だからuniqするときにredisResultが全てソート済なのを利用して再ソートを避けたい + const redisResultIds = shouldFallbackToDb ? [] : Array.from(new Set(redisResult.flat(1))).sort(idCompare); + + let noteIds = redisResultIds.filter(id => id >= fttThresholdId).slice(0, ps.limit); - let noteIds = redisResultIds.slice(0, ps.limit); const oldestNoteId = ascending ? redisResultIds[0] : redisResultIds[redisResultIds.length - 1]; - const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId; + shouldFallbackToDb ||= ps.useDbFallback && (noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId); if (!shouldFallbackToDb) { let filter = ps.noteFilter ?? (_note => true); diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 0c64df569d..11450acb65 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -150,6 +150,7 @@ export default class extends Endpoint { // eslint- withFiles: ps.withFiles, withRenotes: ps.withRenotes, }, me), + preventEmptyTimelineDbFallback: true, }); return timeline;