Files
SGF/scripts/stock-period-concurrency.php
2026-11-04 09:37:20 +02:00

17 lines
3.0 KiB
PHP

<?php
use Illuminate\Support\Facades\DB;
use Tempo\Finance\GeneralLedger\StockValuation;
$output=$s['root'].'/period-concurrency.json';if(file_exists($output))throw new LogicException('Completed concurrency proof preserved');
$proof=json_decode(file_get_contents($s['root'].'/period-batches.json'),true,64,JSON_THROW_ON_ERROR);if(!$proof['passed']||!preg_match('/^period_[a-f0-9]{12}$/D',$s['period_schema']))throw new LogicException('Completed metadata fixture required');
$db=DB::connection();$schema=$s['period_schema'];$db->statement('SET search_path TO '.$schema);config(['database.connections.stock_period_writer'=>$db->getConfig()]);$writer=DB::connection('stock_period_writer');$writer->statement('SET search_path TO '.$schema);$writer->statement("SET lock_timeout='2s'");$writer->statement("SET statement_timeout='5s'");
$checks=[];$ok=function($v,$name)use(&$checks){if(!$v)throw new RuntimeException($name);$checks[]=$name;};$scope=['tenant_id'=>1,'company_id'=>0,'unit_id'=>0];$provider=new StockValuation($db,$scope,1,'period-metadata',fn()=>null,fn()=>null);$period=['period_key'=>'QA-PERIOD','starts_on'=>'2026-10-01','ends_on'=>'2026-10-31'];$original=$db->table('gl_journals')->where('id',205)->value('snapshot');$armed=false;$commits=0;
try{
$before=$provider->period($period);$ok($before['facts']===205&&$before['pending']===0,'metadata period is complete before concurrent change');
DB::listen(function($q)use($db,$writer,&$armed,&$commits){if($armed&&$q->connection===$db&&str_starts_with($q->sql,'select DISTINCT ON')){$armed=false;$writer->transaction(function()use($writer){$writer->update("UPDATE gl_journals SET snapshot=jsonb_set(snapshot,'{source,stock_fact_revision}','null'::jsonb) WHERE id=205");});$commits++;}});
$armed=true;$during=$provider->period($period);$ok($commits===1&&$during===$before,'writer commits between batches without mixing snapshots in reader');
$after=$provider->period($period);$ok($after['facts']===205&&$after['pending']===1&&$after['hash']===$before['hash'],'next period read sees newly incomplete journal and refuses completeness');
$ok($db->transactionLevel()===0&&$writer->transactionLevel()===0,'both independent connections end their owned transactions');
}finally{$armed=false;$writer->update('UPDATE gl_journals SET snapshot=?::jsonb WHERE id=205',[$original]);$writer->statement('SET search_path TO public');}
$restored=$provider->period($period);$ok($restored===$before,'only metadata mutation is restored after concurrent test');$ok((int)$db->selectOne("SELECT count(*) n FROM pg_cursors WHERE name LIKE 'stock_prefix_%'")->n===0,'period concurrency leaves no prefix cursor open');$db->statement('SET search_path TO public');
file_put_contents($output,json_encode(['passed'=>true,'checks'=>$checks,'metadata_fixture_only'=>true,'concurrent_writer_commits'=>1,'after_change_pending'=>$after['pending'],'restored'=>true,'new_financial_effects'=>0,'deployment_capacity_validated'=>false],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo count($checks)." period concurrency checks passed.\n";