atproto relay implementation in zig zlay.waow.tech

perf: diagnose memory growth — smaps attribution, rocksdb caps

- add smaps_rollup + smaps parsing: relay_smaps_rss_kb, relay_smaps_anon_kb,
relay_heap_rss_kb, relay_stack_rss_kb for heap/stack/mmap attribution
- rocksdb: max_open_files=256 (was unlimited), write_buffer_size=16M per CF
(was 64M default × 3 CFs = 192M memtable overhead)
- metrics buffer 12K → 16K

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

+10 -4
+10 -4
src/broadcaster.zig
··· 727 727 // smaps attribution — heap vs stack vs anon mmap breakdown 728 728 appendSmapsMetrics(w); 729 729 730 - // glibc malloc arena stats — mallinfo2 returns size_t fields (accurate 731 - // to full address space, unlike mallinfo which overflows at 2 GiB) 732 - const mi = malloc_h.mallinfo2(); 730 + // glibc malloc arena stats — mallinfo returns c_int fields which overflow 731 + // at 2 GiB; bitcast to u32 extends useful range to 4 GiB. 732 + // (mallinfo2 has size_t fields but isn't in zig's bundled glibc stubs. 733 + // the smaps metrics above give us accurate full-range attribution.) 734 + const mi = malloc_h.mallinfo(); 735 + const arena: u64 = @as(u32, @bitCast(mi.arena)); 736 + const in_use: u64 = @as(u32, @bitCast(mi.uordblks)); 737 + const free_bytes: u64 = @as(u32, @bitCast(mi.fordblks)); 738 + const mmap_bytes: u64 = @as(u32, @bitCast(mi.hblkhd)); 733 739 std.fmt.format(w, 734 740 \\# TYPE relay_malloc_arena_bytes gauge 735 741 \\# HELP relay_malloc_arena_bytes total bytes claimed from OS by malloc ··· 747 753 \\# HELP relay_malloc_mmap_bytes bytes allocated via mmap (large blocks) 748 754 \\relay_malloc_mmap_bytes {d} 749 755 \\ 750 - , .{ mi.arena, mi.uordblks, mi.fordblks, mi.hblkhd }) catch {}; 756 + , .{ arena, in_use, free_bytes, mmap_bytes }) catch {}; 751 757 } 752 758 753 759 fn appendSmapsMetrics(w: anytype) void {