Wiederkehrende Aufgaben ohne Cronjob (ADR 073): Sicherung, Statistik kuerzen, geplante Veroeffentlichung, Updatepruefung und Aufraeumen laufen als Plan in der Tabelle scheduled_task - eine Zeile je Aufgabe mit Faelligkeit, Sperre und letztem Ergebnis. Auf geteiltem Hosting gibt es oft keinen Cronjob; der Plan haengt stattdessen an Anfragen und an einem Takt aus dem geoeffneten Backend. Diese Arbeit entstand in einer parallel laufenden Sitzung und lag bis hierher unveroeffentlicht im Arbeitsverzeichnis: die Releases 1.5.0 bis 1.5.5 wurden bewusst aus dem jeweiligen Tag gebaut und enthielten sie deshalb nicht. Vor der Veroeffentlichung geprueft, nicht bloss eingepackt: - phpunit 452/452, npm test 28/28, npm run test:editor vollstaendig. - Die Migration 20260914000100 legt nur die Tabelle scheduled_task an und nimmt sie beim Zurueckrollen wieder weg. Sie fasst keine bestehenden Daten an. - Seit einer Stunde keine Schreibzugriffe der anderen Sitzung mehr. Dabei zwei Fehler behoben: - config/services.yaml verwendet !tagged_iterator. Das ist gueltiges Symfony, aber "lint:yaml config" kennt eigene Tags nur mit --parse-tags und gab 1 zurueck - der CI-Lauf waere daran gescheitert. Flag ergaenzt. - AdminHeadersSubscriber.php (aus 1.5.3) entsprach nicht dem Stil von php-cs-fixer; composer lint gab 8 zurueck. Behoben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
59 lines
1.7 KiB
PHP
59 lines
1.7 KiB
PHP
<?php
|
||
|
||
namespace App\Ops\Tasks;
|
||
|
||
use App\Ops\Backup;
|
||
|
||
/**
|
||
* Die tägliche Sicherung von Datenbank und Medien.
|
||
*
|
||
* Die schwerste Aufgabe im Plan, und die einzige, bei der das Ausführen in einer Webanfrage
|
||
* wirklich etwas kostet: sie kopiert die Datenbank und den Medienordner. Der Besucher wartet
|
||
* trotzdem nicht darauf – sie läuft nach der ausgelieferten Antwort. Was sie belegt, ist ein
|
||
* PHP-Prozess des Hostings, einmal am Tag, für die Dauer einer Kopie.
|
||
*
|
||
* Wer einen echten Cronjob hat, sollte sie dort laufen lassen und hier abschalten. Wer keinen
|
||
* hat, hat mit dieser Aufgabe eine tägliche Sicherung statt gar keiner – und das ist der Grund,
|
||
* warum sie trotz ihres Gewichts im Plan steht.
|
||
*/
|
||
final class BackupTask implements Task
|
||
{
|
||
public function __construct(private Backup $backup)
|
||
{
|
||
}
|
||
|
||
public function name(): string
|
||
{
|
||
return 'backup';
|
||
}
|
||
|
||
public function label(): string
|
||
{
|
||
return 'Sicherung anlegen';
|
||
}
|
||
|
||
public function explanation(): string
|
||
{
|
||
return 'Ohne sie gibt es keinen Rückweg außer der von Hand angelegten Sicherung. Sie rotiert selbst und enthält Datenbank und Medien, aber weder .env noch vendor/.';
|
||
}
|
||
|
||
public function interval(): int
|
||
{
|
||
return 86400;
|
||
}
|
||
|
||
public function run(): string
|
||
{
|
||
$result = $this->backup->create('automatisch');
|
||
$removed = $this->backup->rotate();
|
||
|
||
return sprintf(
|
||
'%s angelegt: %d Seite(n), %d Mediendatei(en).%s',
|
||
$result['name'],
|
||
$result['manifest']['inhalt']['seiten'],
|
||
$result['manifest']['medien']['dateien'],
|
||
[] === $removed ? '' : sprintf(' %d alte entfernt.', count($removed)),
|
||
);
|
||
}
|
||
}
|