Skip to content

Build1 publisher3 min readPublished

Splitting a tenant DNS cutover into lower, switch and restore gives each step its own deadline

A dev.to walkthrough of per-tenant subdomains on a gaming platform gives the TTL drop, the target change and the TTL restore separate deadlines, preconditions and audit evidence, so a worker that restarts can prove what it already applied.

The Engineer · Build desk

Illustration accompanying Splitting a tenant DNS cutover into lower, switch and restore gives each step its own deadline

What happened

  • A dev.to walkthrough models a per-tenant DNS cutover as three separate idempotent states, pre-change TTL lowering, the cutover itself and TTL restoration, each with its own deadline, evidence and retry policy.
  • The lowering step and the cutover each get their own not_before time, and the interval between them must be at least the previous TTL plus a safety margin the operator picks.
  • When a scheduler set for 01:00 UTC does not start until 01:47, it should still lower the TTL, recompute the earliest cutover from that moment, and record the original 02:00 target as superseded.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

  • constraint Nobody gets exactly-once delivery across a scheduler, a database and a DNS API, so every state has to be written to be re-run against a precondition and converge on the same record set.
  • cost Adopting this means rewriting the scheduler's storage rather than adding a worker to it, and whoever owns the job runner pays that migration before the first tenant moves.
  • decision An unexpected current record stops the automation and routes the plan to a person, which trades coverage for the evidence a later reconciliation needs.
  • exposure The restore is gated on an application owner's stabilization decision, so an unavailable owner leaves a tenant on the temporary TTL, and only an explicit expiry and its alert end that state.

The read-before-write in the DNS adapter is what makes repeated delivery survivable. Before it mutates anything, the adapter fetches the current record and compares it against the expected precondition. If the desired value is already present, it returns an already_applied result with evidence. If the current value matches neither the expected old value nor the desired one, it returns a conflict for human review [17]. Blind overwrites erase the evidence reconciliation needs [17].

That matters for the restart case. A worker can die after applying a change and before recording success, and the next run has to prove what happened and continue without creating a second, conflicting transition [18]. Each command carries an idempotency key of the form planID:version:state [16]. A worker may visit a checkpoint repeatedly, but only one transition may be committed for the same plan version [8]. The audit entry is written in the same database transaction that advances local state, and the remote observation is attached to that transition as evidence [20].

The post is direct about the limit: exactly-once execution across a scheduler, a database and an authoritative DNS service is not a realistic promise [19]. Exactly-once effects are the achievable target, and repeated delivery is acceptable when preconditions, idempotency keys and durable transition records make the mutation converge on one intended record set [19]. The author calls this familiar territory for payment systems [21].

The write succeeding and the resolvers forgetting are separate events [3], and the late plan is where a scheduler can confuse them. Lowering is scheduled for 01:00 UTC, cutover for 02:00 UTC, and the scheduler does not begin until 01:47 [9]. Sixty minutes of planned cache drain is now thirteen [10]. The rule is to apply the lower TTL if the plan is still authorized, compute a new earliest cutover from the observed application time, and mark the original target as superseded in the audit record [9]. "Don't trade correctness for a clock on a dashboard," the author wrote [11].

The timings are illustrative. The worked plan moves a tenant called arcade-17, served at arcade-17.play.example, from 192.0.2.10 to 192.0.2.44, and drops a normal TTL of 3,600 seconds to 300 for the window; the addresses are documentation values and the timings are example policy values, not measured recommendations [7]. For a one-hour drain to transfer to another fleet, the previous TTL has to bound how long the old answer actually survives in the resolvers serving those tenants, since a subdomain cannot move faster than the cached answer already held [1]. The post treats the safety margin as a policy input, not a universal constant [5]. "I'm not sure any fixed value can be defended without resolver observations from the actual tenant population," the author wrote [6].

A job row with run_at and done can record that a worker woke up. The control plane needs an immutable plan identity, a monotonically increasing version, the expected record before and after each mutation, timestamps for eligibility and observation, and a result digest returned by the DNS adapter [14]. The test for that field list is whether it can distinguish a safe retry from an operator editing a plan that is already in flight [15].

What to watch

  • Resolver observations from a real tenant population would turn the safety margin from a policy input into a number an operator can defend.
  • Whether the hosted DNS providers in use expose the precondition compare and result digest the adapter contract assumes.
  • Publication of the full state machine and transition list, which the excerpt breaks off before showing.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories