feat(ip-utils): add ipFaultDomain for peer network-block bucketing#805
feat(ip-utils): add ipFaultDomain for peer network-block bucketing#805vilenarios wants to merge 1 commit into
Conversation
Adds a pure helper that reduces a peer host to a stable "fault domain" key — the network block it belongs to — for chunk-seeding diversity accounting: - IPv4 (incl. IPv4-mapped IPv6): masked to /24 by default -> "a.b.c.0/24" - IPv6: expanded + truncated to /48 by default -> "2001:db8:abcd::/48" - non-IP (unresolved hostname): returned verbatim (its own domain) Also exports `expandIpv6` (8 zero-padded groups, handles `::`), the minimum IPv6 parsing needed for prefix bucketing — ip-utils' existing `isIpInCidr` is IPv4-only. No behavior change: pure additions, not yet wired. This is PR B1 of the chunk-fanout SPOF fix (see architect-handoff/chunk-seeding-durability-plan.md §17): the five tip-*.arweave.xyz preferred nodes all collapse into one 38.29.227.0/24, which the upcoming soft-preferred quorum (B2) uses to detect and route around a single-fault-domain tips outage. Tested: the five tips collapse to one /24; distinct /24s stay distinct; IPv6 /48 bucketing; IPv4-mapped normalization; custom widths; hostname fallback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XQPK4TXcVoXoyFp6sNW2Lr
📝 WalkthroughWalkthroughTwo new exported functions are added to IP Utility Additions
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/lib/ip-utils.ts (1)
285-288: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winHoist the IPv4 regex to module scope.
The TSDoc describes this function as "Pure and hot-path cheap," but
ipv4Segment/ipv4Regexare reconstructed vianew RegExp(...)on every call. Move them to module-level constants to avoid the per-invocation allocation and recompile.This also addresses the static-analysis ReDoS flag: the pattern is built from a fixed constant (not caller input) and is anchored with no catastrophic backtracking, so it is a false positive — hoisting makes that intent explicit.
♻️ Proposed refactor
Add module-level constants (outside the function):
+const IPV4_SEGMENT = '(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)'; +const IPV4_REGEX = new RegExp( + `^${IPV4_SEGMENT}\\.${IPV4_SEGMENT}\\.${IPV4_SEGMENT}\\.${IPV4_SEGMENT}$`, +);Then simplify the function body:
const normalized = normalizeIpv4MappedIpv6(host.trim()); // IPv4 - const ipv4Segment = '(?:25[0-5]|2[0-4]\\d|1?\\d?\\d)'; - const ipv4Regex = new RegExp( - `^${ipv4Segment}\\.${ipv4Segment}\\.${ipv4Segment}\\.${ipv4Segment}$`, - ); - if (ipv4Regex.test(normalized)) { + if (IPV4_REGEX.test(normalized)) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/lib/ip-utils.ts` around lines 285 - 288, The IPv4 matching logic in the hot-path function is rebuilding the same regex on every call, which adds avoidable allocation and compilation overhead. Hoist the fixed IPv4 pattern used by ipv4Segment and ipv4Regex to module scope as constants, then have the function reuse those shared constants instead of creating a new RegExp inside the function body. This keeps the intent of the IPv4 utility clear and preserves the anchored, fixed-pattern behavior referenced by the function’s TSDoc.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/lib/ip-utils.ts`:
- Around line 285-288: The IPv4 matching logic in the hot-path function is
rebuilding the same regex on every call, which adds avoidable allocation and
compilation overhead. Hoist the fixed IPv4 pattern used by ipv4Segment and
ipv4Regex to module scope as constants, then have the function reuse those
shared constants instead of creating a new RegExp inside the function body. This
keeps the intent of the IPv4 utility clear and preserves the anchored,
fixed-pattern behavior referenced by the function’s TSDoc.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 561d8a7d-1571-4866-9f0d-76e25dc1eeae
📒 Files selected for processing (2)
src/lib/ip-utils.test.tssrc/lib/ip-utils.ts
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #805 +/- ##
===========================================
+ Coverage 78.65% 78.79% +0.14%
===========================================
Files 132 132
Lines 49980 50189 +209
Branches 3763 3803 +40
===========================================
+ Hits 39312 39548 +236
+ Misses 10620 10592 -28
- Partials 48 49 +1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Makes chunk fanout fault-domain-aware and removes the single-fault-domain
tips SPOF (all five tip-*.arweave.xyz resolve into one 38.29.227.0/24, so a
tips outage hard-fails every POST today). Both behaviors are opt-in and
default to the legacy quorum exactly.
- Distinct-domain counting: buckets each successful peer by IP /24 (v4) /48
(v6) via ipFaultDomain; CHUNK_POST_MIN_DISTINCT_DOMAINS (default 0=off) adds
a soft target among successes that degrades gracefully when the eligible
peer set can't supply N (never hard-fails on scarcity).
- Soft-preferred fallback: CHUNK_POST_PREFERRED_SOFT_FALLBACK (default false)
lets a strong distinct-domain discovered quorum satisfy a POST when NO tip
was eligible (tips down/over-queue) — but never when tips merely failed,
so tips stay the hard primary on-ranp on a real outage only.
- Centralized verdict: evaluateChunkBroadcastVerdict is the single source of
truth for "seeded sufficiently?"; the handler reads result.succeeded instead
of recomputing the thresholds (removes latent drift). With both flags at
defaults it reduces EXACTLY to the legacy rule (regression test).
- Placement-evidence contract: BroadcastChunkResult now carries
distinctDomainCount/preferredEligibleCount/succeeded, and POST /chunk emits
X-AR-IO-Chunk-Placement-Domains — so the bundler can consume where a chunk
landed instead of collapsing the result to pass/fail (see plan §18).
- Metrics: chunk_post_distinct_domains (histogram),
chunk_post_preferred_shortfall_total{reason}, chunk_post_domain_shortfall_total{reason}.
Preferred shortfall is emitted even when the fallback makes the POST succeed,
so a tips outage is never silently masked.
Depends on #805 (ipFaultDomain). Default-off; soak before any default flip.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XQPK4TXcVoXoyFp6sNW2Lr
|
Superseded by #812 — consolidated into the |
What
Pure, unwired additions to
src/lib/ip-utils.ts:ipFaultDomain(host, { v4Bits=24, v6Bits=48 })— reduces a peer host to a stable "fault domain" bucket key (the network block it belongs to):/24→"a.b.c.0/24"/48→"2001:db8:abcd::/48"expandIpv6(ip)— expands to 8 zero-padded hex groups (handles::); the minimum IPv6 parsing needed for prefix bucketing (the existingisIpInCidris IPv4-only).Why
PR B1 of the chunk-fanout SPOF fix (design:
architect-handoff/chunk-seeding-durability-plan.md§17). The fivetip-*.arweave.xyzpreferred chunk-POST nodes all resolve into one38.29.227.0/24— so today's hardCHUNK_POST_MIN_PREFERRED_SUCCESS_COUNT=2quorum is a single-fault-domain SPOF: a tips outage hard-fails every chunk POST even when independent discovered peers accepted it.ipFaultDomainis the primitive the upcoming B2 (soft-preferred quorum + distinct-domain counting) uses to detect that condition and route around it. The chunk-POST peer list is IP-literal after the peer manager's DNS resolution, so bucketing is in-process with no hot-path DNS.Behavior change
None. Pure helper additions, not yet called anywhere. Ships first so B2 is a smaller, focused review.
Tests
yarn test:file src/lib/ip-utils.test.ts→ 50/50 pass. New cases: the five tips collapse to one/24; distinct/24s stay distinct; IPv6/48bucketing (same /48 ⇒ same bucket, different ⇒ different); IPv4-mapped normalization; custom prefix widths;expandIpv6full/compressed/malformed; unresolved-hostname fallback.🤖 Generated with Claude Code