feat: add memory footprint to /stats and dashboard (#26)

* feat: add memory footprint to /stats and dashboard

Per-structure heap estimation (cache, blocklist, query log, SRTT,
overrides) with process RSS via mach_task_basic_info / sysconf.
Dashboard gets a 6th stat card and a sidebar breakdown panel with
stacked bar visualization.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: use phys_footprint on macOS to match Activity Monitor

Switch from MACH_TASK_BASIC_INFO (resident_size) to TASK_VM_INFO
(phys_footprint) which matches Activity Monitor's Memory column.
Also: capacity-aware heap estimation, entry counts in memory payload,
heap_bytes tests for all stores.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: remove redundant fields and fix naming in memory stats

Remove duplicate entry counts from MemoryStats (already in parent
StatsResponse), rename process_rss_bytes to process_memory_bytes
to match macOS phys_footprint semantics, drop restating comments.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Razvan Dimescu
2026-04-01 09:09:44 +03:00
committed by GitHub
parent 98da440c84
commit da93a3cde3
10 changed files with 512 additions and 6 deletions

View File

@@ -136,6 +136,46 @@ impl DnsRecord {
}
}
pub fn heap_bytes(&self) -> usize {
match self {
DnsRecord::A { domain, .. } => domain.capacity(),
DnsRecord::NS { domain, host, .. } | DnsRecord::CNAME { domain, host, .. } => {
domain.capacity() + host.capacity()
}
DnsRecord::MX { domain, host, .. } => domain.capacity() + host.capacity(),
DnsRecord::AAAA { domain, .. } => domain.capacity(),
DnsRecord::DNSKEY {
domain, public_key, ..
} => domain.capacity() + public_key.capacity(),
DnsRecord::DS { domain, digest, .. } => domain.capacity() + digest.capacity(),
DnsRecord::RRSIG {
domain,
signer_name,
signature,
..
} => domain.capacity() + signer_name.capacity() + signature.capacity(),
DnsRecord::NSEC {
domain,
next_domain,
type_bitmap,
..
} => domain.capacity() + next_domain.capacity() + type_bitmap.capacity(),
DnsRecord::NSEC3 {
domain,
salt,
next_hashed_owner,
type_bitmap,
..
} => {
domain.capacity()
+ salt.capacity()
+ next_hashed_owner.capacity()
+ type_bitmap.capacity()
}
DnsRecord::UNKNOWN { domain, data, .. } => domain.capacity() + data.capacity(),
}
}
pub fn set_ttl(&mut self, new_ttl: u32) {
match self {
DnsRecord::A { ttl, .. }
@@ -650,4 +690,14 @@ mod tests {
let parsed = round_trip(&rec);
assert_eq!(rec, parsed);
}
#[test]
fn heap_bytes_reflects_string_capacity() {
let rec = DnsRecord::CNAME {
domain: "a]".repeat(100),
host: "b".repeat(200),
ttl: 60,
};
assert!(rec.heap_bytes() >= 300);
}
}