|
|
|
@ -11,7 +11,6 @@ |
|
|
|
|
|
|
|
|
|
|
|
body { |
|
|
|
body { |
|
|
|
margin: 0; |
|
|
|
margin: 0; |
|
|
|
height: 100vh; |
|
|
|
|
|
|
|
font-family: 'JetBrains Mono', monospace; |
|
|
|
font-family: 'JetBrains Mono', monospace; |
|
|
|
background-color: #161b22; |
|
|
|
background-color: #161b22; |
|
|
|
color: #ccc; |
|
|
|
color: #ccc; |
|
|
|
@ -80,7 +79,7 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
#canvas { |
|
|
|
#canvas { |
|
|
|
width: 100%; |
|
|
|
width: 100%; |
|
|
|
height: 100%; |
|
|
|
height: 90%; |
|
|
|
background: #121212; |
|
|
|
background: #121212; |
|
|
|
border: 2px dashed #30363d; |
|
|
|
border: 2px dashed #30363d; |
|
|
|
border-radius: 8px; |
|
|
|
border-radius: 8px; |
|
|
|
@ -300,144 +299,182 @@ |
|
|
|
height: 100%; |
|
|
|
height: 100%; |
|
|
|
overflow:hidden; |
|
|
|
overflow:hidden; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
#sd-header { |
|
|
|
|
|
|
|
width: 100%; |
|
|
|
|
|
|
|
background: none; |
|
|
|
|
|
|
|
padding: 12px 24px; |
|
|
|
|
|
|
|
font-size: 24px; |
|
|
|
|
|
|
|
font-weight: bold; |
|
|
|
|
|
|
|
color: #00ff88; |
|
|
|
|
|
|
|
border-bottom: 1px solid #333; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#page-container { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
|
|
width: 100%; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
#main-content { |
|
|
|
|
|
|
|
display: flex; |
|
|
|
|
|
|
|
flex-direction: row; |
|
|
|
|
|
|
|
height: 100%; |
|
|
|
|
|
|
|
background: radial-gradient(circle at 30% 50%, rgba(0, 255, 136, 0.1) 0%, transparent 50%), |
|
|
|
|
|
|
|
radial-gradient(circle at 70% 80%, rgba(255, 107, 53, 0.1) 0%, transparent 50%) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#challenge-container { |
|
|
|
|
|
|
|
background: #121212; |
|
|
|
|
|
|
|
margin: 12px 24px; |
|
|
|
|
|
|
|
border: 2px solid #30363d; |
|
|
|
|
|
|
|
border-radius: 8px; |
|
|
|
|
|
|
|
} |
|
|
|
</style> |
|
|
|
</style> |
|
|
|
</head> |
|
|
|
</head> |
|
|
|
<body> |
|
|
|
<body> |
|
|
|
<div id="challenge-container"> |
|
|
|
<div id="page-container"> |
|
|
|
Challenges |
|
|
|
<div id="sd-header">System Design Game</div> |
|
|
|
</div> |
|
|
|
<div id="main-content"> |
|
|
|
<div id="canvas-wrapper"> |
|
|
|
<div id="challenge-container"> |
|
|
|
<input type="radio" id="tab1" name="tab" checked> |
|
|
|
Challenges |
|
|
|
<input type="radio" id="tab2" name="tab"> |
|
|
|
</div> |
|
|
|
<input type="radio" id="tab3" name="tab"> |
|
|
|
<div id="canvas-wrapper"> |
|
|
|
|
|
|
|
<input type="radio" id="tab1" name="tab" checked> |
|
|
|
<div class="tabs"> |
|
|
|
<input type="radio" id="tab2" name="tab"> |
|
|
|
<div class="tab-labels"> |
|
|
|
<input type="radio" id="tab3" name="tab"> |
|
|
|
<label for="tab1">Requirements</label> |
|
|
|
|
|
|
|
<label for="tab2">Design</label> |
|
|
|
<div class="tabs"> |
|
|
|
<label for="tab3">Metrics</label> |
|
|
|
<div class="tab-labels"> |
|
|
|
</div> |
|
|
|
<label for="tab1">Requirements</label> |
|
|
|
|
|
|
|
<label for="tab2">Design</label> |
|
|
|
<!-- Requirements --> |
|
|
|
<label for="tab3">Metrics</label> |
|
|
|
<div id="content1" class="tab-content">This is Tab 1 content.</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- Design--> |
|
|
|
|
|
|
|
<div id="content2" class="tab-content"> |
|
|
|
|
|
|
|
<div id="sidebar"> |
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="client"> |
|
|
|
|
|
|
|
user |
|
|
|
|
|
|
|
<span class="tooltip">simulates user traffic</span> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="loadbalancer"> |
|
|
|
<!-- Requirements --> |
|
|
|
load balancer |
|
|
|
<div id="content1" class="tab-content">This is Tab 1 content.</div> |
|
|
|
<span class="tooltip">cost: $5/mo<br>distributes traffic evenly<br>latency: 5 ms</span> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="webserversmall"> |
|
|
|
<!-- Design--> |
|
|
|
web server (small) |
|
|
|
<div id="content2" class="tab-content"> |
|
|
|
<span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>base latency: 50 ms</span> |
|
|
|
<div id="sidebar"> |
|
|
|
</div> |
|
|
|
<div class="component-icon" draggable="true" data-type="user"> |
|
|
|
|
|
|
|
user |
|
|
|
<div class="component-icon" draggable="true" data-type="webservermedium"> |
|
|
|
<span class="tooltip">simulates user traffic</span> |
|
|
|
web server (medium) |
|
|
|
</div> |
|
|
|
<span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>base latency: 40 ms</span> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="database"> |
|
|
|
<div class="component-icon" draggable="true" data-type="loadbalancer"> |
|
|
|
database |
|
|
|
load balancer |
|
|
|
<span class="tooltip">cost: $20/mo<br>read capacity: 150 rps<br>base latency: 80 ms<br>supports replication</span> |
|
|
|
<span class="tooltip">cost: $5/mo<br>distributes traffic evenly<br>latency: 5 ms</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="cachestandard"> |
|
|
|
<div class="component-icon" draggable="true" data-type="webserver (small)"> |
|
|
|
cache (standard) |
|
|
|
web server (small) |
|
|
|
<span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>latency: 5 ms<br>80% hit rate with 1hr ttl</span> |
|
|
|
<span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>base latency: 50 ms</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="cachelarge"> |
|
|
|
<div class="component-icon" draggable="true" data-type="webserver (medium)"> |
|
|
|
cache (large) |
|
|
|
web server (medium) |
|
|
|
<span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>latency: 5 ms<br>higher hit rate for large datasets</span> |
|
|
|
<span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>base latency: 40 ms</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="messagequeue"> |
|
|
|
<div class="component-icon" draggable="true" data-type="database"> |
|
|
|
message queue |
|
|
|
database |
|
|
|
<span class="tooltip">cost: $15/mo<br>decouples components<br>useful for batching writes</span> |
|
|
|
<span class="tooltip">cost: $20/mo<br>read capacity: 150 rps<br>base latency: 80 ms<br>supports replication</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="cdn"> |
|
|
|
<div class="component-icon" draggable="true" data-type="cachestandard"> |
|
|
|
cdn/edge cache |
|
|
|
cache (standard) |
|
|
|
<span class="tooltip">cost: $0.03/gb<br>improves global latency<br>caches static content</span> |
|
|
|
<span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>latency: 5 ms<br>80% hit rate with 1hr ttl</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="microservice"> |
|
|
|
<div class="component-icon" draggable="true" data-type="cachelarge"> |
|
|
|
microservice node |
|
|
|
cache (large) |
|
|
|
<span class="tooltip">cost: $10/mo<br>stateless container<br>use for modular logic</span> |
|
|
|
<span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>latency: 5 ms<br>higher hit rate for large datasets</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="datapipeline"> |
|
|
|
<div class="component-icon" draggable="true" data-type="messagequeue"> |
|
|
|
data pipeline |
|
|
|
message queue |
|
|
|
<span class="tooltip">cost: $25/mo<br>stream or batch processing<br>used for analytics / etl</span> |
|
|
|
<span class="tooltip">cost: $15/mo<br>decouples components<br>useful for batching writes</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="monitoring"> |
|
|
|
<div class="component-icon" draggable="true" data-type="cdn"> |
|
|
|
monitoring/alerting |
|
|
|
cdn/edge cache |
|
|
|
<span class="tooltip">cost: $5/mo<br>health checks + logs<br>alerts on failures</span> |
|
|
|
<span class="tooltip">cost: $0.03/gb<br>improves global latency<br>caches static content</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="thirdparty"> |
|
|
|
<div class="component-icon" draggable="true" data-type="microservice"> |
|
|
|
third-party service |
|
|
|
microservice node |
|
|
|
<span class="tooltip">external apis<br>latency + cost vary<br>examples: payment, email, search</span> |
|
|
|
<span class="tooltip">cost: $10/mo<br>stateless container<br>use for modular logic</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div id="arrow-tool">arrow tool</div> |
|
|
|
<div class="component-icon" draggable="true" data-type="datapipeline"> |
|
|
|
<button id="run-button" disabled>run simulation</button> |
|
|
|
data pipeline |
|
|
|
</div> |
|
|
|
<span class="tooltip">cost: $25/mo<br>stream or batch processing<br>used for analytics / etl</span> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div id="canvas-container"> |
|
|
|
<div class="component-icon" draggable="true" data-type="monitoring"> |
|
|
|
<div id="info-panel"> |
|
|
|
monitoring/alerting |
|
|
|
<div id="constraints-panel"> |
|
|
|
<span class="tooltip">cost: $5/mo<br>health checks + logs<br>alerts on failures</span> |
|
|
|
<div class="panel-title">level constraints</div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">🎯 target rps:</span> <span id="constraint-rps">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">⏱️ max p95 latency:</span> <span id="constraint-latency">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">💸 max cost:</span> <span id="constraint-cost">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">🔒 availability:</span> <span id="constraint-availability">–</span></div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div id="score-panel"> |
|
|
|
<div class="component-icon" draggable="true" data-type="thirdparty"> |
|
|
|
<div class="panel-title">simulation results</div> |
|
|
|
third-party service |
|
|
|
<div class="panel-metric"><span class="label">✅ cost:</span> <span id="score-cost">–</span></div> |
|
|
|
<span class="tooltip">external apis<br>latency + cost vary<br>examples: payment, email, search</span> |
|
|
|
<div class="panel-metric"><span class="label">⚡ p95 latency:</span> <span id="score-p95">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">📈 achieved rps:</span> <span id="score-rps">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">🛡️ availability:</span> <span id="score-availability">–</span></div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div id="arrow-tool">arrow tool</div> |
|
|
|
|
|
|
|
<button id="run-button" disabled>Test Design</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<svg id="canvas"> |
|
|
|
<div id="canvas-container"> |
|
|
|
<defs> |
|
|
|
<div id="info-panel"> |
|
|
|
<marker id="arrowhead" markerwidth="10" markerheight="7" refx="10" refy="3.5" orient="auto"> |
|
|
|
<div id="constraints-panel"> |
|
|
|
<polygon points="0 0, 10 3.5, 0 7" fill="#333" /> |
|
|
|
<div class="panel-title">level constraints</div> |
|
|
|
</marker> |
|
|
|
<div class="panel-metric"><span class="label">🎯 target rps:</span> <span id="constraint-rps">–</span></div> |
|
|
|
</defs> |
|
|
|
<div class="panel-metric"><span class="label">⏱️ max p95 latency:</span> <span id="constraint-latency">–</span></div> |
|
|
|
</svg> |
|
|
|
<div class="panel-metric"><span class="label">💸 max cost:</span> <span id="constraint-cost">–</span></div> |
|
|
|
<div id="node-props-panel"> |
|
|
|
<div class="panel-metric"><span class="label">🔒 availability:</span> <span id="constraint-availability">–</span></div> |
|
|
|
<h3>node properties</h3> |
|
|
|
</div> |
|
|
|
<div id="label-group"> |
|
|
|
|
|
|
|
<label>label:<input type="text" name="label" /></label> |
|
|
|
<div id="score-panel"> |
|
|
|
|
|
|
|
<div class="panel-title">simulation results</div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">✅ cost:</span> <span id="score-cost">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">⚡ p95 latency:</span> <span id="score-p95">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">📈 achieved rps:</span> <span id="score-rps">–</span></div> |
|
|
|
|
|
|
|
<div class="panel-metric"><span class="label">🛡️ availability:</span> <span id="score-availability">–</span></div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div id="db-group" class="prop-group"> |
|
|
|
|
|
|
|
<label>replication factor:<input type="number" name="replication" min="1" step="1" /></label> |
|
|
|
<svg id="canvas"> |
|
|
|
|
|
|
|
<defs> |
|
|
|
|
|
|
|
<marker id="arrowhead" markerwidth="10" markerheight="7" refx="10" refy="3.5" orient="auto"> |
|
|
|
|
|
|
|
<polygon points="0 0, 10 3.5, 0 7" fill="#333" /> |
|
|
|
|
|
|
|
</marker> |
|
|
|
|
|
|
|
</defs> |
|
|
|
|
|
|
|
</svg> |
|
|
|
|
|
|
|
<div id="node-props-panel"> |
|
|
|
|
|
|
|
<h3>node properties</h3> |
|
|
|
|
|
|
|
<div id="label-group"> |
|
|
|
|
|
|
|
<label>label:<input type="text" name="label" /></label> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="db-group" class="prop-group"> |
|
|
|
|
|
|
|
<label>replication factor:<input type="number" name="replication" min="1" step="1" /></label> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="cache-group" class="prop-group"> |
|
|
|
|
|
|
|
<label>cache ttl (secs):<input type="number" name="cachettl" min="0" step="60" /></label> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<button id="node-props-save" disabled>save</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div id="cache-group" class="prop-group"> |
|
|
|
<div id="bottom-panel"> |
|
|
|
<label>cache ttl (secs):<input type="number" name="cachettl" min="0" step="60" /></label> |
|
|
|
Hi |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<button id="node-props-save" disabled>save</button> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Metrics--> |
|
|
|
|
|
|
|
<div id="content3" class="tab-content">This is Tab 3 content.</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- Metrics--> |
|
|
|
|
|
|
|
<div id="content3" class="tab-content">This is Tab 3 content.</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|