assertMatchesRegularExpression('/^[a-f0-9]{12}$/D', $run); $file = realpath((string) getenv('SGF_ASSOCIATION_WORKER_INPUT')); $private = realpath(base_path('../private')); if (!$file || !$private || !str_starts_with($file, $private.DIRECTORY_SEPARATOR)) throw new \RuntimeException('Unexpected private input'); $p = json_decode(file_get_contents($file), true, 512, JSON_THROW_ON_ERROR); $identity = DB::selectOne("SELECT current_database() AS database,current_user AS role,inet_server_port() AS port,current_setting('cluster_name') AS cluster"); $this->assertSame('sgf_tempo_test', $identity->database); $this->assertSame('sgf_test_runner', $identity->role); $this->assertSame('sgf_modular_regression_'.$run, $identity->cluster); $this->assertSame(55439, (int) $identity->port); DB::select("SELECT set_config('application_name',?,false)", [$p['application_name']]); $this->withoutExceptionHandling(); $user = User::findOrFail($p['actor']); $token = $p['context'] ?? bin2hex(random_bytes(24)); $this->actingAs($user)->withSession(['auth_version' => $user->auth_version, 'company_context' => [ 'tenant_id' => $p['tenant'], 'company_id' => $p['company'], 'unit_id' => $p['unit'], 'module_id' => null, 'token' => $token, ]])->withHeader('X-SGF-Context', $token); $result = ['mode' => $p['mode'], 'requests' => []]; if ($p['mode'] === 'read') { DB::beginTransaction(); DB::statement('SET TRANSACTION READ ONLY'); DB::statement("SET LOCAL statement_timeout = '3s'"); try { $this->getJson($p['url'])->assertOk(); $this->barrier($p); $metrics = null; DB::listen(function ($q) use (&$metrics) { if ($metrics === null) return; if (!preg_match('/^\s*(select|with)\b/i', $q->sql)) throw new \RuntimeException('READ_WORKER_WROTE_SQL'); $metrics['queries']++; $metrics['database_ms'] += $q->time; }); for ($i = 0; $i < $p['requests']; $i++) { gc_collect_cycles(); memory_reset_peak_usage(); $memory = memory_get_usage(false); $metrics = ['queries' => 0, 'database_ms' => 0.0]; $start = hrtime(true); $wall = microtime(true); try { $r = $this->getJson($p['url']); $elapsed = (hrtime(true) - $start) / 1e6; $finished = microtime(true); $stats = $metrics; $metrics = null; $peak = memory_get_peak_usage(false); $r->assertOk()->assertJsonPath('pagination.total', $p['total']); $this->assertSame($p['response_sha256'], hash('sha256', $r->getContent())); $result['requests'][] = ['status' => 'ok', 'elapsed_ms' => round($elapsed, 3), 'started_at' => $wall, 'finished_at' => $finished, 'queries' => $stats['queries'], 'database_ms' => round($stats['database_ms'], 3), 'incremental_php_bytes' => max(0, $peak - $memory), 'peak_php_bytes' => $peak]; } catch (\Illuminate\Database\QueryException $e) { $metrics = null; $result['requests'][] = ['status' => 'sql_timeout_or_error', 'sqlstate' => $e->errorInfo[0] ?? $e->getCode(), 'elapsed_ms' => round((hrtime(true) - $start) / 1e6, 3)]; break; } } } finally { DB::rollBack(); } } elseif (in_array($p['mode'], ['confirm', 'confirm_http', 'preview_http'], true)) { app(CompanyContext::class)->selectFor($p['actor'], $p['tenant'], $p['company'], $p['unit']); $timeout = (int) ($p['lock_timeout_ms'] ?? 1000); if ($timeout < 100 || $timeout > 10000) throw new \RuntimeException('Unexpected diagnostic lock timeout'); DB::select("SELECT set_config('lock_timeout',?,false)", [$timeout.'ms']); DB::select("SELECT set_config('statement_timeout',?,false)", [isset($p['lock_timeout_ms']) ? '12s' : '5s']); $this->barrier($p); $start = hrtime(true); try { if ($p['mode'] !== 'confirm') { $this->withExceptionHandling(); $response = $this->postJson($p['mode'] === 'preview_http' ? '/api/module-records/preview' : '/api/module-records', $p['mode'] === 'preview_http' ? $p['preview_input'] : ['review_token' => $p['review_token']]); $response->assertStatus(423)->assertExactJson(['code' => 'association_busy', 'message' => 'A conferência está ocupada por outra operação. Aguarde e volte a confirmar; o estado será novamente validado.']); $this->assertSame('no-store, private', $response->headers->get('Cache-Control')); $result['confirmation'] = ['status' => 'http_busy', 'http_status' => $response->status(), 'code' => $response->json('code'), 'body_keys' => array_keys($response->json()), 'elapsed_ms' => round((hrtime(true) - $start) / 1e6, 3)]; } else { $r = Request::create('/api/module-records', 'POST', ['review_token' => $p['review_token']]); $r->headers->set('X-SGF-Context', $token); $result['confirmation'] = ['status' => ($p['expected_result'] ?? false) ? 'completed' : 'unexpected_success', 'result' => app(ModuleAssociationReview::class)->confirm($r), 'elapsed_ms' => round((hrtime(true) - $start) / 1e6, 3)]; } } catch (\Symfony\Component\HttpKernel\Exception\HttpExceptionInterface $e) { $result['confirmation'] = ['status' => 'refused', 'http_status' => $e->getStatusCode(), 'elapsed_ms' => round((hrtime(true) - $start) / 1e6, 3)]; } catch (\Illuminate\Database\QueryException $e) { $result['confirmation'] = ['status' => 'database_error', 'sqlstate' => $e->errorInfo[0] ?? $e->getCode(), 'elapsed_ms' => round((hrtime(true) - $start) / 1e6, 3)]; } } else { throw new \RuntimeException('Unknown worker mode'); } $result['finished_at'] = microtime(true); file_put_contents($p['result'], json_encode($result, JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR)); } private function barrier(array $p): void { file_put_contents($p['ready'], 'ready'); $deadline = microtime(true) + 45; while (!is_file($p['go'])) { if (microtime(true) > $deadline) throw new \RuntimeException('Worker barrier timed out'); usleep(5000); } } }