Motivation
Every rtype Lookup (a.go, aaaa.go, cname.go, mx.go, …) re-implements the same switch val.(type) over the shapes a record can take in the in-memory store: the typed struct slice (e.g. []types.ARecord), the freshly-written []map[string]interface{}, and the post-persistence/replication []interface{} (of map[string]interface{}). The parsing of ip/ttl/target/etc. out of the map is duplicated per type, with subtle drift between them — which is exactly what caused #55 (A served the default TTL because Add stored uint32 while Lookup only read float64) and #56 (AAAA dropped the []map[string]interface{} shape entirely because its switch was missing that case).
Suggested direction
Introduce a shared decode helper that normalizes any stored value into a canonical []map[string]interface{} (or a small typed row) once, so each rtype Lookup consumes a single shape and only maps fields → dns.RR. Options:
- a
func normalizeRecords(val any) []map[string]interface{} in the store/rtypes layer that folds []typed, []interface{} and []map[string]interface{} into one form, plus tolerant ttl/int helpers (the existing ttlFromMap in memory.go is a good seed);
- or normalize the shape at write/persist time so only one shape is ever stored and read.
Either removes the per-rtype switch val.(type) boilerplate and the class of bugs where a new writer (e.g. the ALIAS flattener) stores a shape some reader forgot to handle.
Scope
Refactor only — no behavior change. Touches the rtype Lookups and possibly the store's write path; should be covered by the existing per-rtype lifecycle tests plus the map-shape regression tests added in #55/#56.
Deferred/low priority (raised while fixing #55/#56).
Motivation
Every rtype
Lookup(a.go, aaaa.go, cname.go, mx.go, …) re-implements the sameswitch val.(type)over the shapes a record can take in the in-memory store: the typed struct slice (e.g.[]types.ARecord), the freshly-written[]map[string]interface{}, and the post-persistence/replication[]interface{}(ofmap[string]interface{}). The parsing ofip/ttl/target/etc. out of the map is duplicated per type, with subtle drift between them — which is exactly what caused #55 (A served the default TTL because Add storeduint32while Lookup only readfloat64) and #56 (AAAA dropped the[]map[string]interface{}shape entirely because its switch was missing that case).Suggested direction
Introduce a shared decode helper that normalizes any stored value into a canonical
[]map[string]interface{}(or a small typed row) once, so each rtypeLookupconsumes a single shape and only maps fields →dns.RR. Options:func normalizeRecords(val any) []map[string]interface{}in the store/rtypes layer that folds[]typed,[]interface{}and[]map[string]interface{}into one form, plus tolerantttl/int helpers (the existingttlFromMapin memory.go is a good seed);Either removes the per-rtype
switch val.(type)boilerplate and the class of bugs where a new writer (e.g. the ALIAS flattener) stores a shape some reader forgot to handle.Scope
Refactor only — no behavior change. Touches the rtype
Lookups and possibly the store's write path; should be covered by the existing per-rtype lifecycle tests plus the map-shape regression tests added in #55/#56.Deferred/low priority (raised while fixing #55/#56).