computedを呼び出しごとに生成する(キャッシュしない)

This commit is contained in:
tamaina
2023-08-12 14:16:52 +00:00
parent b205f7746b
commit f9d0ad9163
+53 -71
View File
@@ -72,13 +72,6 @@ export class NoteManager {
*/ */
private notesSource: Ref<Map<Note['id'], CachedNoteSource>>; private notesSource: Ref<Map<Note['id'], CachedNoteSource>>;
/**
* ソースからuser, renote, replyを取得したComputedRefのキャッシュを保持しておく
* nullは削除済みであることを表す
* キャプチャが0になったら削除される
*/
private notesComputed: Map<Note['id'], CachedNote>;
private updatedAt: Map<Note['id'], number>; private updatedAt: Map<Note['id'], number>;
private captureing: Map<Note['id'], number>; private captureing: Map<Note['id'], number>;
private connection: Stream | null; private connection: Stream | null;
@@ -86,7 +79,6 @@ export class NoteManager {
constructor() { constructor() {
this.notesSource = ref(new Map()); this.notesSource = ref(new Map());
this.notesComputed = new Map();
this.updatedAt = new Map(); this.updatedAt = new Map();
this.captureing = new Map(); this.captureing = new Map();
this.connection = $i ? useStream() : null; this.connection = $i ? useStream() : null;
@@ -156,57 +148,53 @@ export class NoteManager {
} }
public get(id: string): CachedNote { public get(id: string): CachedNote {
if (!this.notesComputed.has(id)) { // computedをキャッシュしたいけどバグるのでgetごとにcomputedを作成する
this.notesComputed.set(id, computed<Note | null>(() => {
const note = this.notesSource.value.get(id);
if (this.isDebuggerEnabled) console.log('NoteManager: compute note', id, note); const result = computed<Note | null>(() => {
const note = this.notesSource.value.get(id);
if (!note) { if (this.isDebuggerEnabled) console.log('NoteManager: compute note', id, note);
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: source is null', id);
return null;
}
const user = userLiteManager.get(note.userId)!; if (!note) {
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: source is null', id);
const renote = note.renoteId ? this.get(note.renoteId) : undefined; return null;
// renoteが削除されている場合はCASCADE削除されるためnullを返す
if (renote && !renote.value) {
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: renote is null', id, note.renoteId);
return null;
}
const reply = note.replyId ? this.get(note.replyId) : undefined;
// replyが削除されている場合はCASCADE削除されるためnullを返す
if (reply && !reply.value) {
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: reply is null', id, note.replyId);
return null;
}
const files = note.fileIds.map(id => driveFileManager.get(id)?.value);
const result = Object.assign({}, note, {
user: user.value,
renote: renote?.value ?? undefined,
reply: reply?.value ?? undefined,
files: files.filter(file => file) as DriveFile[],
});
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: not null', id, result);
return result;
}));
if (this.isDebuggerEnabled) {
console.log('NoteManager: get note (new)', id, this.notesComputed.get(id));
console.trace();
} }
} else {
if (this.isDebuggerEnabled) { const user = userLiteManager.get(note.userId)!;
console.log('NoteManager: get note (cached)', id, this.notesComputed.get(id), this.notesSource.value.get(id));
console.trace(); const renote = note.renoteId ? this.get(note.renoteId) : undefined;
// renoteが削除されている場合はCASCADE削除されるためnullを返す
if (renote && !renote.value) {
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: renote is null', id, note.renoteId);
return null;
} }
const reply = note.replyId ? this.get(note.replyId) : undefined;
// replyが削除されている場合はCASCADE削除されるためnullを返す
if (reply && !reply.value) {
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: reply is null', id, note.replyId);
return null;
}
const files = note.fileIds.map(id => driveFileManager.get(id)?.value);
const result = {
...note,
user: user.value,
renote: renote?.value ?? undefined,
reply: reply?.value ?? undefined,
files: files.filter(file => file) as DriveFile[],
};
if (this.isDebuggerEnabled) console.log('NoteManager: compute note: not null', id, result);
return result;
});
if (this.isDebuggerEnabled) {
console.log('NoteManager: get note (new)', id, result);
console.trace();
} }
return this.notesComputed.get(id)!; return result;
} }
/** /**
@@ -282,21 +270,22 @@ export class NoteManager {
public async fetch(id: string, force = false): Promise<CachedNote> { public async fetch(id: string, force = false): Promise<CachedNote> {
if (this.isDebuggerEnabled) console.log('NoteManager: fetch note', id, { force }); if (this.isDebuggerEnabled) console.log('NoteManager: fetch note', id, { force });
const note = this.get(id);
if (!force) { if (!force) {
const cachedNote = this.get(id); if (note.value === null) {
if (cachedNote.value === null) {
// 削除されている場合はnullを返す // 削除されている場合はnullを返す
if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (deleted)', id, cachedNote.value); if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (deleted)', id, note.value);
return cachedNote; return note;
} }
// Renoteの場合はRenote元の更新日時も考慮する // Renoteの場合はRenote元の更新日時も考慮する
const updatedAt = isRenote(cachedNote.value) ? const updatedAt = isRenote(note.value) ?
this.updatedAt.get(id) : this.updatedAt.get(id) :
Math.max(this.updatedAt.get(id) ?? 0, this.updatedAt.get(cachedNote.value!.renoteId!) ?? 0); Math.max(this.updatedAt.get(id) ?? 0, this.updatedAt.get(note.value!.renoteId!) ?? 0);
// 2分以上経過していない場合はキャッシュを返す // 2分以上経過していない場合はキャッシュを返す
if (cachedNote && updatedAt && Date.now() - updatedAt < 1000 * 120) { if (note && updatedAt && Date.now() - updatedAt < 1000 * 120) {
if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (use cache)', id, { cachedNote, updatedAt }); if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (use cache)', id, { note, updatedAt });
return cachedNote; return note;
} }
} }
if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (fetch)', id); if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (fetch)', id);
@@ -304,14 +293,14 @@ export class NoteManager {
.then(fetchedNote => { .then(fetchedNote => {
if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (success)', id, fetchedNote); if (this.isDebuggerEnabled) console.log('NoteManager: fetch note (success)', id, fetchedNote);
this.set(fetchedNote); this.set(fetchedNote);
return this.get(id)!; return note;
}) })
.catch(err => { .catch(err => {
if (this.isDebuggerEnabled) { if (this.isDebuggerEnabled) {
console.error('NoteManager: fetch note (error)', id, err); console.error('NoteManager: fetch note (error)', id, err);
} }
// エラーが発生した場合は何もしない // エラーが発生した場合は何もしない
return this.get(id)!; return note;
}); });
} }
@@ -404,7 +393,6 @@ export class NoteManager {
if (type !== 'deleted') { if (type !== 'deleted') {
if (!note) throw new Error('NoteManager: onStreamNoteUpdated (update) note.value is null'); if (!note) throw new Error('NoteManager: onStreamNoteUpdated (update) note.value is null');
this.notesSource.value.set(id, { ...note, ...diff }); this.notesSource.value.set(id, { ...note, ...diff });
if (this.notesComputed.has(id)) triggerRef(this.notesComputed.get(id)!);
} }
this.updatedAt.set(id, Date.now()); this.updatedAt.set(id, Date.now());
} }
@@ -444,12 +432,6 @@ export class NoteManager {
} }
this.captureing.delete(id); this.captureing.delete(id);
// キャプチャが終わったらcomputedキャッシュも消してしまう
if (!noDeletion) {
this.notesComputed.delete(id);
if (this.isDebuggerEnabled) console.log('NoteManager: decapture (delete computed)', id);
}
} }
/** /**