From f982c3c3dabc41e70d898521b5c5b8fec3b02885 Mon Sep 17 00:00:00 2001 From: Haylan Date: Mon, 14 Sep 2026 22:00:26 +0200 Subject: [PATCH] feat(dump): track async dump jobs in a status table --- .../Migration1789413583CreateDumpJobTable.php | 35 ++++++++ src/Service/DumpJobStatusService.php | 89 +++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 src/Migration/Migration1789413583CreateDumpJobTable.php create mode 100644 src/Service/DumpJobStatusService.php diff --git a/src/Migration/Migration1789413583CreateDumpJobTable.php b/src/Migration/Migration1789413583CreateDumpJobTable.php new file mode 100644 index 0000000..88c8ab5 --- /dev/null +++ b/src/Migration/Migration1789413583CreateDumpJobTable.php @@ -0,0 +1,35 @@ +executeStatement(<<<'SQL' + CREATE TABLE IF NOT EXISTS `aeon_dump_manager_job` ( + `id` BINARY(16) NOT NULL, + `status` VARCHAR(20) NOT NULL, + `percent` SMALLINT UNSIGNED NULL, + `filename` VARCHAR(255) NULL, + `error_message` TEXT NULL, + `created_at` DATETIME(3) NOT NULL, + `updated_at` DATETIME(3) NULL, + PRIMARY KEY (`id`) + ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4; + SQL); + } + + public function updateDestructive(Connection $connection): void + { + // nothing destructive to do + } +} diff --git a/src/Service/DumpJobStatusService.php b/src/Service/DumpJobStatusService.php new file mode 100644 index 0000000..81d6526 --- /dev/null +++ b/src/Service/DumpJobStatusService.php @@ -0,0 +1,89 @@ +connection->insert('aeon_dump_manager_job', [ + 'id' => $id, + 'status' => 'pending', + 'created_at' => (new \DateTimeImmutable())->format('Y-m-d H:i:s.v'), + ]); + + return Uuid::fromBytesToHex($id); + } + + public function markRunning(string $jobId): void + { + $this->update($jobId, ['status' => 'running']); + } + + public function updateProgress(string $jobId, int $percent): void + { + $this->update($jobId, ['percent' => min(99, max(0, $percent))]); + } + + public function markDone(string $jobId, string $filename): void + { + $this->update($jobId, ['status' => 'done', 'percent' => 100, 'filename' => $filename]); + } + + public function markFailed(string $jobId, string $errorMessage): void + { + $this->update($jobId, ['status' => 'failed', 'error_message' => $errorMessage]); + } + + /** + * @return array{id: string, status: string, percent: ?int, filename: ?string, errorMessage: ?string}|null + */ + public function find(string $jobId): ?array + { + $row = $this->connection->fetchAssociative( + 'SELECT LOWER(HEX(id)) AS id, status, percent, filename, error_message FROM aeon_dump_manager_job WHERE id = :id', + ['id' => Uuid::fromHexToBytes($jobId)] + ); + + if ($row === false) { + return null; + } + + return [ + 'id' => $row['id'], + 'status' => $row['status'], + 'percent' => $row['percent'] !== null ? (int) $row['percent'] : null, + 'filename' => $row['filename'], + 'errorMessage' => $row['error_message'], + ]; + } + + public function purgeOlderThan(\DateTimeImmutable $threshold): void + { + $this->connection->executeStatement( + 'DELETE FROM aeon_dump_manager_job WHERE created_at < :threshold', + ['threshold' => $threshold->format('Y-m-d H:i:s.v')] + ); + } + + private function update(string $jobId, array $data): void + { + $data['updated_at'] = (new \DateTimeImmutable())->format('Y-m-d H:i:s.v'); + + $this->connection->update('aeon_dump_manager_job', $data, ['id' => Uuid::fromHexToBytes($jobId)]); + } +}