swactor/docs/diagrams/swim_probe_cycle.svg
Developer 59845f4059 feat: Docker realization, node binary, docs reorg, and simulation testing
Docker realization (bridging simulation to real TCP):
- NodeDriver (`crates/distribution/src/driver.rs`): bridges DistributedNode
  tick loop to TcpTransport with piggyback-extended wire messages
- swactor-node binary (`crates/node/`): CLI node with --listen, --seed,
  --dashboard-port, --actors flags
- Dockerfile: multi-stage build (rust:1.93-slim → debian:bookworm-slim)
- Docker integration tests (`tests/docker/`): 5-node cluster with 4
  scenarios (convergence, failure detection, actor resolution, rejoin)
- LAN cluster scripts for cross-machine validation
- TCP transport retry-on-stale-connection logic
- /api/distribution REST endpoint on dashboard (feature-gated)
- Piggyback fields (piggyback + from_addr) on Ping/Ack/PingReq messages

Docs reorganization:
- docs/runtime/ — actor-model, runtime, worker-thread, channels
- docs/distribution/ — distribution, swim, kademlia, transport
- docs/diagrams/ — all SVG files
- docs/connectome/ — connectome analysis
- docs/development_history/ — DOCKER_REALIZATION.md, SIMULATION_TESTING.md
- render_docs.sh outputs to docs/diagrams/
- README links updated to new paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 07:45:44 +00:00

