47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
|
|
/**
|
||
|
|
* @module transaction/RevisionConflictError
|
||
|
|
* @description Error thrown by `brain.update({ ifRev })` when the persisted entity's
|
||
|
|
* `_rev` no longer matches the caller-supplied expected revision. Carries enough
|
||
|
|
* context (entity id, expected rev, actual rev) for the caller to choose a recovery
|
||
|
|
* strategy: refetch + retry, escalate to the user, or surface the conflict.
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Optimistic-concurrency conflict on `brain.update({ id, ..., ifRev })`.
|
||
|
|
*
|
||
|
|
* Thrown when the persisted entity's `_rev` differs from the caller-supplied `ifRev`.
|
||
|
|
* The standard recovery is read-modify-write: `await brain.get(id)` → reconcile →
|
||
|
|
* retry with the new `_rev`.
|
||
|
|
*
|
||
|
|
* @example
|
||
|
|
* try {
|
||
|
|
* await brain.update({ id, data: '...', ifRev: 5 })
|
||
|
|
* } catch (err) {
|
||
|
|
* if (err instanceof RevisionConflictError) {
|
||
|
|
* // Refetch and retry with the latest rev
|
||
|
|
* const latest = await brain.get(err.id)
|
||
|
|
* await brain.update({ id, data: merge(latest.data, '...'), ifRev: err.actual })
|
||
|
|
* }
|
||
|
|
* }
|
||
|
|
*/
|
||
|
|
export class RevisionConflictError extends Error {
|
||
|
|
/** Entity ID whose revision check failed */
|
||
|
|
readonly id: string
|
||
|
|
/** Revision number the caller expected to see (the `ifRev` argument) */
|
||
|
|
readonly expected: number
|
||
|
|
/** Revision number actually persisted */
|
||
|
|
readonly actual: number
|
||
|
|
|
||
|
|
constructor(id: string, expected: number, actual: number) {
|
||
|
|
super(
|
||
|
|
`update({ id: ${JSON.stringify(id)}, ifRev: ${expected} }) failed: persisted _rev is ${actual}. ` +
|
||
|
|
`The entity was modified by another writer since you read it. ` +
|
||
|
|
`Refetch with brain.get(${JSON.stringify(id)}) and retry with the latest _rev.`
|
||
|
|
)
|
||
|
|
this.name = 'RevisionConflictError'
|
||
|
|
this.id = id
|
||
|
|
this.expected = expected
|
||
|
|
this.actual = actual
|
||
|
|
}
|
||
|
|
}
|