fix(lastleg): read committed order command replays after lock waits
This commit is contained in:
@@ -178,6 +178,12 @@ test('fresh immutable IDs drive two full cycles and preserve independent HOLD ac
|
||||
await verifyReceivedOrderHold(context)
|
||||
const proof=context.createdResources.phase4TailOrderControls.holdInventoryProof
|
||||
assert.equal(proof.status,'VERIFIED');assert.equal(proof.restored,true);assert.equal(order.version,8)
|
||||
assert.equal(proof.concurrentCommands.length,2)
|
||||
for(const command of proof.concurrentCommands) {
|
||||
assert.equal(command.copies,2)
|
||||
assert.deepEqual(command.replies[0],command.replies[1])
|
||||
assert.equal(command.replies[0].version,command.expectedVersion+1)
|
||||
}
|
||||
assert.equal(proof.snapshots.C2_RELEASED.positions.find(row=>row.id==='22').holdPackages,1)
|
||||
assert.equal(proof.snapshots.AFTER.balances[0].availablePackages,4)
|
||||
assert.equal(calls.filter(c=>c.path.endsWith('/manual')).length,2)
|
||||
@@ -185,6 +191,41 @@ test('fresh immutable IDs drive two full cycles and preserve independent HOLD ac
|
||||
assert.equal(calls.filter(c=>c.method==='POST'&&c.path.endsWith('/inventory/release')).length,1)
|
||||
})
|
||||
|
||||
test('hold duplicate requests are in flight together, with bounded concurrency of two',async()=>{
|
||||
const {context}=holdModel()
|
||||
const originalRequest=context.request
|
||||
let active=0,peak=0
|
||||
context.request=async(method,path,...args)=>{
|
||||
if(method!=='PUT'||!path.includes('/orders/')) return originalRequest(method,path,...args)
|
||||
active++;peak=Math.max(peak,active)
|
||||
try { await new Promise(resolve=>setImmediate(resolve));return await originalRequest(method,path,...args) }
|
||||
finally {active--}
|
||||
}
|
||||
await verifyReceivedOrderHold(context)
|
||||
assert.equal(peak,2)
|
||||
assert.equal(active,0)
|
||||
})
|
||||
|
||||
test('ambiguous parallel hold waits for its sibling and never starts recovery writes',async()=>{
|
||||
const {context,calls}=holdModel()
|
||||
const originalRequest=context.request
|
||||
let holdRequests=0,siblingSettled=false
|
||||
context.request=async(method,path,body,...args)=>{
|
||||
if(method==='PUT' && body?.held && ++holdRequests<=2) {
|
||||
if(holdRequests===1) throw new Error('lost response')
|
||||
await new Promise(resolve=>setImmediate(resolve))
|
||||
const response=await originalRequest(method,path,body,...args)
|
||||
siblingSettled=true
|
||||
return response
|
||||
}
|
||||
return originalRequest(method,path,body,...args)
|
||||
}
|
||||
await assert.rejects(verifyReceivedOrderHold(context),/lost response/)
|
||||
assert.equal(siblingSettled,true)
|
||||
assert.equal(context.createdResources.phase4TailOrderControls.holdInventoryProof.uncertainWrite,true)
|
||||
assert.ok(!calls.some(call=>call.path.endsWith('/inventory/release')||call.body?.held===false))
|
||||
})
|
||||
|
||||
for(const fault of ['stale marker','no evidence','wrong immutable customer','missing position','unsafe bucket','not enough stock','moved source']) {
|
||||
test(`missing or unsafe fresh hold prerequisite blocks all writes: ${fault}`,async()=>{
|
||||
const {context,calls}=holdModel(fault)
|
||||
|
||||
Reference in New Issue
Block a user