186 lines
12 KiB
XML

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1100 750" font-family="system-ui, sans-serif">
<defs>
<filter id="shadow" x="-4%" y="-4%" width="108%" height="108%">
<feDropShadow dx="1" dy="2" stdDeviation="3" flood-color="#000" flood-opacity="0.10"/>
</filter>
<marker id="arrow-gray" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#5f6368">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-green" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#34a853">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-red" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#ea4335">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
<marker id="arrow-blue" viewBox="0 0 10 7" refX="10" refY="3.5"
markerWidth="10" markerHeight="7" orient="auto-start-reverse" fill="#4285f4">
<polygon points="0 0, 10 3.5, 0 7"/>
</marker>
</defs>
<!-- Background -->
<rect width="1100" height="750" rx="8" fill="#f8f9fb" stroke="#e0e3e8" stroke-width="1.5"/>
<!-- Title -->
<text x="550" y="40" text-anchor="middle" font-size="20" font-weight="bold" fill="#202124">SWIM Probe Cycle</text>
<text x="550" y="60" text-anchor="middle" font-size="12" fill="#5f6368">swim/probe.rs · swim/node.rs · swim/lifeguard.rs</text>
<!-- ============================================ -->
<!-- SWIM LANE: PROBE STATE MACHINE -->
<!-- ============================================ -->
<rect x="30" y="80" width="690" height="550" rx="6" fill="#e8f0fe" fill-opacity="0.2" stroke="#4285f4" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="100" font-size="13" font-weight="700" fill="#4285f4" letter-spacing="1">PROBE STATE MACHINE</text>
<!-- STATE: IDLE -->
<rect x="60" y="130" width="160" height="56" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="140" y="155" text-anchor="middle" font-size="14" font-weight="600" fill="#1a73e8">IDLE</text>
<text x="140" y="173" text-anchor="middle" font-size="10" fill="#5f6368">waiting for interval</text>
<!-- Arrow: IDLE → pick target -->
<line x1="220" y1="158" x2="280" y2="158" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<text x="250" y="148" text-anchor="middle" font-size="10" fill="#5f6368">tick ≥</text>
<text x="250" y="172" text-anchor="middle" font-size="10" fill="#5f6368">next_probe</text>
<!-- Diamond: has target? -->
<polygon points="340,158 395,130 450,158 395,186" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.5" filter="url(#shadow)"/>
<text x="395" y="155" text-anchor="middle" font-size="10" font-weight="600" fill="#e37400">pick</text>
<text x="395" y="167" text-anchor="middle" font-size="10" font-weight="600" fill="#e37400">target</text>
<!-- Arrow: pick target → SendPing -->
<line x1="450" y1="158" x2="510" y2="158" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- STATE: SendPing -->
<rect x="520" y="130" width="170" height="56" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="605" y="152" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">SendPing</text>
<text x="605" y="170" text-anchor="middle" font-size="10" fill="#5f6368">direct probe to target</text>
<!-- Arrow: SendPing → WaitingDirectAck -->
<line x1="605" y1="186" x2="605" y2="240" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- STATE: WaitingDirectAck -->
<rect x="490" y="250" width="230" height="56" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="605" y="273" text-anchor="middle" font-size="13" font-weight="600" fill="#e37400">WaitingDirectAck</text>
<text x="605" y="291" text-anchor="middle" font-size="10" fill="#5f6368">probe_timeout ticks</text>
<!-- Arrow: WaitingDirectAck → IDLE (ack received, green, curves left) -->
<path d="M 490 270 Q 350 270 240 210 Q 200 195 200 186"
fill="none" stroke="#34a853" stroke-width="1.8" marker-end="url(#arrow-green)"/>
<text x="340" y="248" font-size="11" fill="#34a853" font-weight="600">Ack received</text>
<!-- Diamond: timeout? -->
<polygon points="605,316 660,345 605,374 550,345" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.5" filter="url(#shadow)"/>
<text x="605" y="349" text-anchor="middle" font-size="10" font-weight="600" fill="#e37400">timeout?</text>
<!-- Arrow: WaitingDirectAck → timeout diamond -->
<line x1="605" y1="306" x2="605" y2="316" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- Arrow: timeout → SendPingReq -->
<line x1="605" y1="374" x2="605" y2="405" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<text x="638" y="395" font-size="10" fill="#ea4335">no ack</text>
<!-- STATE: SendPingReq (indirect) -->
<rect x="475" y="410" width="260" height="56" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.8" filter="url(#shadow)"/>
<text x="605" y="433" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">SendPingReq</text>
<text x="605" y="451" text-anchor="middle" font-size="10" fill="#5f6368">k relays forward to target</text>
<!-- Arrow: SendPingReq → WaitingIndirectAck -->
<line x1="605" y1="466" x2="605" y2="495" stroke="#5f6368" stroke-width="1.8" marker-end="url(#arrow-gray)"/>
<!-- STATE: WaitingIndirectAck -->
<rect x="475" y="500" width="260" height="56" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="605" y="523" text-anchor="middle" font-size="13" font-weight="600" fill="#e37400">WaitingIndirectAck</text>
<text x="605" y="541" text-anchor="middle" font-size="10" fill="#5f6368">probe_timeout ticks</text>
<!-- Arrow: WaitingIndirectAck → IDLE (indirect ack, green, curves far left) -->
<path d="M 475 520 Q 300 520 180 400 Q 120 330 120 186"
fill="none" stroke="#34a853" stroke-width="1.8" marker-end="url(#arrow-green)"/>
<text x="155" y="380" font-size="11" fill="#34a853" font-weight="600">Indirect</text>
<text x="155" y="395" font-size="11" fill="#34a853" font-weight="600">ack</text>
<!-- Arrow: WaitingIndirectAck → SUSPECT (timeout, red) -->
<path d="M 605 556 L 605 590"
fill="none" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<text x="638" y="578" font-size="10" fill="#ea4335">timeout</text>
<!-- ============================================ -->
<!-- SWIM LANE: SUSPICION -->
<!-- ============================================ -->
<rect x="30" y="640" width="1040" height="85" rx="6" fill="#fce8e6" fill-opacity="0.2" stroke="#ea4335" stroke-width="1" stroke-opacity="0.4"/>
<text x="50" y="660" font-size="13" font-weight="700" fill="#ea4335" letter-spacing="1">SUSPICION</text>
<!-- STATE: SUSPECT -->
<rect x="475" y="595" width="200" height="48" rx="12" fill="#fef7e0" stroke="#f9ab00" stroke-width="1.8" filter="url(#shadow)"/>
<text x="575" y="616" text-anchor="middle" font-size="13" font-weight="600" fill="#e37400">Suspect(target)</text>
<text x="575" y="632" text-anchor="middle" font-size="10" fill="#5f6368">start suspicion timer</text>
<!-- Arrow: SUSPECT → back to IDLE -->
<path d="M 475 610 Q 300 610 200 550 Q 100 490 80 186"
fill="none" stroke="#34a853" stroke-width="1.5" stroke-dasharray="5,3" marker-end="url(#arrow-green)"/>
<text x="190" y="545" font-size="10" fill="#34a853">refuted / ack</text>
<!-- Arrow: SUSPECT timer → DEAD -->
<line x1="675" y1="619" x2="790" y2="690" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<text x="750" y="650" font-size="10" fill="#ea4335">suspicion_timeout</text>
<!-- STATE: DEAD -->
<rect x="790" y="668" width="180" height="44" rx="12" fill="#fce8e6" stroke="#ea4335" stroke-width="1.8" filter="url(#shadow)"/>
<text x="880" y="688" text-anchor="middle" font-size="14" font-weight="600" fill="#c5221f">DeclareDead</text>
<text x="880" y="703" text-anchor="middle" font-size="10" fill="#5f6368">disseminate + cascade</text>
<!-- ============================================ -->
<!-- RIGHT PANEL: NETWORK PEERS -->
<!-- ============================================ -->
<rect x="740" y="80" width="330" height="380" rx="6" fill="#e6f4ea" fill-opacity="0.2" stroke="#34a853" stroke-width="1" stroke-opacity="0.4"/>
<text x="760" y="100" font-size="13" font-weight="700" fill="#34a853" letter-spacing="1">NETWORK</text>
<!-- Target node -->
<rect x="780" y="130" width="160" height="50" rx="12" fill="#e6f4ea" stroke="#34a853" stroke-width="1.8" filter="url(#shadow)"/>
<text x="860" y="152" text-anchor="middle" font-size="13" font-weight="600" fill="#137333">Target Node</text>
<text x="860" y="168" text-anchor="middle" font-size="10" fill="#5f6368">probe destination</text>
<!-- Arrow: SendPing → Target -->
<path d="M 690 150 L 770 148" fill="none" stroke="#4285f4" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<text x="730" y="140" text-anchor="middle" font-size="10" fill="#4285f4">Ping</text>
<!-- Arrow: Target → Ack -->
<path d="M 780 170 Q 740 200 730 265" fill="none" stroke="#34a853" stroke-width="1.5" marker-end="url(#arrow-green)"/>
<text x="746" y="218" font-size="10" fill="#34a853">Ack</text>
<!-- Relay nodes -->
<rect x="780" y="240" width="160" height="50" rx="12" fill="#e8f0fe" stroke="#4285f4" stroke-width="1.5" filter="url(#shadow)"/>
<text x="860" y="262" text-anchor="middle" font-size="13" font-weight="600" fill="#1a73e8">Relay 1..k</text>
<text x="860" y="278" text-anchor="middle" font-size="10" fill="#5f6368">indirect_probes peers</text>
<!-- Arrow: SendPingReq → Relays -->
<path d="M 735 435 Q 760 380 785 310 Q 790 290 780 280"
fill="none" stroke="#4285f4" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<text x="773" y="365" font-size="10" fill="#4285f4">PingReq</text>
<!-- Arrow: Relay → Target (forward) -->
<line x1="860" y1="240" x2="860" y2="190" stroke="#5f6368" stroke-width="1.2" stroke-dasharray="4,3" marker-end="url(#arrow-gray)"/>
<text x="890" y="218" font-size="9" fill="#5f6368">forward</text>
<!-- Lifeguard annotation -->
<rect x="780" y="340" width="260" height="80" rx="10" fill="#fff" stroke="#e0e3e8" stroke-width="1" filter="url(#shadow)"/>
<text x="800" y="360" font-size="11" font-weight="600" fill="#202124">Lifeguard Scaling</text>
<text x="800" y="378" font-size="10" fill="#5f6368">probe_interval *= (1 + health_score)</text>
<text x="800" y="394" font-size="10" fill="#5f6368">probe_timeout *= (1 + health_score)</text>
<text x="800" y="410" font-size="10" fill="#5f6368">suspicion_timeout = base * log(n) * mult</text>
<!-- ============================================ -->
<!-- Legend -->
<rect x="50" y="670" width="400" height="55" rx="8" fill="none" stroke="#e0e3e8" stroke-width="1"/>
<text x="70" y="690" font-size="12" font-weight="600" fill="#202124">Legend</text>
<line x1="70" y1="710" x2="100" y2="710" stroke="#34a853" stroke-width="1.8" marker-end="url(#arrow-green)"/>
<text x="110" y="714" font-size="10" fill="#34a853">Ack / success</text>
<line x1="200" y1="710" x2="230" y2="710" stroke="#ea4335" stroke-width="1.8" marker-end="url(#arrow-red)"/>
<text x="240" y="714" font-size="10" fill="#ea4335">Timeout / failure</text>
<line x1="350" y1="710" x2="380" y2="710" stroke="#4285f4" stroke-width="1.5" marker-end="url(#arrow-blue)"/>
<text x="390" y="714" font-size="10" fill="#4285f4">Probe msg</text>
<!-- Source ref -->
<text x="550" y="740" text-anchor="middle" font-size="10" fill="#9aa0a6">probe.rs:139-344 · node.rs:81-328 · lifeguard.rs:47-102</text>
</svg>