From f920991015845eaa1a1bc85e9bec333813955b11 Mon Sep 17 00:00:00 2001 From: Lars Gebhardt-Kusche Date: Sat, 2 May 2026 01:38:39 +0200 Subject: [PATCH] dsfsdf --- modules/mining-checker/src/Api/Router.php | 50 ++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/modules/mining-checker/src/Api/Router.php b/modules/mining-checker/src/Api/Router.php index a3a0d6c..3bc0535 100644 --- a/modules/mining-checker/src/Api/Router.php +++ b/modules/mining-checker/src/Api/Router.php @@ -19,6 +19,10 @@ use PDO; final class Router { + private const BOOTSTRAP_MEASUREMENT_LIMIT = 150; + private const BOOTSTRAP_SNAPSHOT_LIMIT = 40; + private const LONG_REQUEST_BUDGET_SECONDS = 8.0; + private string $moduleBasePath; private ModuleConfig $config; private ?PDO $pdo = null; @@ -61,6 +65,7 @@ final class Router public function handle(string $relativePath): never { try { + $this->configureRuntimeGuards(); $this->releaseSessionLock(); $method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET')); $path = trim($relativePath, '/'); @@ -315,10 +320,10 @@ final class Router 'purchased_miners' => [], 'measurement_rates' => [], ]); - $measurements = $this->safeRead(fn () => $this->measurements($projectKey, $settings, false), []); + $measurements = $this->safeRead(fn () => $this->bootstrapMeasurements($projectKey, $settings), []); $targets = $this->safeRead(fn () => $this->targets($projectKey), []); $dashboards = $this->safeRead(fn () => $this->dashboards($projectKey), []); - $fxSnapshots = $this->safeRead(fn () => $this->measurementFxSnapshots($measurements), []); + $fxSnapshots = $this->safeRead(fn () => $this->measurementFxSnapshots($measurements, self::BOOTSTRAP_SNAPSHOT_LIMIT), []); $summary = $this->safeRead(fn () => $this->analytics()->buildSummary($measurements, $settings, $targets), [ 'latest_measurement' => $measurements !== [] ? $measurements[array_key_last($measurements)] : null, 'baseline' => $settings, @@ -326,6 +331,7 @@ final class Router 'payouts' => [], 'miner_offers' => [], ]); + $measurementCount = is_array($measurements) ? count($measurements) : 0; return [ 'project' => $this->repository()->getProject($projectKey), @@ -336,7 +342,10 @@ final class Router 'fx_snapshots' => $fxSnapshots, 'summary' => $summary, 'bootstrap_meta' => [ - 'degraded' => false, + 'degraded' => $measurementCount >= self::BOOTSTRAP_MEASUREMENT_LIMIT, + 'measurement_limit' => self::BOOTSTRAP_MEASUREMENT_LIMIT, + 'snapshot_limit' => self::BOOTSTRAP_SNAPSHOT_LIMIT, + 'measurement_count' => $measurementCount, 'duration_ms' => round((microtime(true) - $startedAt) * 1000, 2), ], ]; @@ -773,6 +782,7 @@ final class Router throw new ApiException('Das Modul fx-rates ist nicht verfuegbar.', 422); } + $startedAt = microtime(true); module_fn('fx-rates', 'ensure_schema'); $legacyRows = $this->repository()->listAllFxRates(); $legacyFetches = $this->groupLegacyFxFetches($legacyRows); @@ -784,6 +794,7 @@ final class Router $migratedRates = 0; foreach ($legacyFetches as $legacyFetchId => $legacyFetch) { + $this->assertRequestWithinBudget($startedAt, 'Legacy-FX-Migration dauert zu lange.'); $existing = $this->findExistingFxFetchForLegacy($fxRepository, $legacyFetch); if (is_array($existing) && !empty($existing['id'])) { $fetchIdMap[$legacyFetchId] = (int) $existing['id']; @@ -817,6 +828,7 @@ final class Router $unresolvedMeasurements = 0; foreach ($measurements as $measurement) { + $this->assertRequestWithinBudget($startedAt, 'Legacy-FX-Migration dauert zu lange.'); $measurementId = is_numeric($measurement['id'] ?? null) ? (int) $measurement['id'] : 0; if ($measurementId <= 0) { continue; @@ -1133,6 +1145,12 @@ final class Router return $this->analytics()->enrichMeasurements($rows, $settings); } + private function bootstrapMeasurements(string $projectKey, array $settings): array + { + $rows = $this->repository()->listMeasurements($projectKey, self::BOOTSTRAP_MEASUREMENT_LIMIT); + return $this->analytics()->enrichMeasurements($rows, $settings); + } + private function createMeasurement(string $projectKey, array $input): array { $projectTimezone = $this->projectTimezone($projectKey); @@ -1981,10 +1999,15 @@ final class Router return $resolved; } - private function measurementFxSnapshots(array $measurements): array + private function measurementFxSnapshots(array $measurements, ?int $limit = null): array { $snapshots = []; - foreach ($measurements as $measurement) { + $measurementPool = $measurements; + if ($limit !== null && $limit > 0 && count($measurementPool) > $limit) { + $measurementPool = array_slice($measurementPool, -$limit); + } + + foreach ($measurementPool as $measurement) { $fetchId = is_numeric($measurement['fx_fetch_id'] ?? null) ? (int) $measurement['fx_fetch_id'] : 0; if ($fetchId <= 0 || isset($snapshots[$fetchId])) { continue; @@ -2114,6 +2137,16 @@ final class Router Http::json($payload, $statusCode); } + private function configureRuntimeGuards(): void + { + if (function_exists('ignore_user_abort')) { + @ignore_user_abort(false); + } + if (function_exists('set_time_limit')) { + @set_time_limit(15); + } + } + private function releaseSessionLock(): void { if (PHP_SAPI === 'cli') { @@ -2125,6 +2158,13 @@ final class Router } } + private function assertRequestWithinBudget(float $startedAt, string $message): void + { + if ((microtime(true) - $startedAt) > self::LONG_REQUEST_BUDGET_SECONDS) { + throw new ApiException($message, 503, ['timeout' => true]); + } + } + private function debugLatest(): array { $filePath = DebugState::latestFilePath();