Files
coolify/app/Services/Dns/ManagedDnsHostnameLock.php
Andras BacsaiandClaude Opus 5.5 2063e84d7c fix(dns): release managed records when a domain edit removes a hostname
- Editing a domain (UI application, service and preview domains, the
  General form, API application, preview, service, and service
  application updates) now releases the hostnames that the edit
  removed, like removing a domain does. Records that another live
  resource still uses are kept.
- A per-hostname lock (managed-dns:{team}:{hostname}) now covers the
  release (re-check, Cloudflare delete, row delete) and the create
  paths. A release can no longer delete a record that is being added
  again; a busy lock makes the release retry and the create wait or
  retry, and a create after a release makes the record again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:06:37 +02:00

42 lines
1.3 KiB
PHP

<?php
namespace App\Services\Dns;
use Illuminate\Contracts\Cache\LockTimeoutException;
use Illuminate\Support\Facades\Cache;
/**
* Serializes managed DNS changes per team and hostname. The create path (check existing row, create the provider
* record, add references) and the release path (re-check usage, delete the provider record and the row) both
* run inside this lock, so a release can never delete a record that a concurrent create just started to use.
*/
class ManagedDnsHostnameLock
{
public const TTL_SECONDS = 60;
public const WAIT_SECONDS = 5;
public function __construct(
public int $waitSeconds = self::WAIT_SECONDS,
public int $ttlSeconds = self::TTL_SECONDS,
) {}
public static function key(int $teamId, string $hostname): string
{
return 'managed-dns:'.$teamId.':'.strtolower(rtrim($hostname, '.'));
}
/**
* @template TResult
*
* @param callable(): TResult $callback
* @return TResult
*
* @throws LockTimeoutException when another change for the hostname does not finish in time
*/
public function run(int $teamId, string $hostname, callable $callback): mixed
{
return Cache::lock(self::key($teamId, $hostname), $this->ttlSeconds)->block($this->waitSeconds, $callback);
}
}