yssd
This commit is contained in:
@@ -69,6 +69,10 @@ final class Router
|
||||
$this->releaseSessionLock();
|
||||
$method = strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'GET'));
|
||||
$path = trim($relativePath, '/');
|
||||
$this->debug->add('router.handle.start', [
|
||||
'path' => $path,
|
||||
'method' => $method,
|
||||
]);
|
||||
|
||||
if ($path === 'v1/health') {
|
||||
$this->respond(['ok' => true, 'module' => 'mining-checker']);
|
||||
@@ -285,11 +289,20 @@ final class Router
|
||||
|
||||
throw new ApiException('Ressource nicht gefunden.', 404, ['resource' => $resource, 'method' => $method]);
|
||||
} catch (ApiException $exception) {
|
||||
$this->debug->add('router.handle.api_exception', [
|
||||
'status' => $exception->statusCode(),
|
||||
'message' => $exception->getMessage(),
|
||||
'context' => $exception->context(),
|
||||
]);
|
||||
Http::json([
|
||||
'error' => $exception->getMessage(),
|
||||
'context' => $exception->context(),
|
||||
], $exception->statusCode());
|
||||
} catch (\Throwable $exception) {
|
||||
$this->debug->add('router.handle.error', [
|
||||
'type' => get_debug_type($exception),
|
||||
'message' => $exception->getMessage(),
|
||||
]);
|
||||
Http::json([
|
||||
'error' => 'Unerwarteter Mining-Checker Fehler.',
|
||||
'context' => ['message' => $exception->getMessage()],
|
||||
@@ -300,7 +313,13 @@ final class Router
|
||||
private function bootstrap(string $projectKey): array
|
||||
{
|
||||
$startedAt = microtime(true);
|
||||
$settings = $this->safeRead(fn () => $this->settings($projectKey), [
|
||||
$this->debug->add('bootstrap.start', [
|
||||
'project_key' => $projectKey,
|
||||
'measurement_limit' => self::BOOTSTRAP_MEASUREMENT_LIMIT,
|
||||
'snapshot_limit' => self::BOOTSTRAP_SNAPSHOT_LIMIT,
|
||||
]);
|
||||
|
||||
$settings = $this->safeTimed('bootstrap.settings', fn () => $this->settings($projectKey), [
|
||||
'project_key' => $projectKey,
|
||||
'baseline_measured_at' => null,
|
||||
'baseline_coins_total' => null,
|
||||
@@ -319,19 +338,38 @@ final class Router
|
||||
'miner_offers' => [],
|
||||
'purchased_miners' => [],
|
||||
'measurement_rates' => [],
|
||||
], ['project_key' => $projectKey]);
|
||||
$measurements = $this->safeTimed('bootstrap.measurements', fn () => $this->bootstrapMeasurements($projectKey, $settings), [], [
|
||||
'project_key' => $projectKey,
|
||||
]);
|
||||
$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, self::BOOTSTRAP_SNAPSHOT_LIMIT), []);
|
||||
$summary = $this->safeRead(fn () => $this->analytics()->buildSummary($measurements, $settings, $targets), [
|
||||
$targets = $this->safeTimed('bootstrap.targets', fn () => $this->targets($projectKey), [], [
|
||||
'project_key' => $projectKey,
|
||||
]);
|
||||
$dashboards = $this->safeTimed('bootstrap.dashboards', fn () => $this->dashboards($projectKey), [], [
|
||||
'project_key' => $projectKey,
|
||||
]);
|
||||
$fxSnapshots = $this->safeTimed('bootstrap.fx_snapshots', fn () => $this->measurementFxSnapshots($measurements, self::BOOTSTRAP_SNAPSHOT_LIMIT), [], [
|
||||
'measurement_count' => is_array($measurements) ? count($measurements) : 0,
|
||||
]);
|
||||
$summary = $this->safeTimed('bootstrap.summary', fn () => $this->analytics()->buildSummary($measurements, $settings, $targets), [
|
||||
'latest_measurement' => $measurements !== [] ? $measurements[array_key_last($measurements)] : null,
|
||||
'baseline' => $settings,
|
||||
'targets' => [],
|
||||
'payouts' => [],
|
||||
'miner_offers' => [],
|
||||
], [
|
||||
'measurement_count' => is_array($measurements) ? count($measurements) : 0,
|
||||
'target_count' => is_array($targets) ? count($targets) : 0,
|
||||
]);
|
||||
$measurementCount = is_array($measurements) ? count($measurements) : 0;
|
||||
$this->debug->add('bootstrap.end', [
|
||||
'project_key' => $projectKey,
|
||||
'duration_ms' => round((microtime(true) - $startedAt) * 1000, 2),
|
||||
'measurement_count' => $measurementCount,
|
||||
'target_count' => is_array($targets) ? count($targets) : 0,
|
||||
'dashboard_count' => is_array($dashboards) ? count($dashboards) : 0,
|
||||
'snapshot_count' => is_array($fxSnapshots) ? count($fxSnapshots) : 0,
|
||||
]);
|
||||
|
||||
return [
|
||||
'project' => $this->repository()->getProject($projectKey),
|
||||
@@ -783,22 +821,39 @@ final class Router
|
||||
}
|
||||
|
||||
$startedAt = microtime(true);
|
||||
$this->debug->add('legacy_fx_migrate.start', [
|
||||
'project_key' => $projectKey,
|
||||
]);
|
||||
module_fn('fx-rates', 'ensure_schema');
|
||||
$legacyRows = $this->repository()->listAllFxRates();
|
||||
$legacyFetches = $this->groupLegacyFxFetches($legacyRows);
|
||||
$fxRepository = module_fn('fx-rates', 'repository');
|
||||
$this->debug->add('legacy_fx_migrate.loaded', [
|
||||
'legacy_rows' => count($legacyRows),
|
||||
'legacy_fetches' => count($legacyFetches),
|
||||
]);
|
||||
|
||||
$fetchIdMap = [];
|
||||
$importedFetches = 0;
|
||||
$reusedFetches = 0;
|
||||
$migratedRates = 0;
|
||||
|
||||
$legacyFetchIndex = 0;
|
||||
foreach ($legacyFetches as $legacyFetchId => $legacyFetch) {
|
||||
$this->assertRequestWithinBudget($startedAt, 'Legacy-FX-Migration dauert zu lange.');
|
||||
$legacyFetchIndex++;
|
||||
$existing = $this->findExistingFxFetchForLegacy($fxRepository, $legacyFetch);
|
||||
if (is_array($existing) && !empty($existing['id'])) {
|
||||
$fetchIdMap[$legacyFetchId] = (int) $existing['id'];
|
||||
$reusedFetches++;
|
||||
if ($legacyFetchIndex % 50 === 0) {
|
||||
$this->debug->add('legacy_fx_migrate.fetch_progress', [
|
||||
'processed' => $legacyFetchIndex,
|
||||
'total' => count($legacyFetches),
|
||||
'reused_fetches' => $reusedFetches,
|
||||
'imported_fetches' => $importedFetches,
|
||||
]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -816,19 +871,33 @@ final class Router
|
||||
}
|
||||
$importedFetches++;
|
||||
$migratedRates += count($saved['rates'] ?? []);
|
||||
if ($legacyFetchIndex % 50 === 0) {
|
||||
$this->debug->add('legacy_fx_migrate.fetch_progress', [
|
||||
'processed' => $legacyFetchIndex,
|
||||
'total' => count($legacyFetches),
|
||||
'reused_fetches' => $reusedFetches,
|
||||
'imported_fetches' => $importedFetches,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$measurements = $this->repository()->listAllMeasurements($projectKey);
|
||||
$measurementRatesById = $this->groupMeasurementRatesByMeasurementId(
|
||||
$this->repository()->listMeasurementRates($projectKey)
|
||||
);
|
||||
$this->debug->add('legacy_fx_migrate.measurements_loaded', [
|
||||
'measurement_count' => count($measurements),
|
||||
'measurement_rate_groups' => count($measurementRatesById),
|
||||
]);
|
||||
|
||||
$updatedMeasurements = 0;
|
||||
$reusedMeasurements = 0;
|
||||
$unresolvedMeasurements = 0;
|
||||
|
||||
$measurementIndex = 0;
|
||||
foreach ($measurements as $measurement) {
|
||||
$this->assertRequestWithinBudget($startedAt, 'Legacy-FX-Migration dauert zu lange.');
|
||||
$measurementIndex++;
|
||||
$measurementId = is_numeric($measurement['id'] ?? null) ? (int) $measurement['id'] : 0;
|
||||
if ($measurementId <= 0) {
|
||||
continue;
|
||||
@@ -837,6 +906,15 @@ final class Router
|
||||
$currentFetchId = is_numeric($measurement['fx_fetch_id'] ?? null) ? (int) $measurement['fx_fetch_id'] : 0;
|
||||
if ($currentFetchId > 0 && $this->fx()->snapshotByFetchId($currentFetchId, null, null) !== null) {
|
||||
$reusedMeasurements++;
|
||||
if ($measurementIndex % 100 === 0) {
|
||||
$this->debug->add('legacy_fx_migrate.measurement_progress', [
|
||||
'processed' => $measurementIndex,
|
||||
'total' => count($measurements),
|
||||
'updated' => $updatedMeasurements,
|
||||
'reused' => $reusedMeasurements,
|
||||
'unresolved' => $unresolvedMeasurements,
|
||||
]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -849,13 +927,44 @@ final class Router
|
||||
|
||||
if (!is_numeric($resolvedFetchId) || (int) $resolvedFetchId <= 0) {
|
||||
$unresolvedMeasurements++;
|
||||
if ($measurementIndex % 100 === 0) {
|
||||
$this->debug->add('legacy_fx_migrate.measurement_progress', [
|
||||
'processed' => $measurementIndex,
|
||||
'total' => count($measurements),
|
||||
'updated' => $updatedMeasurements,
|
||||
'reused' => $reusedMeasurements,
|
||||
'unresolved' => $unresolvedMeasurements,
|
||||
]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->repository()->setMeasurementFxFetchId($projectKey, $measurementId, (int) $resolvedFetchId);
|
||||
$updatedMeasurements++;
|
||||
if ($measurementIndex % 100 === 0) {
|
||||
$this->debug->add('legacy_fx_migrate.measurement_progress', [
|
||||
'processed' => $measurementIndex,
|
||||
'total' => count($measurements),
|
||||
'updated' => $updatedMeasurements,
|
||||
'reused' => $reusedMeasurements,
|
||||
'unresolved' => $unresolvedMeasurements,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->debug->add('legacy_fx_migrate.end', [
|
||||
'duration_ms' => round((microtime(true) - $startedAt) * 1000, 2),
|
||||
'legacy_fetches_found' => count($legacyFetches),
|
||||
'legacy_rates_found' => count($legacyRows),
|
||||
'fx_fetches_imported' => $importedFetches,
|
||||
'fx_fetches_reused' => $reusedFetches,
|
||||
'fx_rates_imported' => $migratedRates,
|
||||
'measurements_checked' => count($measurements),
|
||||
'measurements_updated' => $updatedMeasurements,
|
||||
'measurements_reused' => $reusedMeasurements,
|
||||
'measurements_unresolved' => $unresolvedMeasurements,
|
||||
]);
|
||||
|
||||
return [
|
||||
'message' => 'Legacy-FX-Rates wurden nach fx-rates migriert und Messpunkte aktualisiert.',
|
||||
'legacy_fetches_found' => count($legacyFetches),
|
||||
@@ -1148,6 +1257,11 @@ final class Router
|
||||
private function bootstrapMeasurements(string $projectKey, array $settings): array
|
||||
{
|
||||
$rows = $this->repository()->listMeasurements($projectKey, self::BOOTSTRAP_MEASUREMENT_LIMIT);
|
||||
$this->debug->add('bootstrap.measurements.loaded', [
|
||||
'project_key' => $projectKey,
|
||||
'row_count' => count($rows),
|
||||
'limit' => self::BOOTSTRAP_MEASUREMENT_LIMIT,
|
||||
]);
|
||||
return $this->analytics()->enrichMeasurements($rows, $settings);
|
||||
}
|
||||
|
||||
@@ -2145,6 +2259,10 @@ final class Router
|
||||
if (function_exists('set_time_limit')) {
|
||||
@set_time_limit(15);
|
||||
}
|
||||
$this->debug->add('runtime.guards', [
|
||||
'time_limit_sec' => 15,
|
||||
'budget_sec' => self::LONG_REQUEST_BUDGET_SECONDS,
|
||||
]);
|
||||
}
|
||||
|
||||
private function releaseSessionLock(): void
|
||||
@@ -2160,8 +2278,41 @@ 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]);
|
||||
$elapsed = microtime(true) - $startedAt;
|
||||
if ($elapsed > self::LONG_REQUEST_BUDGET_SECONDS) {
|
||||
$this->debug->add('request.budget_exceeded', [
|
||||
'elapsed_ms' => round($elapsed * 1000, 2),
|
||||
'budget_ms' => round(self::LONG_REQUEST_BUDGET_SECONDS * 1000, 2),
|
||||
'message' => $message,
|
||||
]);
|
||||
throw new ApiException($message, 503, ['timeout' => true, 'elapsed_ms' => round($elapsed * 1000, 2)]);
|
||||
}
|
||||
}
|
||||
|
||||
private function safeTimed(string $event, callable $callback, mixed $fallback = null, array $context = []): mixed
|
||||
{
|
||||
$startedAt = microtime(true);
|
||||
$this->debug->add($event . '.start', $context);
|
||||
|
||||
try {
|
||||
$result = $callback();
|
||||
$meta = [
|
||||
'duration_ms' => round((microtime(true) - $startedAt) * 1000, 2),
|
||||
];
|
||||
if (is_array($result)) {
|
||||
$meta['count'] = count($result);
|
||||
}
|
||||
$this->debug->add($event . '.end', $context + $meta);
|
||||
return $result;
|
||||
} catch (\Throwable $exception) {
|
||||
$meta = [
|
||||
'duration_ms' => round((microtime(true) - $startedAt) * 1000, 2),
|
||||
'fallback_used' => true,
|
||||
'error' => $exception->getMessage(),
|
||||
'type' => get_debug_type($exception),
|
||||
];
|
||||
$this->debug->add($event . '.error', $context + $meta);
|
||||
return $fallback;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user