Replace drifting cron loops with persistent cadence-tracked workers and CI-verifiable Compose wiring.
122 lines
5.2 KiB
PHP
122 lines
5.2 KiB
PHP
<?php
|
|
|
|
namespace classes;
|
|
|
|
class cron_schema_bootstrap
|
|
{
|
|
private static bool $initialized = false;
|
|
|
|
public static function ensureTables(): void
|
|
{
|
|
if (self::$initialized) {
|
|
return;
|
|
}
|
|
|
|
global $db;
|
|
|
|
if (!isset($db) || !is_object($db) || !method_exists($db, 'query')) {
|
|
return;
|
|
}
|
|
|
|
$db->query(
|
|
"CREATE TABLE IF NOT EXISTS cron_task_state (
|
|
task_id VARCHAR(191) NOT NULL PRIMARY KEY,
|
|
module VARCHAR(64) NOT NULL,
|
|
enabled TINYINT(1) NOT NULL DEFAULT 1,
|
|
schedule_json LONGTEXT NULL,
|
|
last_run_at DATETIME NULL,
|
|
next_run_at DATETIME NULL,
|
|
locked_until DATETIME NULL,
|
|
lock_owner VARCHAR(191) NULL,
|
|
current_run_id BIGINT UNSIGNED NULL,
|
|
last_status VARCHAR(32) NULL,
|
|
last_error TEXT NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_cron_task_state_next_run (enabled, next_run_at),
|
|
KEY idx_cron_task_state_lock (locked_until)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
);
|
|
|
|
$db->query(
|
|
"CREATE TABLE IF NOT EXISTS cron_task_runs (
|
|
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
|
task_id VARCHAR(191) NOT NULL,
|
|
module VARCHAR(64) NOT NULL,
|
|
source VARCHAR(32) NOT NULL,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'running',
|
|
actor_user_id INT NULL,
|
|
scheduled_for DATETIME NULL,
|
|
started_at DATETIME NULL,
|
|
completed_at DATETIME NULL,
|
|
duration_ms INT UNSIGNED NULL,
|
|
summary_json LONGTEXT NULL,
|
|
error_message TEXT NULL,
|
|
lock_owner VARCHAR(191) NULL,
|
|
force_run TINYINT(1) NOT NULL DEFAULT 0,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_cron_task_runs_task_created (task_id, created_at),
|
|
KEY idx_cron_task_runs_status_created (status, created_at),
|
|
KEY idx_cron_task_runs_module_created (module, created_at)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
);
|
|
self::ensureColumn('cron_task_runs', 'force_run', 'TINYINT(1) NOT NULL DEFAULT 0 AFTER lock_owner');
|
|
|
|
$db->query(
|
|
"CREATE TABLE IF NOT EXISTS cron_worker_state (
|
|
worker_id VARCHAR(191) NOT NULL PRIMARY KEY,
|
|
name VARCHAR(191) NOT NULL,
|
|
hostname VARCHAR(191) NULL,
|
|
pid INT UNSIGNED NULL,
|
|
source VARCHAR(64) NOT NULL DEFAULT 'coolify_worker',
|
|
status VARCHAR(32) NOT NULL DEFAULT 'starting',
|
|
release_channel_id BIGINT UNSIGNED NULL,
|
|
release_target_id BIGINT UNSIGNED NULL,
|
|
coolify_resource_uuid VARCHAR(128) NULL,
|
|
coolify_resource_type VARCHAR(32) NULL,
|
|
commit_sha VARCHAR(64) NULL,
|
|
poll_seconds INT UNSIGNED NOT NULL DEFAULT 15,
|
|
last_run_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
last_stale_run_count INT UNSIGNED NOT NULL DEFAULT 0,
|
|
last_error TEXT NULL,
|
|
started_at DATETIME NULL,
|
|
last_heartbeat_at DATETIME NULL,
|
|
last_loop_started_at DATETIME NULL,
|
|
last_loop_finished_at DATETIME NULL,
|
|
last_loop_gap_seconds INT UNSIGNED NULL,
|
|
consecutive_minute_loops INT UNSIGNED NOT NULL DEFAULT 0,
|
|
stopped_at DATETIME NULL,
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
|
|
KEY idx_cron_worker_state_heartbeat (last_heartbeat_at),
|
|
KEY idx_cron_worker_state_status (status),
|
|
KEY idx_cron_worker_state_release_target (release_target_id),
|
|
KEY idx_cron_worker_state_coolify_resource (coolify_resource_uuid)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
|
|
);
|
|
self::ensureColumn('cron_worker_state', 'last_loop_gap_seconds', 'INT UNSIGNED NULL AFTER last_loop_finished_at');
|
|
self::ensureColumn('cron_worker_state', 'consecutive_minute_loops', 'INT UNSIGNED NOT NULL DEFAULT 0 AFTER last_loop_gap_seconds');
|
|
|
|
self::$initialized = true;
|
|
}
|
|
|
|
private static function ensureColumn(string $table, string $column, string $definition): void
|
|
{
|
|
global $db;
|
|
|
|
$table = preg_replace('/[^a-zA-Z0-9_]/', '', $table);
|
|
$column = preg_replace('/[^a-zA-Z0-9_]/', '', $column);
|
|
if ($table === '' || $column === '') {
|
|
return;
|
|
}
|
|
|
|
$result = $db->query("SHOW COLUMNS FROM `{$table}` LIKE '{$column}'");
|
|
if ($result && $result->num_rows > 0) {
|
|
return;
|
|
}
|
|
|
|
$db->query("ALTER TABLE `{$table}` ADD COLUMN `{$column}` {$definition}");
|
|
}
|
|
}
|