|
|
|
@ -94,20 +94,21 @@ |
|
|
|
stroke-width: 2; |
|
|
|
stroke-width: 2; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#run-button { |
|
|
|
/* === PANELS === */ |
|
|
|
margin-top: auto; |
|
|
|
#info-panel { |
|
|
|
padding: 10px; |
|
|
|
position: absolute; |
|
|
|
background-color: #007acc; |
|
|
|
top: 12px; |
|
|
|
color: white; |
|
|
|
right: 12px; |
|
|
|
border: none; |
|
|
|
background: var(--color-bg-dark); |
|
|
|
border-radius: 4px; |
|
|
|
color: var(--color-text-primary); |
|
|
|
cursor: pointer; |
|
|
|
padding: 1rem; |
|
|
|
|
|
|
|
border-radius: var(--radius-large); |
|
|
|
|
|
|
|
font-family: monospace; |
|
|
|
font-size: 14px; |
|
|
|
font-size: 14px; |
|
|
|
} |
|
|
|
min-width: 220px; |
|
|
|
|
|
|
|
z-index: 10; |
|
|
|
#run-button:disabled { |
|
|
|
border: 1px solid var(--color-text-dark); |
|
|
|
background-color: #555; |
|
|
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.3); |
|
|
|
cursor: not-allowed; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
#node-props-panel { |
|
|
|
#node-props-panel { |
|
|
|
@ -334,146 +335,172 @@ |
|
|
|
<div id="page-container"> |
|
|
|
<div id="page-container"> |
|
|
|
<div id="sd-header">System Design Game</div> |
|
|
|
<div id="sd-header">System Design Game</div> |
|
|
|
<div id="main-content"> |
|
|
|
<div id="main-content"> |
|
|
|
<div id="challenge-container"> |
|
|
|
<div id="challenge-container"> |
|
|
|
Challenges |
|
|
|
<h2 class="sidebar-title">Challenges</h2> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="canvas-wrapper"> |
|
|
|
<ul class="challenge-list"> |
|
|
|
<input type="radio" id="tab1" name="tab" checked> |
|
|
|
<li class="challenge-item active"> |
|
|
|
<input type="radio" id="tab2" name="tab"> |
|
|
|
<div class="challenge-name">Url Shortener</div> |
|
|
|
<input type="radio" id="tab3" name="tab"> |
|
|
|
<div class="challenge-difficulty easy">Easy</div> |
|
|
|
|
|
|
|
</li> |
|
|
|
<div class="tabs"> |
|
|
|
<li class="challenge-item"> |
|
|
|
<div class="tab-labels"> |
|
|
|
<div class="challenge-name">Url Shortener</div> |
|
|
|
<label for="tab1">Requirements</label> |
|
|
|
<div class="challenge-difficulty easy">Easy</div> |
|
|
|
<label for="tab2">Design</label> |
|
|
|
</li> |
|
|
|
<label for="tab3">Metrics</label> |
|
|
|
<li class="challenge-item"> |
|
|
|
</div> |
|
|
|
<div class="challenge-name">Url Shortener</div> |
|
|
|
|
|
|
|
<div class="challenge-difficulty medium">Medium</div> |
|
|
|
<!-- Requirements --> |
|
|
|
</li> |
|
|
|
<div id="content1" class="tab-content">This is Tab 1 content.</div> |
|
|
|
<li class="challenge-item"> |
|
|
|
|
|
|
|
<div class="challenge-name">Something hard</div> |
|
|
|
<!-- Design--> |
|
|
|
<div class="challenge-difficulty hard">Hard</div> |
|
|
|
<div id="content2" class="tab-content"> |
|
|
|
</li> |
|
|
|
<div id="sidebar"> |
|
|
|
</ul> |
|
|
|
<div class="component-icon" draggable="true" data-type="user"> |
|
|
|
</div> |
|
|
|
user |
|
|
|
<div id="canvas-wrapper"> |
|
|
|
<span class="tooltip">simulates user traffic</span> |
|
|
|
<input type="radio" id="tab1" name="tab" checked> |
|
|
|
</div> |
|
|
|
<input type="radio" id="tab2" name="tab"> |
|
|
|
|
|
|
|
<input type="radio" id="tab3" name="tab"> |
|
|
|
<div class="component-icon" draggable="true" data-type="loadbalancer"> |
|
|
|
|
|
|
|
load balancer |
|
|
|
<div class="tabs"> |
|
|
|
<span class="tooltip">cost: $5/mo<br>distributes traffic evenly<br>latency: 5 ms</span> |
|
|
|
<div class="tab-labels"> |
|
|
|
</div> |
|
|
|
<label for="tab1">Requirements</label> |
|
|
|
|
|
|
|
<label for="tab2">Design</label> |
|
|
|
|
|
|
|
<label for="tab3">Metrics</label> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="webserver (small)"> |
|
|
|
<!-- Requirements --> |
|
|
|
web server (small) |
|
|
|
<div id="content1" class="tab-content"> |
|
|
|
<span class="tooltip">cost: $10/mo<br>capacity: 100 rps<br>base latency: 50 ms</span> |
|
|
|
<div class="requirements-section"> |
|
|
|
</div> |
|
|
|
<h3>Functional Requirements</h3> |
|
|
|
|
|
|
|
<ul class="requirements-list"> |
|
|
|
|
|
|
|
<li class="requirement-item">Something</li> |
|
|
|
|
|
|
|
<li class="requirement-item">Something else</li> |
|
|
|
|
|
|
|
</ul> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div class="component-icon" draggable="true" data-type="webserver (medium)"> |
|
|
|
<!-- Design--> |
|
|
|
web server (medium) |
|
|
|
<div id="content2" class="tab-content"> |
|
|
|
<span class="tooltip">cost: $20/mo<br>capacity: 200 rps<br>base latency: 40 ms</span> |
|
|
|
<div id="sidebar"> |
|
|
|
</div> |
|
|
|
<div class="component-icon" draggable="true" data-type="user"> |
|
|
|
|
|
|
|
user |
|
|
|
|
|
|
|
<span class="tooltip">simulates user traffic</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>Test Design</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> |
|
|
|
</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> |
|
|
|
<button id="run-button" disabled>Test Design</button> |
|
|
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<button id="node-props-save" disabled>save</button> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
<div id="bottom-panel"> |
|
|
|
|
|
|
|
Hi |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Metrics--> |
|
|
|
|
|
|
|
<div id="content3" class="tab-content">This is Tab 3 content.</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<!-- Metrics--> |
|
|
|
|
|
|
|
<div id="content3" class="tab-content">This is Tab 3 content.</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|