fix: serialize state writes with retry for EBUSY errors
- Add write serialization to prevent concurrent writes - Retry with exponential backoff on Windows EBUSY - Use direct write instead of temp file + rename Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
58ef04fa68
commit
317c886529
1 changed files with 34 additions and 8 deletions
|
|
@ -8,6 +8,8 @@ export class StateStore {
|
|||
private dirty = new Set<string>()
|
||||
private persistTimer: NodeJS.Timeout | null = null
|
||||
private loaded = false
|
||||
private writeInProgress = false
|
||||
private pendingWrite = false
|
||||
|
||||
async load(): Promise<void> {
|
||||
if (this.loaded) {
|
||||
|
|
@ -57,24 +59,48 @@ export class StateStore {
|
|||
this.persistTimer = null
|
||||
|
||||
if (this.dirty.size === 0) {
|
||||
this.writeInProgress = false
|
||||
return
|
||||
}
|
||||
|
||||
if (this.writeInProgress) {
|
||||
this.pendingWrite = true
|
||||
return
|
||||
}
|
||||
|
||||
this.writeInProgress = true
|
||||
|
||||
// Write full state, not just dirty keys (atomicity)
|
||||
const toSave: Record<string, unknown> = {}
|
||||
for (const [key, value] of this.state.entries()) {
|
||||
toSave[key] = value
|
||||
}
|
||||
|
||||
try {
|
||||
// Atomic write: temp file + rename
|
||||
const tmpFile = `${STATE_FILE}.tmp`
|
||||
await fs.writeFile(tmpFile, JSON.stringify(toSave, null, 2))
|
||||
await fs.rename(tmpFile, STATE_FILE)
|
||||
this.dirty.clear()
|
||||
const retries = 3
|
||||
const backoffMs = [100, 300, 500]
|
||||
|
||||
for (let attempt = 0; attempt < retries; attempt++) {
|
||||
try {
|
||||
// Direct write without temp file (Windows EBUSY workaround)
|
||||
await fs.writeFile(STATE_FILE, JSON.stringify(toSave, null, 2))
|
||||
this.dirty.clear()
|
||||
break
|
||||
}
|
||||
catch (err) {
|
||||
const error = err as NodeJS.ErrnoException
|
||||
if (error.code === 'EBUSY' && attempt < retries - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, backoffMs[attempt]))
|
||||
continue
|
||||
}
|
||||
console.error('[StateStore] Failed to write state file:', err)
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
console.error('[StateStore] Failed to write state file:', err)
|
||||
|
||||
this.writeInProgress = false
|
||||
|
||||
if (this.pendingWrite) {
|
||||
this.pendingWrite = false
|
||||
await this.persist()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue