Files
coolify/app/Actions/Database/RestartDatabase.php
T
Andras BacsaiandClaude Opus 5.5 fbfe342f89 fix(databases): reserve start and restart atomically
Two parallel start requests both passed the in-progress check and got
200, because the start activity is created later by the queued action.
A per-database reservation (Cache::add, 600 s) is now taken before the
check, so the second request gets 409. The action releases it when it
creates its activity, skips, or fails. Restart, import, the deploy
API, MCP, and Livewire use the same reservation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 21:19:14 +02:00

54 lines
2.0 KiB
PHP

<?php
namespace App\Actions\Database;
use App\Models\StandaloneClickhouse;
use App\Models\StandaloneDragonfly;
use App\Models\StandaloneKeydb;
use App\Models\StandaloneMariadb;
use App\Models\StandaloneMongodb;
use App\Models\StandaloneMysql;
use App\Models\StandalonePostgresql;
use App\Models\StandaloneRedis;
use App\Support\DatabaseOperationReservation;
use App\Support\ResourceStartActivity;
use Lorisleiva\Actions\Concerns\AsAction;
class RestartDatabase
{
use AsAction;
/**
* @param string|null $reservation The token from StartDatabase::reserveOperation(). The request
* that queued this action holds the reservation; this action
* keeps it during the stop and releases it after the start.
*/
public function handle(StandaloneRedis|StandalonePostgresql|StandaloneMongodb|StandaloneMysql|StandaloneMariadb|StandaloneKeydb|StandaloneDragonfly|StandaloneClickhouse $database, ?string $reservation = null)
{
$reservation ??= DatabaseOperationReservation::acquire($database->uuid);
if ($reservation === null) {
return ResourceStartActivity::DATABASE_OPERATION_IN_PROGRESS_MESSAGE;
}
try {
$server = $database->destination->server;
if (! $server->isFunctional()) {
return 'Server is not functional';
}
$busyError = StartDatabase::operationInProgressError($database, $reservation);
if ($busyError !== null) {
return $busyError;
}
$prerequisiteError = StartDatabase::prerequisiteError($database);
if ($prerequisiteError !== null) {
return $prerequisiteError;
}
StopDatabase::run($database, dockerCleanup: false);
return StartDatabase::run($database, $reservation);
} finally {
DatabaseOperationReservation::release($database->uuid, $reservation);
}
}
}