fix: eliminate racing saveData() calls during message restore
When onOpen() restored saved messages, each appendMessage() call fired a fire-and-forget saveMessages() → saveData(). With N saved messages, N overlapping saveData() writes raced against each other. If a stale write landed last, the persisted data could be corrupted or appear empty on next restart. Fix: add _restoring flag. Skip saveMessages() inside appendMessage() while restoring. Do a single clean save after the restore loop. Also wraps the restore loop in try/catch so a rendering failure in one message doesn't silently kill the entire restore.
This commit is contained in:
parent
0cebcf6084
commit
fc5c9c01bf
2 changed files with 29 additions and 11 deletions
12
main.js
12
main.js
File diff suppressed because one or more lines are too long
|
|
@ -35,6 +35,10 @@ export class ChatView extends ItemView {
|
|||
private textarea: HTMLTextAreaElement | null = null;
|
||||
private sendButton: HTMLButtonElement | null = null;
|
||||
|
||||
/** True during initial message restore in onOpen() — suppress
|
||||
* saveMessages() calls to avoid N racing saveData() writes. */
|
||||
private _restoring: boolean = false;
|
||||
|
||||
/** Context captured on chat focus — avoids the timing bug where
|
||||
* activeEditor is null by the time handleSend() runs. */
|
||||
private capturedNote: {
|
||||
|
|
@ -176,13 +180,24 @@ export class ChatView extends ItemView {
|
|||
this.statusBar = container.createDiv("chat-status-bar");
|
||||
this.updateStatus(this.chatState.model, null);
|
||||
|
||||
// Restore saved messages from previous session
|
||||
// Restore saved messages from previous session.
|
||||
// Set _restoring flag so appendMessage skips fire-and-forget saves —
|
||||
// we do a single save at the end to avoid N racing saveData() calls.
|
||||
if (this.persistence) {
|
||||
const saved = this.persistence.loadMessages();
|
||||
if (saved.length > 0) {
|
||||
for (const msg of saved) {
|
||||
await this.appendMessage(msg.role, msg.content);
|
||||
this._restoring = true;
|
||||
try {
|
||||
for (const msg of saved) {
|
||||
await this.appendMessage(msg.role, msg.content);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("[Hermes] Failed to restore messages:", err);
|
||||
} finally {
|
||||
this._restoring = false;
|
||||
}
|
||||
// Single save to persist the restored state cleanly
|
||||
await this.persistence.saveMessages(this.chatState.messages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -228,8 +243,11 @@ export class ChatView extends ItemView {
|
|||
timestamp: Date.now(),
|
||||
};
|
||||
this.chatState.messages.push(msg);
|
||||
// Fire-and-forget save — don't block rendering
|
||||
this.persistence?.saveMessages(this.chatState.messages);
|
||||
// Fire-and-forget save — don't block rendering.
|
||||
// Suppressed during restore (too many racing saveData writes).
|
||||
if (!this._restoring) {
|
||||
this.persistence?.saveMessages(this.chatState.messages);
|
||||
}
|
||||
|
||||
const wrapper = this.messagesContainer.createDiv("chat-message");
|
||||
wrapper.addClass(`chat-message-${role}`);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue