Fix queued backups never building, per-type retention, and cron connection handling

This commit is contained in:
johnnyq
2026-07-31 16:35:39 -04:00
parent ae468d6cee
commit 53f45fa008
5 changed files with 103 additions and 49 deletions

View File

@@ -46,6 +46,16 @@ if ($queued > 0) {
* CONTRIBUTING's cron rules. A second dispatch in the same day (a manual Run Now, a catch-up
* after downtime) must not produce a second scheduled archive.
*/
$backup_job_row = mysqli_fetch_assoc(mysqli_query($mysqli, "SELECT cron_job_enabled FROM cron_jobs WHERE cron_job_name = 'backup'"));
if (empty($backup_job_row['cron_job_enabled'])) {
// Reached by a Run Now, which is how a queued backup gets built while the schedule is
// off. Building the scheduled archive as well would hand somebody who asked for a
// database backup an unasked-for full one. Scheduled work belongs to the schedule.
echo "Scheduled backups are switched off - built queued work only\n";
return;
}
$type = in_array($config_backup_cron_type, backupUnattendedTypes(), true) ? $config_backup_cron_type : BACKUP_TYPE_FULL;
$type_esc = escapeSql($type);

View File

@@ -183,6 +183,11 @@ function cronJobFinished($mysqli, string $job_name, string $status, ?float $dura
// "no job was due" apart from "nothing has run this since the server was rebuilt".
mysqli_query($mysqli, "UPDATE settings SET config_cron_last_dispatch_at = '" . date('Y-m-d H:i:s') . "' WHERE company_id = 1");
// Best effort: ask the server not to hang up while a long job is quiet.
if (function_exists('backupDbHoldOpen')) {
backupDbHoldOpen($mysqli);
}
// A fatal error inside a job cannot be caught, and it takes the rest of the cycle with it.
// Recording which job was running at the time is the only trace of that left behind.
$cron_dispatch_running = null;
@@ -224,6 +229,15 @@ foreach (cronJobRegistry() as $cron_dispatch_job) {
$cron_dispatch_running = $cron_dispatch_job['name'];
$cron_dispatch_started = microtime(true);
// Every job runs in this one process on this one connection, and a job that spends
// minutes on network or file work without querying leaves it idle long enough for a
// server with a short wait_timeout to close it. The next job then dies on a connection
// it never touched. Re-establish before each one so a quiet job cannot poison the rest
// of the cycle.
if (function_exists('backupDbEnsure')) {
$mysqli = backupDbEnsure($mysqli);
}
try {
require_once $cron_dispatch_path;
cronJobFinished($mysqli, $cron_dispatch_job['name'], 'Completed', microtime(true) - $cron_dispatch_started);