{"record":{"id":"50bb195283122990","repo":"linshenkx/prompt-optimizer","slug":"failed-to-perform-atomic-update-after-maxretries","errorCode":null,"errorMessage":"Failed to perform atomic update after ${maxRetries} attempts","messagePattern":"Failed to perform atomic update after (.+?) attempts","errorType":"exception","errorClass":"StorageError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/storage/dexieStorageProvider.ts","lineNumber":239,"sourceCode":"        // 如果是最后一次尝试或非事务错误，尝试降级到简单更新\n        if (attempt === maxRetries) {\n          console.warn(`All retries failed; falling back to simple update (${key})`);\n          try {\n            await this._performSimpleUpdate(key, updateFn);\n            console.log(`Fallback update succeeded (${key})`);\n            return;\n          } catch (fallbackError) {\n            console.error(`Fallback update also failed (${key}):`, fallbackError);\n            throw lastError; // 抛出原始错误\n          }\n        }\n      }\n    }\n\n    if (lastError) {\n      throw lastError\n    }\n    throw new StorageError(`Failed to perform atomic update after ${maxRetries} attempts`, 'write')\n  }\n\n  /**\n   * 简单更新（降级方案）\n   */\n  private async _performSimpleUpdate<T>(\n    key: string,\n    updateFn: (currentValue: T | null) => T\n  ): Promise<void> {\n    try {\n      // 读取当前值\n      const currentRecord = await this.db.storage.get(key);\n      const currentValue = currentRecord?.value\n        ? JSON.parse(currentRecord.value) as T\n        : null;\n\n      // 应用更新函数\n      const newValue = updateFn(currentValue);","sourceCodeStart":221,"sourceCodeEnd":257,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/storage/dexieStorageProvider.ts#L221-L257","documentation":"Terminal StorageError thrown by _performAtomicUpdateWithRetry after all retry attempts of the transactional update failed (it rethrows the lastError if one exists; this message only appears when no lastError was captured). The update path uses Dexie transactions with a lock and retries, degrading to _performSimpleUpdate on certain failures.","triggerScenarios":"Repeated transaction failures — e.g., persistent PrematureCommitError, lock contention where the promise-based lock keeps failing, or IndexedDB instability — exhausting maxRetries in update().","commonSituations":"High-frequency concurrent updates to the same key (counters, settings) in a multi-tab app; performing Dexie operations inside the transaction that trigger premature commits; browser storage corruption or quota pressure.","solutions":["Reduce concurrent writers: serialize updates per key or lower update frequency","Avoid doing async non-Dexie work inside the update function passed to update() (causes PrematureCommitError)","Catch this error and fall back to a non-transactional write, or surface it for a user retry"],"exampleFix":"// before\nawait provider.update('counter', fn);\n\n// after\ntry { await provider.update('counter', fn); }\ncatch (e) {\n  if (e instanceof StorageError) {\n    await provider.setItem('counter', await readAndBump(provider)); // manual fallback\n  } else throw e;\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"const isAtomicUpdateExhausted = (e: unknown): e is StorageError =>\n  e instanceof StorageError && /after \\d+ attempts/.test(String((e as Error).message));","tryCatchPattern":"try { await provider.update(key, fn); } catch (e) { if (isAtomicUpdateExhausted(e)) { await sleep(250); await provider.update(key, fn); /* single retry with backoff */ } else throw e; }","preventionTips":["Serialize writes per key (queue/mutex) instead of racing concurrent update() calls","Keep update callbacks free of non-Dexie async work","Use exponential backoff if you must retry; more hammering worsens contention"],"tags":["storage","dexie","transactions","concurrency","retry"],"backgroundTag":"transaction-retry-exhausted","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}