++ consolidamento funzionalità Backup
This commit is contained in:
@@ -31,6 +31,23 @@ class BackupOperationState
|
||||
/** Coda dei log: solo le ultime righe, lo stato viaggia a ogni poll. */
|
||||
private const MAX_LOG = 60;
|
||||
|
||||
/**
|
||||
* Puntatore all'unica operazione ammessa alla volta.
|
||||
*
|
||||
* Backup, ripristino e azzeramento lavorano tutti sullo stesso database e
|
||||
* sullo stesso disco: due in parallelo — due schede aperte, due
|
||||
* amministratori — non si limitano a rallentarsi, si sovrascrivono. Un
|
||||
* ripristino che riscrive le tabelle mentre un azzeramento le cancella
|
||||
* lascia uno stato che nessuno dei due voleva.
|
||||
*
|
||||
* Il TTL e' la valvola di sicurezza: se il worker muore senza chiudere
|
||||
* l'operazione, il blocco si scioglie da solo entro un'ora invece di
|
||||
* richiedere un intervento sul server. Prima ancora, corrente() considera
|
||||
* libero il posto se l'operazione puntata non esiste piu' o e' terminata.
|
||||
*/
|
||||
private const CHIAVE_CORRENTE = 'backup-op:corrente';
|
||||
private const TTL_CORRENTE = 3600;
|
||||
|
||||
public function initialize(string $id, string $tipo, string $fase, int $total = 0): array
|
||||
{
|
||||
$state = [
|
||||
@@ -48,10 +65,40 @@ class BackupOperationState
|
||||
];
|
||||
|
||||
$this->put($id, $state);
|
||||
Cache::put(self::CHIAVE_CORRENTE, $id, self::TTL_CORRENTE);
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Id dell'operazione in corso, o null se non ce n'e' nessuna.
|
||||
*
|
||||
* Serve a due cose: rifiutare l'avvio di una seconda operazione, e
|
||||
* riagganciare la barra di avanzamento quando la pagina viene ricaricata —
|
||||
* senza, un azzeramento fermo in attesa di conferma resterebbe irraggiungibile
|
||||
* e nessuno potrebbe piu' confermarlo o annullarlo.
|
||||
*/
|
||||
public function corrente(): ?string
|
||||
{
|
||||
$id = Cache::get(self::CHIAVE_CORRENTE);
|
||||
|
||||
if (! is_string($id) || $id === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$stato = $this->get($id);
|
||||
|
||||
// Puntatore orfano (stato scaduto) o operazione gia' chiusa: il posto
|
||||
// e' libero, e il puntatore va tolto di mezzo.
|
||||
if ($stato === null || in_array($stato['status'] ?? '', ['completed', 'failed'], true)) {
|
||||
Cache::forget(self::CHIAVE_CORRENTE);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
return $id;
|
||||
}
|
||||
|
||||
public function get(string $id): ?array
|
||||
{
|
||||
return Cache::get($this->key($id));
|
||||
@@ -141,6 +188,8 @@ class BackupOperationState
|
||||
|
||||
return $state;
|
||||
});
|
||||
|
||||
$this->liberaCorrente($id);
|
||||
}
|
||||
|
||||
public function markFailed(string $id, string $error): void
|
||||
@@ -154,6 +203,19 @@ class BackupOperationState
|
||||
});
|
||||
|
||||
$this->log($id, 'ERRORE: ' . $error);
|
||||
$this->liberaCorrente($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Libera il posto, ma solo se e' ancora questa operazione a occuparlo:
|
||||
* senza il confronto, la chiusura di un'operazione vecchia sbloccherebbe
|
||||
* quella appena avviata da qualcun altro.
|
||||
*/
|
||||
private function liberaCorrente(string $id): void
|
||||
{
|
||||
if (Cache::get(self::CHIAVE_CORRENTE) === $id) {
|
||||
Cache::forget(self::CHIAVE_CORRENTE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user