Stats heatmap: empty hours same height; cname_chain only for long chains
The "Busiest times" grid's hours without data used the class "empty", which picked up the padding of empty messages. One CNAME to another no longer warns; three or more get an info note showing the chain. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
9db76a113c
commit
12013b12d5
6 files changed
+41
-10
No files matched your search
@@ -385,8 +385,16 @@ func (c *checker) checkCNAMETarget(z *model.Zone, ix *zoneIndex, r *model.Record
|
||||
if !ix.inZoneTarget(target) {
|
||||
return
|
||||
}
|
||||
// Follow the chain inside the zone to detect loops.
|
||||
// Follow the chain inside the zone to detect loops. One CNAME to
|
||||
// another is common and costs nothing (named answers the chain in
|
||||
// one response); a longer chain gets a note.
|
||||
seen := map[string]bool{r.Name: true}
|
||||
chain := []string{disp(r.Name), disp(target)}
|
||||
note := func() {
|
||||
if len(chain) > 3 {
|
||||
c.recProblem(z, r, Info, "cname_chain", "%s reaches its address through %d CNAMEs (%s); fine, but each one is another lookup for resolvers that don't get the chain at once", disp(r.Name), len(chain)-1, strings.Join(chain, " → "))
|
||||
}
|
||||
}
|
||||
cur := target
|
||||
for hops := 0; hops < 20; hops++ {
|
||||
if seen[cur] {
|
||||
@@ -398,18 +406,18 @@ func (c *checker) checkCNAMETarget(z *model.Zone, ix *zoneIndex, r *model.Record
|
||||
if len(next) == 0 {
|
||||
break
|
||||
}
|
||||
if hops == 0 {
|
||||
c.recProblem(z, r, Warning, "cname_chain", "%s is a CNAME to %s, which is itself a CNAME; chains slow down resolution", disp(r.Name), disp(target))
|
||||
}
|
||||
p := ix.parsed[next[0].ID]
|
||||
if p == nil {
|
||||
break
|
||||
}
|
||||
cur = p.RR.(*dns.CNAME).Target
|
||||
chain = append(chain, disp(cur))
|
||||
if !ix.inZoneTarget(cur) {
|
||||
note()
|
||||
return
|
||||
}
|
||||
}
|
||||
note()
|
||||
if len(ix.byName[target]) == 0 && !ix.hasAddress(target) && !hasDescendant(ix, target) {
|
||||
c.recProblem(z, r, Warning, "cname_dangling", "%s is a CNAME to %s, which does not exist in the zone", disp(r.Name), disp(target))
|
||||
}
|
||||
|
||||
@@ -161,8 +161,9 @@ func TestRecordConflicts(t *testing.T) {
|
||||
}, "cname_loop", Error},
|
||||
{"cname chain", func(b *builder, z *model.Zone) {
|
||||
b.rec(z, "a", "CNAME", "b")
|
||||
b.rec(z, "b", "CNAME", "ns1")
|
||||
}, "cname_chain", Warning},
|
||||
b.rec(z, "b", "CNAME", "c")
|
||||
b.rec(z, "c", "CNAME", "ns1")
|
||||
}, "cname_chain", Info},
|
||||
{"dangling cname", func(b *builder, z *model.Zone) { b.rec(z, "a", "CNAME", "nothing") }, "cname_dangling", Warning},
|
||||
{"null mx not alone", func(b *builder, z *model.Zone) {
|
||||
b.rec(z, "@", "MX", "0 .")
|
||||
@@ -380,3 +381,16 @@ func TestProblemStringDisplaysZone(t *testing.T) {
|
||||
t.Errorf("String() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
// One CNAME to another is common and says nothing.
|
||||
func TestShortCNAMEChain(t *testing.T) {
|
||||
b := newBuilder()
|
||||
z := b.primary("example.com", nil)
|
||||
b.rec(z, "songshare", "CNAME", "navidrome")
|
||||
b.rec(z, "navidrome", "CNAME", "ns1")
|
||||
for _, p := range State(b.s, Options{}) {
|
||||
if p.Code == "cname_chain" {
|
||||
t.Errorf("one extra CNAME: %s %s", p.Severity, p.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -283,7 +283,7 @@ export function heatmap(hm, unit) {
|
||||
const n = hm.samples[d][hr];
|
||||
const text = v < 0 ? day + " " + pad(hr) + ":00 – no data" :
|
||||
day + " " + pad(hr) + ":00 – " + Math.round(v).toLocaleString() + " " + unit + " per hour (average of " + n + (n === 1 ? " hour)" : " hours)");
|
||||
const cell = h("div", { class: "hm-cell " + (v < 0 ? "empty" : "heat-" + level(v)), tabindex: "-1", "aria-label": text });
|
||||
const cell = h("div", { class: "hm-cell " + (v < 0 ? "hm-empty" : "heat-" + level(v)), tabindex: "-1", "aria-label": text });
|
||||
const showTip = () => {
|
||||
tip.textContent = text;
|
||||
tip.hidden = false;
|
||||
@@ -313,7 +313,7 @@ export function heatmap(hm, unit) {
|
||||
h("span", { class: "muted small" }, "0"),
|
||||
Array.from({ length: steps }, (_, i) => h("span", { class: "hm-swatch heat-" + i })),
|
||||
h("span", { class: "muted small" }, compact(peak) + " " + unit + "/hour"),
|
||||
h("span", { class: "hm-swatch empty" }), h("span", { class: "muted small" }, "no data"));
|
||||
h("span", { class: "hm-swatch hm-empty" }), h("span", { class: "muted small" }, "no data"));
|
||||
return h("div", { class: "heatmap-wrap" }, grid, tip, scale);
|
||||
}
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
|
||||
.hm-row { display: flex; align-items: center; }
|
||||
.hm-cell { height: 22px; border-radius: 3px; }
|
||||
.hm-cell:hover, .hm-cell:focus { outline: 2px solid var(--text); outline-offset: -1px; }
|
||||
.hm-cell.empty, .hm-swatch.empty { background: transparent; box-shadow: inset 0 0 0 1px var(--chart-grid); }
|
||||
.hm-empty { background: transparent; box-shadow: inset 0 0 0 1px var(--chart-grid); }
|
||||
.heat-0 { background: var(--heat-0); } .heat-1 { background: var(--heat-1); } .heat-2 { background: var(--heat-2); }
|
||||
.heat-3 { background: var(--heat-3); } .heat-4 { background: var(--heat-4); } .heat-5 { background: var(--heat-5); }
|
||||
.heat-6 { background: var(--heat-6); }
|
||||
|
||||
Reference in new issue
Block a user