feat: 외부 도메인 레코드 관리 도입 - #124
Merged
Merged
Conversation
The edit is a desired state rather than a list of operations, so a repeat of the same intent is one change and a retry is safe. Pushes are ordered by a per-domain generation and run under the shared name lock; a set that never reached the zone is dropped rather than queued for a removal the zone would refuse forever. Values are refused before they are stored: our own ingress and the campus range, addresses that are not destinations on the public internet, a CNAME back into a platform root, and TXT that would speak for the parent's mail policy.
Nothing came back for them. The resync walks routes and an external domain has none, and the zone pass beside it decides by the platform's own address, which no external record carries, so an apply that died mid-push left rows owed a write that nothing retried. The push half is driven by the rows, because the provider already reads a set before writing it and re-deciding that here would be a second copy of the comparison. The listing answers the other direction, which rows cannot: a set the zone holds that no row claims, left in place and reported unless orphan pruning is on.
FAILED means the set is owed a write, so a failed removal recorded that way had the next apply put the set back into the zone instead of taking it down. The row goes only once the provider confirms the deletion, so it would then be owed a write for good and the name it belongs to could never be reclaimed.
Nothing above such a name expires and it is issued without approval, so whoever typed it first would hold it for good. Its own deadline replaces that: notices at the configured stages, then a release that takes the records down and puts the name into the same reservation grace a release by hand gets. Renewing is a button rather than a liveness check the platform makes. A site that is briefly down and a name whose owner has gone look the same from outside, and only the owner can tell them apart. Taking a domain down goes through one door still, which now has an external branch: without it a name with no route read as one that never served and was freed the instant its owner let go of it. The reclaim of such a name waits on its record sets being out of the zone, so the next holder cannot inherit the last one's DNS, and its notices are addressed through its own access list because it has no VM to ask.
Two enum values reach the public spec: the external domain kind, and the resource type that will carry its access list. No operation changes.
The generation guard runs before the provider call, so an edit that commits during one is invisible to it. The row was then stamped APPLIED holding values the zone had never received, and the push that superseded it skips applied rows, so the zone and the row disagreed with nothing left to find it. The status write now lands only while the row still holds what was pushed. Compared on the values rather than the generation, because the generation moves for any edit to the domain and a set that edit did not touch was written correctly.
An administrator's takedown frees the name in the same transaction that marks the sets for removal, and every scan started from the domains table is blind to a retired row. One failed push therefore left records standing in the platform's zone on a name anybody could now register, which is what the takedown exists to prevent. The question is asked of the record rows instead, which is where the debt is written down. A retired domain never writes: its owed sets become removals, and when a live row has since taken the name they are forgotten rather than removed, since removing them would delete the new owner's records.
A released row keeps its name through the reservation grace, and only the retired case was refused. So a set added or still owed during that grace was pushed into the zone for a name mid-reclaim, and being owed a write rather than a removal it made the reclaim find work forever: the name was never freed and the sweep said so once an hour with nothing changing. An edit is refused once the name is let go, and a push under one turns whatever it finds into a removal.
Addresses in the campus range are refused because a university name aimed at another machine on that network is the shape a phishing page takes. A CNAME reaches the same place and no address rule can see it, because the target is deliberately never resolved here, so the whole family was one record type away from being bypassed. Values are also normalized before they are checked and stored. The guards ran on a stripped and folded copy while the raw string was what the zone received, so a leading space passed every rule and then failed at the provider for as long as the row existed.
The notice was addressed through the VM, so a name with none reached nobody at all. The audit row alone reaches no user, and a public address disappearing must not be discovered from a dead link, which is what the method already said it was for. The sentence has a form that names no VM rather than naming an empty one, which is how a reader tells a notice from a defect.
The reclaim asked the rows and nothing else, and rows going is not the same as the zone being clear. A push that reached the zone and then failed to record itself leaves a set no row remembers, and the next edit drops the row that would have owed its removal, so the row side has nothing left to say. Freeing the name then handed its next holder a record pointing at the last one's server. Every reclaim of such a name now reads the zone under it and takes down whatever is there, or keeps the name reserved another cycle if it cannot. Not gated behind the orphan-pruning switch: that switch guards a scan that has to guess which records are the platform's to remove, and here the platform still holds the name.
Two reservation notices now reach names with no VM, and both were written for one: they called the name a platform subdomain and told its owner to re-attach it, which is not an action such a name has. The noun and the verb are chosen the same way the link already was.
The locked record query was never called, and its javadoc described it as what serialises two applies for one domain. That defence is the name lock, so the comment was documenting protection the code did not have, which is the sort of thing that makes a real gap read as covered. Two more statements corrected: a push that stands down does not itself write the final state, and the reader-facing list now has the order it claimed. A dead set in the policy goes too.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📝 작업내용
EXTERNAL추가DomainRecordsServiceDomainRecordPolicyDomainRecordApplyJobDomainRecordsReconcilerDomainRenewalSweeperDomainKind에EXTERNAL,ResourceType에DOMAIN. 연산 변화 없음⭐️ 검증
💬 리뷰 포인트