23 lines
3.8 KiB
PHP
23 lines
3.8 KiB
PHP
<?php
|
|
require __DIR__.'/sgf-stock-final-proof-bootstrap.php';
|
|
$mode=$argv[1]??'';$directory=$s['root'].'/stock-final-concurrency-20261110';
|
|
if(!is_dir($directory))mkdir($directory,0700,true);$progress=$directory.'/progress.json';
|
|
if($mode==='prepare'){
|
|
if(is_file($progress))throw new LogicException('Reuse the existing concurrency evidence; do not dispatch twice');
|
|
$data=['stock_item_id'=>$policy['transfer_item_id'],'source_warehouse_id'=>$policy['source_warehouse_id'],'destination_warehouse_id'=>$policy['destination_warehouse_id'],'transfer_key'=>(string)Str::uuid(),'quantity'=>'1.000'];[$sent]=$command('warehouse','dispatch',$data);
|
|
$state=$invoke('workshop_verify','index');$cuts=[];foreach([1,2] as $i)$cuts[$i]=$invoke('workshop_verify','preview',['action'=>'receive','data'=>array_replace($data,['quantity'=>'0.600']),'operation_key'=>(string)Str::uuid(),'revision'=>$state['revision'],'reason'=>'Duas recepções concorrentes da mesma parcela por receber.'])['preview'];
|
|
file_put_contents($progress,json_encode(['data'=>$data,'sent'=>$sent,'cuts'=>$cuts],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'prepared'=>true]).PHP_EOL;
|
|
}elseif($mode==='first'){
|
|
$p=json_decode(file_get_contents($progress),true);$host->beginTransaction();$native->beginTransaction();
|
|
try{$result=$invoke('workshop_verify','confirm',['preview'=>$p['cuts'][1]]);file_put_contents($directory.'/locked.json',json_encode(['locks'=>'host QA reviewer row then native tenant row','effect_not_yet_committed'=>true]));$end=microtime(true)+20;while(!is_file($directory.'/release')&µtime(true)<$end)usleep(50000);if(!is_file($directory.'/release'))throw new RuntimeException('Test release signal absent');$native->commit();$host->commit();file_put_contents($directory.'/first.json',json_encode(['passed'=>true,'result'=>$result]));}
|
|
finally{while($native->transactionLevel()>0)$native->rollBack();while($host->transactionLevel()>0)$host->rollBack();}
|
|
}elseif($mode==='second'){
|
|
$p=json_decode(file_get_contents($progress),true);$start=microtime(true);$result=$invoke('workshop_verify','confirm',['preview'=>$p['cuts'][2]]);file_put_contents($directory.'/second.json',json_encode(['result'=>$result,'elapsed_seconds'=>microtime(true)-$start]));
|
|
}elseif($mode==='recover'){
|
|
$p=json_decode(file_get_contents($progress),true);$bad=false;try{$invoke('workshop_verify','confirm',['preview'=>$p['cuts'][2]]);}catch(Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e){$bad=$e->getStatusCode()===409;}
|
|
if(!$bad)throw new RuntimeException('Stale conference must refuse after lock release');$first=$invoke('workshop_verify','confirm',['preview'=>$p['cuts'][1]]);if(!$first['replayed'])throw new RuntimeException('First receipt duplicated');
|
|
[$last]=$command('workshop_verify','receive',array_replace($p['data'],['quantity'=>'0.400']));$events=$native->table('sgf_stock_transit_events')->where($f['scope'])->where('transfer_key',$p['data']['transfer_key'])->get();
|
|
if($events->count()!==3||(int)$events->where('action','receive')->sum('quantity_milli')!==1000||$last['impact']['in_transit_after_milli']!==0)throw new RuntimeException('Concurrent receipt exceeded or duplicated dispatch');
|
|
file_put_contents($directory.'/result.json',json_encode(['passed'=>true,'lock'=>'host reviewer users row, before native tenant/company writer lock','blocked_second'=>json_decode(file_get_contents($directory.'/second.json'),true),'after_release'=>'409 stale version; fresh conference receives remaining400milli','events'=>3,'received_milli'=>1000,'duplicate_movements'=>0,'same_first_key_replayed'=>true,'real_access'=>false,'new_databases'=>0],JSON_PRETTY_PRINT));echo json_encode(['passed'=>true,'proof'=>$directory.'/result.json']).PHP_EOL;
|
|
}else throw new LogicException('Known directed concurrency phase required');
|