claude one shot lol

This commit is contained in:
Harivansh Rathi 2026-01-06 23:14:25 +05:30
parent 7964b78a3a
commit 1ab3058cca
27 changed files with 5218 additions and 0 deletions

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
node_modules
.svelte-kit
build
package-lock.json
yarn.lock
.env
.env.*
!.env.example
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

32
package.json Normal file
View file

@ -0,0 +1,32 @@
{
"name": "system-design-explorer",
"version": "0.0.1",
"private": true,
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^6.0.0",
"@sveltejs/kit": "^2.21.0",
"@sveltejs/vite-plugin-svelte": "^5.0.0",
"@types/d3": "^7.4.3",
"autoprefixer": "^10.4.20",
"postcss": "^8.5.1",
"svelte": "^5.17.4",
"svelte-check": "^4.0.0",
"tailwindcss": "^3.4.17",
"typescript": "^5.0.0",
"vite": "^6.0.0"
},
"dependencies": {
"@xyflow/svelte": "^0.1.26",
"d3": "^7.9.0",
"gsap": "^3.12.7",
"lucide-svelte": "^0.469.0"
},
"type": "module"
}

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};

111
src/app.css Normal file
View file

@ -0,0 +1,111 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
* {
@apply border-surface-700;
}
body {
@apply antialiased;
}
::-webkit-scrollbar {
@apply w-2 h-2;
}
::-webkit-scrollbar-track {
@apply bg-surface-900;
}
::-webkit-scrollbar-thumb {
@apply bg-surface-700 rounded-full;
}
::-webkit-scrollbar-thumb:hover {
@apply bg-surface-600;
}
}
@layer components {
.card {
@apply bg-surface-900 border border-surface-800 rounded-xl p-6;
}
.card-hover {
@apply card hover:border-surface-600 hover:bg-surface-800/50 transition-all duration-200 cursor-pointer;
}
.btn {
@apply px-4 py-2 rounded-lg font-medium transition-all duration-200;
}
.btn-primary {
@apply btn bg-blue-600 hover:bg-blue-500 text-white;
}
.btn-secondary {
@apply btn bg-surface-800 hover:bg-surface-700 text-surface-200;
}
.section-title {
@apply text-2xl font-bold text-surface-100 mb-4;
}
.section-subtitle {
@apply text-surface-400 mb-6;
}
.tag {
@apply inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium;
}
.tag-aws {
@apply tag bg-aws-orange/20 text-aws-orange;
}
.tag-gcp {
@apply tag bg-gcp-blue/20 text-gcp-blue;
}
.tag-concept {
@apply tag bg-purple-500/20 text-purple-400;
}
/* Animated flow lines for diagrams */
.flow-line {
stroke-dasharray: 5 5;
animation: flowAnimation 1s linear infinite;
}
@keyframes flowAnimation {
to {
stroke-dashoffset: -10;
}
}
/* Node styles for diagrams */
.diagram-node {
@apply bg-surface-800 border-2 border-surface-600 rounded-xl p-4 shadow-lg;
@apply hover:border-blue-500 hover:shadow-blue-500/20 transition-all duration-200;
}
.diagram-node-aws {
@apply diagram-node border-aws-orange/50 hover:border-aws-orange;
}
.diagram-node-gcp {
@apply diagram-node border-gcp-blue/50 hover:border-gcp-blue;
}
}
@layer utilities {
.text-gradient {
@apply bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-400;
}
.glass {
@apply bg-surface-900/80 backdrop-blur-lg;
}
}

15
src/app.html Normal file
View file

@ -0,0 +1,15 @@
<!doctype html>
<html lang="en" class="dark">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover" class="bg-surface-950 text-surface-100">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

View file

@ -0,0 +1,163 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import { slide } from 'svelte/transition';
interface ComparisonItem {
feature: string;
description?: string;
values: Record<string, string | boolean>;
}
interface Option {
id: string;
name: string;
color: string;
icon: string;
whenToUse: string[];
whenNotToUse: string[];
realWorldExample?: string;
}
interface Props {
title: string;
subtitle?: string;
options: Option[];
features: ComparisonItem[];
}
let { title, subtitle = '', options, features }: Props = $props();
let selectedOption = $state<string | null>(null);
function getIcon(iconName: string) {
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Box;
}
function getCellContent(value: string | boolean) {
if (typeof value === 'boolean') {
return value
? { type: 'check', class: 'text-green-400' }
: { type: 'x', class: 'text-red-400' };
}
return { type: 'text', value, class: 'text-surface-300' };
}
</script>
<div class="space-y-6">
<!-- Header -->
<div>
<h2 class="section-title">{title}</h2>
{#if subtitle}
<p class="section-subtitle">{subtitle}</p>
{/if}
</div>
<!-- Comparison Table -->
<div class="overflow-x-auto">
<table class="w-full border-collapse">
<thead>
<tr>
<th class="text-left p-4 bg-surface-900 border-b border-surface-700 text-surface-400 font-medium">
Feature
</th>
{#each options as option}
{@const Icon = getIcon(option.icon)}
<th
class="p-4 bg-surface-900 border-b border-surface-700 text-center cursor-pointer hover:bg-surface-800 transition-colors"
onclick={() => selectedOption = selectedOption === option.id ? null : option.id}
>
<div class="flex flex-col items-center gap-2">
<div
class="w-10 h-10 rounded-lg flex items-center justify-center"
style="background-color: {option.color}20; color: {option.color}"
>
<Icon class="w-6 h-6" />
</div>
<span class="font-medium text-surface-100">{option.name}</span>
</div>
</th>
{/each}
</tr>
</thead>
<tbody>
{#each features as feature, i}
<tr class="border-b border-surface-800 hover:bg-surface-900/50">
<td class="p-4">
<div class="font-medium text-surface-200">{feature.feature}</div>
{#if feature.description}
<div class="text-sm text-surface-500 mt-1">{feature.description}</div>
{/if}
</td>
{#each options as option}
{@const cell = getCellContent(feature.values[option.id])}
<td class="p-4 text-center {cell.class}">
{#if cell.type === 'check'}
<Icons.Check class="w-5 h-5 mx-auto" />
{:else if cell.type === 'x'}
<Icons.X class="w-5 h-5 mx-auto" />
{:else}
<span class="text-sm">{cell.value}</span>
{/if}
</td>
{/each}
</tr>
{/each}
</tbody>
</table>
</div>
<!-- When to Use / When Not to Use Panels -->
{#if selectedOption}
{@const selected = options.find(o => o.id === selectedOption)}
{#if selected}
<div transition:slide={{ duration: 200 }} class="grid md:grid-cols-2 gap-4">
<!-- When to Use -->
<div class="card border-green-500/30">
<div class="flex items-center gap-2 mb-4">
<Icons.CheckCircle class="w-5 h-5 text-green-400" />
<h3 class="font-semibold text-green-400">When to Use {selected.name}</h3>
</div>
<ul class="space-y-2">
{#each selected.whenToUse as reason}
<li class="flex items-start gap-2 text-sm text-surface-300">
<Icons.Check class="w-4 h-4 text-green-400 mt-0.5 flex-shrink-0" />
<span>{reason}</span>
</li>
{/each}
</ul>
</div>
<!-- When NOT to Use -->
<div class="card border-red-500/30">
<div class="flex items-center gap-2 mb-4">
<Icons.XCircle class="w-5 h-5 text-red-400" />
<h3 class="font-semibold text-red-400">When NOT to Use {selected.name}</h3>
</div>
<ul class="space-y-2">
{#each selected.whenNotToUse as reason}
<li class="flex items-start gap-2 text-sm text-surface-300">
<Icons.X class="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
<span>{reason}</span>
</li>
{/each}
</ul>
</div>
<!-- Real World Example -->
{#if selected.realWorldExample}
<div class="card md:col-span-2 border-blue-500/30">
<div class="flex items-center gap-2 mb-3">
<Icons.Lightbulb class="w-5 h-5 text-blue-400" />
<h3 class="font-semibold text-blue-400">Real World Example</h3>
</div>
<p class="text-sm text-surface-300">{selected.realWorldExample}</p>
</div>
{/if}
</div>
{/if}
{:else}
<div class="text-center py-6 text-surface-500">
<Icons.MousePointerClick class="w-8 h-8 mx-auto mb-2 opacity-50" />
<p class="text-sm">Click on a column header to see when to use each option</p>
</div>
{/if}
</div>

View file

@ -0,0 +1,207 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import { fly, fade } from 'svelte/transition';
interface DecisionNode {
id: string;
type: 'question' | 'answer';
text: string;
description?: string;
icon?: string;
yes?: string; // id of next node if yes
no?: string; // id of next node if no
recommendation?: string;
reasoning?: string;
alternatives?: string[];
}
interface Props {
title: string;
subtitle?: string;
nodes: DecisionNode[];
startNode: string;
}
let { title, subtitle = '', nodes, startNode }: Props = $props();
let path = $state<string[]>([startNode]);
let answers = $state<Record<string, boolean>>({});
function getNode(id: string): DecisionNode | undefined {
return nodes.find(n => n.id === id);
}
function getIcon(iconName?: string) {
if (!iconName) return Icons.HelpCircle;
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.HelpCircle;
}
function answer(nodeId: string, isYes: boolean) {
const node = getNode(nodeId);
if (!node) return;
answers[nodeId] = isYes;
const nextId = isYes ? node.yes : node.no;
if (nextId) {
path = [...path, nextId];
}
}
function goBack() {
if (path.length > 1) {
const lastNodeId = path[path.length - 1];
delete answers[path[path.length - 2]];
path = path.slice(0, -1);
}
}
function reset() {
path = [startNode];
answers = {};
}
const currentNode = $derived(getNode(path[path.length - 1]));
const isAtEnd = $derived(currentNode?.type === 'answer');
</script>
<div class="space-y-6">
<!-- Header -->
<div>
<h2 class="section-title">{title}</h2>
{#if subtitle}
<p class="section-subtitle">{subtitle}</p>
{/if}
</div>
<!-- Progress Trail -->
<div class="flex items-center gap-2 flex-wrap">
{#each path as nodeId, i}
{@const node = getNode(nodeId)}
{@const isLast = i === path.length - 1}
{#if node}
<div class="flex items-center gap-2">
{#if i > 0}
<Icons.ChevronRight class="w-4 h-4 text-surface-600" />
{/if}
<span
class="px-3 py-1 rounded-full text-sm transition-colors"
class:bg-blue-600={isLast}
class:text-white={isLast}
class:bg-surface-800={!isLast}
class:text-surface-400={!isLast}
>
{#if node.type === 'question'}
{answers[nodeId] !== undefined ? (answers[nodeId] ? 'Yes' : 'No') : '?'}
{:else}
{node.text.substring(0, 20)}...
{/if}
</span>
</div>
{/if}
{/each}
</div>
<!-- Current Decision -->
{#if currentNode}
<div
class="card max-w-2xl mx-auto"
in:fly={{ y: 20, duration: 300 }}
>
{#if currentNode.type === 'question'}
<!-- Question Node -->
<div class="text-center space-y-6">
<div class="w-16 h-16 mx-auto rounded-full bg-blue-500/20 flex items-center justify-center">
<Icons.HelpCircle class="w-8 h-8 text-blue-400" />
</div>
<div>
<h3 class="text-xl font-semibold text-surface-100 mb-2">{currentNode.text}</h3>
{#if currentNode.description}
<p class="text-surface-400">{currentNode.description}</p>
{/if}
</div>
<div class="flex justify-center gap-4">
<button
onclick={() => answer(currentNode.id, true)}
class="flex items-center gap-2 px-6 py-3 rounded-lg bg-green-600 hover:bg-green-500 text-white font-medium transition-colors"
>
<Icons.Check class="w-5 h-5" />
Yes
</button>
<button
onclick={() => answer(currentNode.id, false)}
class="flex items-center gap-2 px-6 py-3 rounded-lg bg-red-600 hover:bg-red-500 text-white font-medium transition-colors"
>
<Icons.X class="w-5 h-5" />
No
</button>
</div>
</div>
{:else}
<!-- Answer Node -->
{@const Icon = getIcon(currentNode.icon)}
<div class="text-center space-y-6">
<div class="w-20 h-20 mx-auto rounded-full bg-green-500/20 flex items-center justify-center">
<Icon class="w-10 h-10 text-green-400" />
</div>
<div>
<span class="tag tag-concept mb-2">Recommendation</span>
<h3 class="text-2xl font-bold text-surface-100 mt-2">{currentNode.text}</h3>
</div>
{#if currentNode.reasoning}
<div class="text-left bg-surface-800 rounded-lg p-4 border border-surface-700">
<div class="flex items-center gap-2 mb-2">
<Icons.Lightbulb class="w-5 h-5 text-yellow-400" />
<span class="font-medium text-yellow-400">Why this choice?</span>
</div>
<p class="text-surface-300 text-sm">{currentNode.reasoning}</p>
</div>
{/if}
{#if currentNode.alternatives && currentNode.alternatives.length > 0}
<div class="text-left bg-surface-800 rounded-lg p-4 border border-surface-700">
<div class="flex items-center gap-2 mb-2">
<Icons.GitBranch class="w-5 h-5 text-purple-400" />
<span class="font-medium text-purple-400">Alternatives to consider</span>
</div>
<ul class="space-y-1">
{#each currentNode.alternatives as alt}
<li class="text-surface-400 text-sm flex items-start gap-2">
<Icons.ChevronRight class="w-4 h-4 mt-0.5 flex-shrink-0" />
<span>{alt}</span>
</li>
{/each}
</ul>
</div>
{/if}
</div>
{/if}
</div>
{/if}
<!-- Controls -->
<div class="flex justify-center gap-4">
{#if path.length > 1}
<button
onclick={goBack}
class="btn-secondary flex items-center gap-2"
>
<Icons.ArrowLeft class="w-4 h-4" />
Go Back
</button>
{/if}
{#if isAtEnd}
<button
onclick={reset}
class="btn-primary flex items-center gap-2"
>
<Icons.RotateCcw class="w-4 h-4" />
Start Over
</button>
{/if}
</div>
</div>

View file

@ -0,0 +1,223 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import { fly, fade } from 'svelte/transition';
interface WalkthroughStep {
id: string;
title: string;
description: string;
icon?: string;
highlight?: string; // CSS selector or id to highlight
details?: string;
tip?: string;
}
interface Props {
title: string;
subtitle?: string;
steps: WalkthroughStep[];
autoPlay?: boolean;
autoPlayInterval?: number;
}
let {
title,
subtitle = '',
steps,
autoPlay = false,
autoPlayInterval = 3000
}: Props = $props();
let currentStep = $state(0);
let isPlaying = $state(autoPlay);
let intervalId: ReturnType<typeof setInterval> | null = null;
function getIcon(iconName?: string) {
if (!iconName) return Icons.Circle;
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Circle;
}
function nextStep() {
if (currentStep < steps.length - 1) {
currentStep++;
} else {
currentStep = 0;
}
}
function prevStep() {
if (currentStep > 0) {
currentStep--;
}
}
function goToStep(index: number) {
currentStep = index;
}
function togglePlay() {
isPlaying = !isPlaying;
}
$effect(() => {
if (isPlaying) {
intervalId = setInterval(nextStep, autoPlayInterval);
} else if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
return () => {
if (intervalId) clearInterval(intervalId);
};
});
const step = $derived(steps[currentStep]);
const StepIcon = $derived(getIcon(step?.icon));
const progress = $derived(((currentStep + 1) / steps.length) * 100);
</script>
<div class="space-y-6">
<!-- Header -->
<div class="flex items-start justify-between">
<div>
<h2 class="section-title">{title}</h2>
{#if subtitle}
<p class="section-subtitle">{subtitle}</p>
{/if}
</div>
<div class="flex items-center gap-2">
<button
onclick={togglePlay}
class="btn-secondary flex items-center gap-2"
>
{#if isPlaying}
<Icons.Pause class="w-4 h-4" />
Pause
{:else}
<Icons.Play class="w-4 h-4" />
Auto Play
{/if}
</button>
</div>
</div>
<!-- Progress Bar -->
<div class="relative">
<div class="h-1 bg-surface-800 rounded-full overflow-hidden">
<div
class="h-full bg-gradient-to-r from-blue-500 to-purple-500 transition-all duration-300"
style="width: {progress}%"
></div>
</div>
<div class="flex justify-between mt-2">
<span class="text-xs text-surface-500">Step {currentStep + 1} of {steps.length}</span>
<span class="text-xs text-surface-500">{Math.round(progress)}% complete</span>
</div>
</div>
<!-- Step Indicators -->
<div class="flex justify-center gap-2 flex-wrap">
{#each steps as s, i}
{@const Icon = getIcon(s.icon)}
<button
onclick={() => goToStep(i)}
class="flex items-center gap-2 px-3 py-2 rounded-lg transition-all duration-200"
class:bg-blue-600={i === currentStep}
class:text-white={i === currentStep}
class:bg-surface-800={i !== currentStep && i <= currentStep}
class:text-surface-300={i !== currentStep && i <= currentStep}
class:bg-surface-900={i > currentStep}
class:text-surface-600={i > currentStep}
>
<Icon class="w-4 h-4" />
<span class="text-sm font-medium hidden md:inline">{i + 1}</span>
</button>
{/each}
</div>
<!-- Current Step Content -->
{#key currentStep}
<div
class="card"
in:fly={{ x: 50, duration: 300 }}
out:fade={{ duration: 150 }}
>
<div class="flex items-start gap-6">
<!-- Icon -->
<div class="flex-shrink-0">
<div class="w-16 h-16 rounded-xl bg-gradient-to-br from-blue-500/20 to-purple-500/20 flex items-center justify-center">
<StepIcon class="w-8 h-8 text-blue-400" />
</div>
</div>
<!-- Content -->
<div class="flex-1 space-y-4">
<div>
<span class="tag tag-concept mb-2">Step {currentStep + 1}</span>
<h3 class="text-xl font-semibold text-surface-100 mt-2">{step.title}</h3>
</div>
<p class="text-surface-300">{step.description}</p>
{#if step.details}
<div class="bg-surface-800 rounded-lg p-4 border border-surface-700">
<div class="flex items-center gap-2 mb-2">
<Icons.Info class="w-4 h-4 text-blue-400" />
<span class="text-sm font-medium text-blue-400">Technical Details</span>
</div>
<p class="text-sm text-surface-400">{step.details}</p>
</div>
{/if}
{#if step.tip}
<div class="bg-yellow-500/10 rounded-lg p-4 border border-yellow-500/30">
<div class="flex items-center gap-2 mb-2">
<Icons.Lightbulb class="w-4 h-4 text-yellow-400" />
<span class="text-sm font-medium text-yellow-400">Pro Tip</span>
</div>
<p class="text-sm text-surface-300">{step.tip}</p>
</div>
{/if}
</div>
</div>
</div>
{/key}
<!-- Navigation -->
<div class="flex justify-between items-center">
<button
onclick={prevStep}
disabled={currentStep === 0}
class="btn-secondary flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
>
<Icons.ArrowLeft class="w-4 h-4" />
Previous
</button>
<div class="flex gap-1">
{#each steps as _, i}
<button
onclick={() => goToStep(i)}
class="w-2 h-2 rounded-full transition-all duration-200"
class:bg-blue-500={i === currentStep}
class:w-4={i === currentStep}
class:bg-surface-600={i !== currentStep}
></button>
{/each}
</div>
<button
onclick={nextStep}
class="btn-primary flex items-center gap-2"
>
{#if currentStep === steps.length - 1}
Start Over
<Icons.RotateCcw class="w-4 h-4" />
{:else}
Next
<Icons.ArrowRight class="w-4 h-4" />
{/if}
</button>
</div>
</div>

View file

@ -0,0 +1,90 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
interface Props {
id: string;
label: string;
description?: string;
icon?: string;
type?: 'aws' | 'gcp' | 'concept' | 'default';
selected?: boolean;
onclick?: () => void;
}
let {
id,
label,
description = '',
icon = 'Box',
type = 'default',
selected = false,
onclick
}: Props = $props();
function getIcon(iconName: string) {
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Box;
}
const Icon = $derived(getIcon(icon));
const borderColor = $derived({
aws: 'border-aws-orange',
gcp: 'border-gcp-blue',
concept: 'border-purple-500',
default: 'border-surface-600'
}[type]);
const bgColor = $derived({
aws: 'bg-aws-orange/10',
gcp: 'bg-gcp-blue/10',
concept: 'bg-purple-500/10',
default: 'bg-surface-800'
}[type]);
const iconColor = $derived({
aws: 'text-aws-orange',
gcp: 'text-gcp-blue',
concept: 'text-purple-400',
default: 'text-surface-300'
}[type]);
</script>
<button
{onclick}
class="relative group cursor-pointer transition-all duration-200 transform hover:scale-105"
class:ring-2={selected}
class:ring-blue-500={selected}
class:ring-offset-2={selected}
class:ring-offset-surface-950={selected}
>
<div
class="flex flex-col items-center p-4 rounded-xl border-2 min-w-[120px] {borderColor} {bgColor}"
>
<!-- Icon -->
<div class="w-12 h-12 rounded-lg flex items-center justify-center mb-2 {iconColor}">
<Icon class="w-8 h-8" />
</div>
<!-- Label -->
<span class="text-sm font-medium text-surface-100 text-center">{label}</span>
<!-- Description (on hover) -->
{#if description}
<div class="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 px-3 py-2 bg-surface-800 rounded-lg text-xs text-surface-300 whitespace-nowrap opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10 border border-surface-700 shadow-lg">
{description}
<div class="absolute top-full left-1/2 -translate-x-1/2 -mt-1 w-2 h-2 bg-surface-800 border-r border-b border-surface-700 transform rotate-45"></div>
</div>
{/if}
</div>
<!-- Type badge -->
{#if type !== 'default'}
<span class="absolute -top-2 -right-2 px-1.5 py-0.5 text-[10px] font-medium rounded-full uppercase
{type === 'aws' ? 'bg-aws-orange text-white' : ''}
{type === 'gcp' ? 'bg-gcp-blue text-white' : ''}
{type === 'concept' ? 'bg-purple-500 text-white' : ''}
">
{type}
</span>
{/if}
</button>

View file

@ -0,0 +1,126 @@
<script lang="ts">
import { page } from '$app/stores';
import { navigationItems, sidebarOpen, expandedSections, type NavItem } from '$lib/stores/navigation';
import * as Icons from 'lucide-svelte';
import { slide } from 'svelte/transition';
function getIcon(iconName: string) {
return (Icons as Record<string, typeof Icons.Home>)[iconName] || Icons.Circle;
}
function toggleSection(id: string) {
expandedSections.update(set => {
const newSet = new Set(set);
if (newSet.has(id)) {
newSet.delete(id);
} else {
newSet.add(id);
}
return newSet;
});
}
function isActive(href: string): boolean {
return $page.url.pathname === href || $page.url.pathname.startsWith(href + '/');
}
</script>
<aside
class="fixed left-0 top-0 h-screen bg-surface-900 border-r border-surface-800 flex flex-col z-40 transition-all duration-300"
class:w-64={$sidebarOpen}
class:w-16={!$sidebarOpen}
>
<!-- Logo -->
<div class="h-16 flex items-center px-4 border-b border-surface-800">
{#if $sidebarOpen}
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center">
<Icons.Layers class="w-5 h-5 text-white" />
</div>
<span class="font-bold text-lg text-surface-100">System Design</span>
</div>
{:else}
<div class="w-8 h-8 rounded-lg bg-gradient-to-br from-blue-500 to-purple-500 flex items-center justify-center mx-auto">
<Icons.Layers class="w-5 h-5 text-white" />
</div>
{/if}
</div>
<!-- Navigation -->
<nav class="flex-1 overflow-y-auto py-4 px-2">
{#each navigationItems as item}
{@const Icon = getIcon(item.icon)}
{@const hasChildren = item.children && item.children.length > 0}
{@const isExpanded = $expandedSections.has(item.id)}
{@const active = isActive(item.href)}
<div class="mb-1">
{#if hasChildren}
<button
onclick={() => toggleSection(item.id)}
class="w-full flex items-center gap-3 px-3 py-2 rounded-lg text-left transition-colors duration-150"
class:bg-surface-800={active}
class:text-surface-100={active}
class:text-surface-400={!active}
class:hover:bg-surface-800={!active}
class:hover:text-surface-200={!active}
>
<Icon class="w-5 h-5 flex-shrink-0" />
{#if $sidebarOpen}
<span class="flex-1 text-sm font-medium">{item.label}</span>
<Icons.ChevronDown
class="w-4 h-4 transition-transform duration-200 {isExpanded ? 'rotate-180' : ''}"
/>
{/if}
</button>
{:else}
<a
href={item.href}
class="w-full flex items-center gap-3 px-3 py-2 rounded-lg transition-colors duration-150"
class:bg-blue-600={active}
class:text-white={active}
class:text-surface-400={!active}
class:hover:bg-surface-800={!active}
class:hover:text-surface-200={!active}
>
<Icon class="w-5 h-5 flex-shrink-0" />
{#if $sidebarOpen}
<span class="text-sm font-medium">{item.label}</span>
{/if}
</a>
{/if}
{#if hasChildren && isExpanded && $sidebarOpen}
<div transition:slide={{ duration: 200 }} class="ml-4 mt-1 space-y-1">
{#each item.children as child}
{@const ChildIcon = getIcon(child.icon)}
{@const childActive = isActive(child.href)}
<a
href={child.href}
class="flex items-center gap-3 px-3 py-1.5 rounded-lg text-sm transition-colors duration-150 {childActive ? 'bg-blue-600/20 text-blue-400' : 'text-surface-500 hover:bg-surface-800 hover:text-surface-300'}"
>
<ChildIcon class="w-4 h-4 flex-shrink-0" />
<span>{child.label}</span>
</a>
{/each}
</div>
{/if}
</div>
{/each}
</nav>
<!-- Toggle Button -->
<div class="p-4 border-t border-surface-800">
<button
onclick={() => sidebarOpen.update(v => !v)}
class="w-full flex items-center justify-center gap-2 px-3 py-2 rounded-lg bg-surface-800 hover:bg-surface-700 text-surface-400 hover:text-surface-200 transition-colors"
>
{#if $sidebarOpen}
<Icons.PanelLeftClose class="w-5 h-5" />
<span class="text-sm">Collapse</span>
{:else}
<Icons.PanelLeftOpen class="w-5 h-5" />
{/if}
</button>
</div>
</aside>

View file

@ -0,0 +1,125 @@
import { writable } from 'svelte/store';
export interface NavItem {
id: string;
label: string;
icon: string;
href: string;
children?: NavItem[];
}
export const navigationItems: NavItem[] = [
{
id: 'home',
label: 'Overview',
icon: 'Home',
href: '/'
},
{
id: 'fundamentals',
label: 'Fundamentals',
icon: 'BookOpen',
href: '/fundamentals',
children: [
{ id: 'cap-theorem', label: 'CAP Theorem', icon: 'Triangle', href: '/fundamentals/cap-theorem' },
{ id: 'consistency', label: 'Consistency Models', icon: 'RefreshCw', href: '/fundamentals/consistency' },
{ id: 'scaling', label: 'Scaling', icon: 'TrendingUp', href: '/fundamentals/scaling' },
{ id: 'latency', label: 'Latency vs Throughput', icon: 'Gauge', href: '/fundamentals/latency' }
]
},
{
id: 'compute',
label: 'Compute',
icon: 'Cpu',
href: '/compute',
children: [
{ id: 'vms', label: 'Virtual Machines', icon: 'Server', href: '/compute/virtual-machines' },
{ id: 'serverless', label: 'Serverless', icon: 'Zap', href: '/compute/serverless' },
{ id: 'containers', label: 'Containers', icon: 'Box', href: '/compute/containers' },
{ id: 'kubernetes', label: 'Kubernetes', icon: 'Boxes', href: '/compute/kubernetes' }
]
},
{
id: 'storage',
label: 'Storage',
icon: 'HardDrive',
href: '/storage',
children: [
{ id: 'object', label: 'Object Storage', icon: 'File', href: '/storage/object' },
{ id: 'block', label: 'Block Storage', icon: 'Database', href: '/storage/block' },
{ id: 'file', label: 'File Storage', icon: 'Folder', href: '/storage/file' }
]
},
{
id: 'databases',
label: 'Databases',
icon: 'Database',
href: '/databases',
children: [
{ id: 'sql', label: 'SQL Databases', icon: 'Table', href: '/databases/sql' },
{ id: 'nosql', label: 'NoSQL Databases', icon: 'FileJson', href: '/databases/nosql' },
{ id: 'cache', label: 'Caching', icon: 'Layers', href: '/databases/caching' },
{ id: 'replication', label: 'Replication', icon: 'Copy', href: '/databases/replication' }
]
},
{
id: 'networking',
label: 'Networking',
icon: 'Network',
href: '/networking',
children: [
{ id: 'vpc', label: 'VPC & Subnets', icon: 'Globe', href: '/networking/vpc' },
{ id: 'dns', label: 'DNS', icon: 'Link', href: '/networking/dns' },
{ id: 'load-balancing', label: 'Load Balancing', icon: 'Scale', href: '/networking/load-balancing' },
{ id: 'cdn', label: 'CDN', icon: 'Radio', href: '/networking/cdn' }
]
},
{
id: 'messaging',
label: 'Messaging',
icon: 'MessageSquare',
href: '/messaging',
children: [
{ id: 'queues', label: 'Message Queues', icon: 'ListOrdered', href: '/messaging/queues' },
{ id: 'pubsub', label: 'Pub/Sub', icon: 'Bell', href: '/messaging/pubsub' },
{ id: 'event-driven', label: 'Event-Driven', icon: 'Activity', href: '/messaging/event-driven' }
]
},
{
id: 'security',
label: 'Security',
icon: 'Shield',
href: '/security',
children: [
{ id: 'iam', label: 'IAM', icon: 'Key', href: '/security/iam' },
{ id: 'encryption', label: 'Encryption', icon: 'Lock', href: '/security/encryption' },
{ id: 'secrets', label: 'Secrets Management', icon: 'KeyRound', href: '/security/secrets' }
]
},
{
id: 'compare',
label: 'Compare',
icon: 'GitCompare',
href: '/compare',
children: [
{ id: 'sql-vs-nosql', label: 'SQL vs NoSQL', icon: 'GitCompare', href: '/compare/sql-vs-nosql' },
{ id: 'kafka-vs-rabbitmq', label: 'Kafka vs RabbitMQ', icon: 'GitCompare', href: '/compare/kafka-vs-rabbitmq' },
{ id: 'rest-vs-graphql', label: 'REST vs GraphQL vs gRPC', icon: 'GitCompare', href: '/compare/api-design' }
]
},
{
id: 'decisions',
label: 'Decision Trees',
icon: 'GitBranch',
href: '/decisions',
children: [
{ id: 'which-database', label: 'Which Database?', icon: 'HelpCircle', href: '/decisions/which-database' },
{ id: 'serverless-or-not', label: 'Serverless or Not?', icon: 'HelpCircle', href: '/decisions/serverless' },
{ id: 'which-queue', label: 'Which Message Queue?', icon: 'HelpCircle', href: '/decisions/which-queue' }
]
}
];
export const sidebarOpen = writable(true);
export const activeSection = writable('home');
export const expandedSections = writable<Set<string>>(new Set(['fundamentals']));

21
src/routes/+layout.svelte Normal file
View file

@ -0,0 +1,21 @@
<script lang="ts">
import '../app.css';
import Sidebar from '$lib/components/ui/Sidebar.svelte';
import { sidebarOpen } from '$lib/stores/navigation';
let { children } = $props();
</script>
<div class="min-h-screen bg-surface-950">
<Sidebar />
<main
class="transition-all duration-300 min-h-screen"
class:ml-64={$sidebarOpen}
class:ml-16={!$sidebarOpen}
>
<div class="p-8">
{@render children()}
</div>
</main>
</div>

225
src/routes/+page.svelte Normal file
View file

@ -0,0 +1,225 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import { navigationItems } from '$lib/stores/navigation';
const categories = [
{
id: 'fundamentals',
title: 'Fundamentals',
description: 'Core distributed systems concepts: CAP theorem, consistency, scaling',
icon: 'BookOpen',
color: 'from-blue-500 to-cyan-500',
href: '/fundamentals/cap-theorem'
},
{
id: 'compute',
title: 'Compute',
description: 'VMs, serverless, containers, and Kubernetes',
icon: 'Cpu',
color: 'from-purple-500 to-pink-500',
href: '/compute/virtual-machines'
},
{
id: 'databases',
title: 'Databases',
description: 'SQL, NoSQL, caching, replication patterns',
icon: 'Database',
color: 'from-green-500 to-emerald-500',
href: '/databases/sql'
},
{
id: 'networking',
title: 'Networking',
description: 'VPC, DNS, load balancing, CDN',
icon: 'Network',
color: 'from-orange-500 to-red-500',
href: '/networking/vpc'
},
{
id: 'messaging',
title: 'Messaging',
description: 'Message queues, pub/sub, event-driven architecture',
icon: 'MessageSquare',
color: 'from-yellow-500 to-orange-500',
href: '/messaging/queues'
},
{
id: 'decisions',
title: 'Decision Trees',
description: 'Interactive guides: which database? serverless or not?',
icon: 'GitBranch',
color: 'from-indigo-500 to-purple-500',
href: '/decisions/which-database'
}
];
const quickLinks = [
{ label: 'SQL vs NoSQL', href: '/compare/sql-vs-nosql', icon: 'GitCompare' },
{ label: 'Kafka vs RabbitMQ', href: '/compare/kafka-vs-rabbitmq', icon: 'GitCompare' },
{ label: 'REST vs GraphQL vs gRPC', href: '/compare/api-design', icon: 'GitCompare' },
{ label: 'Which Database?', href: '/decisions/which-database', icon: 'HelpCircle' }
];
function getIcon(iconName: string) {
return (Icons as Record<string, typeof Icons.Box>)[iconName] || Icons.Box;
}
</script>
<svelte:head>
<title>System Design Explorer</title>
</svelte:head>
<div class="max-w-6xl mx-auto space-y-12">
<!-- Hero Section -->
<div class="text-center space-y-6 py-8">
<div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-blue-500/10 border border-blue-500/20 text-blue-400 text-sm">
<Icons.Sparkles class="w-4 h-4" />
Interactive Visual Learning
</div>
<h1 class="text-4xl md:text-5xl font-bold text-surface-100">
Master <span class="text-gradient">System Design</span>
</h1>
<p class="text-lg text-surface-400 max-w-2xl mx-auto">
Learn cloud infrastructure, distributed systems, and architecture patterns through
interactive diagrams, visual comparisons, and decision-guided walkthroughs.
</p>
<div class="flex justify-center gap-4">
<a href="/fundamentals/cap-theorem" class="btn-primary flex items-center gap-2">
<Icons.Play class="w-4 h-4" />
Start Learning
</a>
<a href="/decisions/which-database" class="btn-secondary flex items-center gap-2">
<Icons.GitBranch class="w-4 h-4" />
Try Decision Tree
</a>
</div>
</div>
<!-- Category Cards -->
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
{#each categories as category}
{@const Icon = getIcon(category.icon)}
<a
href={category.href}
class="card-hover group"
>
<div class="flex items-start gap-4">
<div class="w-12 h-12 rounded-xl bg-gradient-to-br {category.color} flex items-center justify-center flex-shrink-0 group-hover:scale-110 transition-transform duration-200">
<Icon class="w-6 h-6 text-white" />
</div>
<div>
<h3 class="font-semibold text-surface-100 group-hover:text-white transition-colors">
{category.title}
</h3>
<p class="text-sm text-surface-400 mt-1">{category.description}</p>
</div>
</div>
<div class="mt-4 flex items-center text-sm text-blue-400 group-hover:text-blue-300">
<span>Explore</span>
<Icons.ArrowRight class="w-4 h-4 ml-1 group-hover:translate-x-1 transition-transform" />
</div>
</a>
{/each}
</div>
<!-- Quick Comparisons -->
<div class="card">
<div class="flex items-center gap-3 mb-6">
<div class="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center">
<Icons.Scale class="w-5 h-5 text-purple-400" />
</div>
<div>
<h2 class="font-semibold text-surface-100">Quick Comparisons</h2>
<p class="text-sm text-surface-400">Side-by-side analysis with "when to use" guidance</p>
</div>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-4 gap-3">
{#each quickLinks as link}
{@const Icon = getIcon(link.icon)}
<a
href={link.href}
class="flex items-center gap-3 p-3 rounded-lg bg-surface-800 hover:bg-surface-700 transition-colors"
>
<Icon class="w-5 h-5 text-surface-400" />
<span class="text-sm text-surface-200">{link.label}</span>
</a>
{/each}
</div>
</div>
<!-- Cloud Providers Section -->
<div class="grid md:grid-cols-2 gap-6">
<!-- AWS -->
<div class="card border-aws-orange/30">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-aws-orange/20 flex items-center justify-center">
<Icons.Cloud class="w-5 h-5 text-aws-orange" />
</div>
<div>
<h3 class="font-semibold text-surface-100">Amazon Web Services</h3>
<p class="text-xs text-surface-500">EC2, S3, Lambda, RDS, EKS, and more</p>
</div>
</div>
<div class="grid grid-cols-3 gap-2">
{#each ['EC2', 'S3', 'Lambda', 'RDS', 'EKS', 'SQS'] as service}
<div class="px-2 py-1 rounded bg-aws-orange/10 text-aws-orange text-xs text-center">
{service}
</div>
{/each}
</div>
</div>
<!-- GCP -->
<div class="card border-gcp-blue/30">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-gcp-blue/20 flex items-center justify-center">
<Icons.Cloud class="w-5 h-5 text-gcp-blue" />
</div>
<div>
<h3 class="font-semibold text-surface-100">Google Cloud Platform</h3>
<p class="text-xs text-surface-500">Compute Engine, Cloud Storage, GKE, and more</p>
</div>
</div>
<div class="grid grid-cols-3 gap-2">
{#each ['Compute', 'Storage', 'GKE', 'Cloud SQL', 'Pub/Sub', 'BigQuery'] as service}
<div class="px-2 py-1 rounded bg-gcp-blue/10 text-gcp-blue text-xs text-center">
{service}
</div>
{/each}
</div>
</div>
</div>
<!-- Key Concepts Preview -->
<div class="card">
<div class="flex items-center gap-3 mb-6">
<div class="w-10 h-10 rounded-lg bg-green-500/20 flex items-center justify-center">
<Icons.Lightbulb class="w-5 h-5 text-green-400" />
</div>
<div>
<h2 class="font-semibold text-surface-100">Key Concepts You'll Learn</h2>
<p class="text-sm text-surface-400">Each with interactive diagrams and real-world examples</p>
</div>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-4">
{#each [
{ name: 'CAP Theorem', desc: 'Consistency, Availability, Partition Tolerance trade-offs' },
{ name: 'Load Balancing', desc: 'L4 vs L7, algorithms, health checks' },
{ name: 'Database Sharding', desc: 'Horizontal scaling, partition strategies' },
{ name: 'Caching Patterns', desc: 'Cache-aside, write-through, invalidation' },
{ name: 'Message Queues', desc: 'When to use Kafka vs RabbitMQ vs SQS' },
{ name: 'Kubernetes', desc: 'Pods, services, deployments, scaling' }
] as concept}
<div class="p-4 rounded-lg bg-surface-800/50 border border-surface-700">
<h4 class="font-medium text-surface-200">{concept.name}</h4>
<p class="text-xs text-surface-500 mt-1">{concept.desc}</p>
</div>
{/each}
</div>
</div>
</div>

View file

@ -0,0 +1,406 @@
<script lang="ts">
import ComparisonTable from '$lib/components/diagrams/ComparisonTable.svelte';
let selectedSystem: 'kafka' | 'rabbitmq' | null = null;
const comparisonOptions = [
{
id: 'kafka',
name: 'Apache Kafka',
description: 'Distributed event streaming platform for high-throughput data pipelines',
whenToUse: [
'High-throughput event streaming (millions of messages/sec)',
'Real-time analytics and data pipelines',
'Event sourcing and audit trails',
'Log aggregation from multiple sources',
'Need to replay/reprocess historical messages',
'Building data lakes and ML pipelines',
'Long-term message retention (days/weeks)'
],
whenNotToUse: [
'Simple task queues with low volume',
'Need complex routing patterns',
'Request/reply messaging patterns',
'Low-latency at low throughput more important',
'Need message priorities',
'Want simpler operations and setup'
]
},
{
id: 'rabbitmq',
name: 'RabbitMQ',
description: 'Traditional message broker for reliable delivery and flexible routing',
whenToUse: [
'Complex routing patterns (topic, fanout, headers)',
'Traditional request/reply messaging',
'Task queues with acknowledgments',
'Need message priorities',
'Multiple protocol support (AMQP, MQTT, STOMP)',
'Microservices communication',
'Lower volume with guaranteed delivery'
],
whenNotToUse: [
'Need millions of messages per second',
'Want to replay old messages',
'Building real-time analytics pipelines',
'Long-term message storage required',
'Event sourcing architecture',
'Horizontal scaling is critical'
]
}
];
const features = [
{ name: 'Architecture', kafka: 'Distributed log (append-only)', rabbitmq: 'Message broker (queue-based)' },
{ name: 'Delivery Model', kafka: 'Pull (consumers poll)', rabbitmq: 'Push (broker pushes)' },
{ name: 'Message Retention', kafka: 'Time/size based (keeps messages)', rabbitmq: 'Until acknowledged (deletes)' },
{ name: 'Throughput', kafka: 'Millions/sec per broker', rabbitmq: '10K-50K/sec' },
{ name: 'Latency', kafka: '~5ms at scale', rabbitmq: 'Sub-ms at low volume' },
{ name: 'Ordering', kafka: 'Per partition only', rabbitmq: 'Per queue' },
{ name: 'Replay', kafka: 'Yes (offset-based)', rabbitmq: 'No (messages deleted)' },
{ name: 'Routing', kafka: 'Topic-based only', rabbitmq: 'Flexible (exchanges, bindings)' },
{ name: 'Protocol', kafka: 'Kafka protocol (binary)', rabbitmq: 'AMQP, MQTT, STOMP, HTTP' },
{ name: 'Scaling', kafka: 'Horizontal (partitions)', rabbitmq: 'Vertical + clustering' },
{ name: 'Consumer Groups', kafka: 'Built-in (partition-based)', rabbitmq: 'Manual (competing consumers)' }
];
const architectureDiff = {
kafka: {
title: 'Distributed Log',
description: 'Messages are appended to partitioned logs and retained based on time/size policies',
flow: [
{ label: 'Producer', desc: 'Writes to topic partition' },
{ label: 'Broker', desc: 'Stores in append-only log' },
{ label: 'Consumer', desc: 'Pulls at own pace via offset' }
],
keyPoint: 'Messages persist regardless of consumption - consumers track their own position (offset)'
},
rabbitmq: {
title: 'Message Broker',
description: 'Messages routed through exchanges to queues and removed once acknowledged',
flow: [
{ label: 'Producer', desc: 'Sends to exchange' },
{ label: 'Exchange', desc: 'Routes to queue(s)' },
{ label: 'Consumer', desc: 'Receives push, sends ack' }
],
keyPoint: 'Messages deleted after acknowledgment - broker tracks delivery state'
}
};
</script>
<svelte:head>
<title>Kafka vs RabbitMQ</title>
</svelte:head>
<div class="max-w-6xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-2">Kafka vs RabbitMQ</h1>
<p class="text-gray-400 mb-8">Event streaming platform vs traditional message broker - understanding the fundamental differences</p>
<!-- TL;DR -->
<section class="mb-12">
<div class="bg-gradient-to-r from-orange-500/10 to-green-500/10 rounded-xl p-6 border border-gray-700">
<h2 class="text-lg font-semibold text-white mb-4">TL;DR</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="flex items-start gap-3">
<div class="w-3 h-3 rounded-full bg-orange-500 mt-1.5"></div>
<div>
<span class="text-orange-400 font-medium">Kafka</span>
<span class="text-gray-300"> = Event streaming. High throughput, message replay, data pipelines.</span>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-3 h-3 rounded-full bg-green-500 mt-1.5"></div>
<div>
<span class="text-green-400 font-medium">RabbitMQ</span>
<span class="text-gray-300"> = Message broker. Complex routing, guaranteed delivery, task queues.</span>
</div>
</div>
</div>
</div>
</section>
<!-- Architecture Comparison -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Architecture Difference</h2>
<p class="text-gray-400 mb-6">The fundamental architectural difference explains most of their behavior differences.</p>
<div class="grid md:grid-cols-2 gap-6">
<!-- Kafka -->
<div class="bg-gray-800/50 rounded-xl p-6 border border-orange-500/30">
<h3 class="text-lg font-semibold text-orange-400 mb-2">{architectureDiff.kafka.title}</h3>
<p class="text-gray-400 text-sm mb-4">{architectureDiff.kafka.description}</p>
<div class="space-y-3 mb-4">
{#each architectureDiff.kafka.flow as step, i}
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-orange-500/20 flex items-center justify-center text-orange-400 font-bold text-sm">
{i + 1}
</div>
<div>
<div class="text-white font-medium text-sm">{step.label}</div>
<div class="text-gray-500 text-xs">{step.desc}</div>
</div>
{#if i < architectureDiff.kafka.flow.length - 1}
<div class="flex-1 border-t border-dashed border-orange-500/30"></div>
{/if}
</div>
{/each}
</div>
<div class="p-3 bg-orange-500/10 rounded-lg border border-orange-500/20">
<div class="text-orange-300 text-sm">{architectureDiff.kafka.keyPoint}</div>
</div>
</div>
<!-- RabbitMQ -->
<div class="bg-gray-800/50 rounded-xl p-6 border border-green-500/30">
<h3 class="text-lg font-semibold text-green-400 mb-2">{architectureDiff.rabbitmq.title}</h3>
<p class="text-gray-400 text-sm mb-4">{architectureDiff.rabbitmq.description}</p>
<div class="space-y-3 mb-4">
{#each architectureDiff.rabbitmq.flow as step, i}
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-green-500/20 flex items-center justify-center text-green-400 font-bold text-sm">
{i + 1}
</div>
<div>
<div class="text-white font-medium text-sm">{step.label}</div>
<div class="text-gray-500 text-xs">{step.desc}</div>
</div>
{#if i < architectureDiff.rabbitmq.flow.length - 1}
<div class="flex-1 border-t border-dashed border-green-500/30"></div>
{/if}
</div>
{/each}
</div>
<div class="p-3 bg-green-500/10 rounded-lg border border-green-500/20">
<div class="text-green-300 text-sm">{architectureDiff.rabbitmq.keyPoint}</div>
</div>
</div>
</div>
</section>
<!-- Message Lifecycle -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Message Lifecycle</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h3 class="text-lg font-semibold text-orange-400 mb-4">Kafka: Log-Based</h3>
<div class="space-y-3">
<div class="flex items-center gap-3 p-3 bg-orange-500/10 rounded-lg">
<span class="text-2xl">1.</span>
<div>
<div class="text-white">Message written to partition</div>
<div class="text-gray-500 text-sm">Appended to end of log with offset</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-orange-500/10 rounded-lg">
<span class="text-2xl">2.</span>
<div>
<div class="text-white">Consumers read at their offset</div>
<div class="text-gray-500 text-sm">Multiple consumers can read same message</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-orange-500/10 rounded-lg">
<span class="text-2xl">3.</span>
<div>
<div class="text-white">Retention policy deletes old messages</div>
<div class="text-gray-500 text-sm">After 7 days or 1GB (configurable)</div>
</div>
</div>
</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h3 class="text-lg font-semibold text-green-400 mb-4">RabbitMQ: Queue-Based</h3>
<div class="space-y-3">
<div class="flex items-center gap-3 p-3 bg-green-500/10 rounded-lg">
<span class="text-2xl">1.</span>
<div>
<div class="text-white">Message routed to queue(s)</div>
<div class="text-gray-500 text-sm">Exchange determines destination</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-green-500/10 rounded-lg">
<span class="text-2xl">2.</span>
<div>
<div class="text-white">Broker pushes to consumer</div>
<div class="text-gray-500 text-sm">One consumer receives each message</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-green-500/10 rounded-lg">
<span class="text-2xl">3.</span>
<div>
<div class="text-white">Consumer acknowledges</div>
<div class="text-gray-500 text-sm">Message deleted from queue</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Feature Comparison -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Feature Comparison</h2>
<div class="bg-gray-800/50 rounded-xl border border-gray-700 overflow-hidden">
<table class="w-full">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left p-4 text-gray-400 font-medium">Feature</th>
<th class="text-left p-4 text-orange-400 font-medium">Kafka</th>
<th class="text-left p-4 text-green-400 font-medium">RabbitMQ</th>
</tr>
</thead>
<tbody>
{#each features as feature, i}
<tr class="border-b border-gray-700/50 {i % 2 === 0 ? 'bg-gray-800/30' : ''}">
<td class="p-4 text-white font-medium">{feature.name}</td>
<td class="p-4 text-gray-300">{feature.kafka}</td>
<td class="p-4 text-gray-300">{feature.rabbitmq}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</section>
<!-- When to Use -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">When to Use Each</h2>
<ComparisonTable options={comparisonOptions} />
</section>
<!-- Exchange Types (RabbitMQ) -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">RabbitMQ Routing Patterns</h2>
<p class="text-gray-400 mb-4">RabbitMQ's flexible exchange types enable complex routing scenarios that Kafka cannot do natively.</p>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-green-400 font-semibold mb-2">Direct</div>
<div class="text-gray-400 text-sm">Route by exact routing key match</div>
<div class="mt-2 text-xs text-gray-500">order.created to orders queue</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-green-400 font-semibold mb-2">Topic</div>
<div class="text-gray-400 text-sm">Route by pattern matching (*.error)</div>
<div class="mt-2 text-xs text-gray-500">app.*.error to error-handler</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-green-400 font-semibold mb-2">Fanout</div>
<div class="text-gray-400 text-sm">Broadcast to all bound queues</div>
<div class="mt-2 text-xs text-gray-500">notification to all subscribers</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-green-400 font-semibold mb-2">Headers</div>
<div class="text-gray-400 text-sm">Route by message headers</div>
<div class="mt-2 text-xs text-gray-500">x-priority: high to fast queue</div>
</div>
</div>
</section>
<!-- Kafka Partitions -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Kafka Partitioning</h2>
<p class="text-gray-400 mb-4">Kafka achieves scalability through partitions - each partition is an ordered, immutable log.</p>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="grid md:grid-cols-3 gap-4 mb-6">
<div class="p-4 bg-orange-500/10 rounded-lg border border-orange-500/30">
<div class="text-orange-400 font-semibold mb-1">Partition 0</div>
<div class="flex gap-1">
{#each [0, 1, 2, 3, 4] as offset}
<div class="w-8 h-8 bg-orange-500/30 rounded flex items-center justify-center text-xs text-orange-300">{offset}</div>
{/each}
</div>
</div>
<div class="p-4 bg-orange-500/10 rounded-lg border border-orange-500/30">
<div class="text-orange-400 font-semibold mb-1">Partition 1</div>
<div class="flex gap-1">
{#each [0, 1, 2, 3] as offset}
<div class="w-8 h-8 bg-orange-500/30 rounded flex items-center justify-center text-xs text-orange-300">{offset}</div>
{/each}
</div>
</div>
<div class="p-4 bg-orange-500/10 rounded-lg border border-orange-500/30">
<div class="text-orange-400 font-semibold mb-1">Partition 2</div>
<div class="flex gap-1">
{#each [0, 1, 2, 3, 4, 5] as offset}
<div class="w-8 h-8 bg-orange-500/30 rounded flex items-center justify-center text-xs text-orange-300">{offset}</div>
{/each}
</div>
</div>
</div>
<div class="grid md:grid-cols-3 gap-4 text-sm">
<div>
<div class="text-white font-medium">Ordering</div>
<div class="text-gray-400">Guaranteed within partition only</div>
</div>
<div>
<div class="text-white font-medium">Parallelism</div>
<div class="text-gray-400">Max consumers = number of partitions</div>
</div>
<div>
<div class="text-white font-medium">Key-based routing</div>
<div class="text-gray-400">Same key always goes to same partition</div>
</div>
</div>
</div>
</section>
<!-- Use Case Examples -->
<section>
<h2 class="text-xl font-semibold text-white mb-4">Real World Examples</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-orange-500/30">
<h3 class="text-lg font-semibold text-orange-400 mb-4">Kafka Use Cases</h3>
<div class="space-y-3">
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">Real-time Analytics</div>
<div class="text-gray-400 text-sm">Stream clickstream data to analytics pipeline</div>
</div>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">Event Sourcing</div>
<div class="text-gray-400 text-sm">Store all state changes as immutable events</div>
</div>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">Log Aggregation</div>
<div class="text-gray-400 text-sm">Centralize logs from thousands of services</div>
</div>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">CDC Pipelines</div>
<div class="text-gray-400 text-sm">Stream database changes to data warehouse</div>
</div>
</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-green-500/30">
<h3 class="text-lg font-semibold text-green-400 mb-4">RabbitMQ Use Cases</h3>
<div class="space-y-3">
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">Task Queues</div>
<div class="text-gray-400 text-sm">Background job processing with retries</div>
</div>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">Microservices</div>
<div class="text-gray-400 text-sm">Async communication between services</div>
</div>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">IoT Messaging</div>
<div class="text-gray-400 text-sm">MQTT protocol for device communication</div>
</div>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-white font-medium">Request/Reply</div>
<div class="text-gray-400 text-sm">RPC-style communication patterns</div>
</div>
</div>
</div>
</div>
</section>
</div>

View file

@ -0,0 +1,343 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import ComparisonTable from '$lib/components/diagrams/ComparisonTable.svelte';
const options = [
{
id: 'sql',
name: 'SQL (Relational)',
color: '#3B82F6',
icon: 'Table',
whenToUse: [
'Complex queries with JOINs across multiple tables',
'ACID compliance is required (financial transactions)',
'Data has clear relationships and fixed schema',
'Strong data integrity and consistency needed',
'Reporting and analytics with complex aggregations'
],
whenNotToUse: [
'Rapidly changing or unknown schema',
'Massive horizontal scaling requirements',
'Simple key-value lookups at extreme scale',
'Hierarchical or document-like data',
'Write-heavy workloads at massive scale'
],
realWorldExample: 'E-commerce order management: Orders, customers, products, and inventory have clear relationships. You need to JOIN tables to show "all orders for customer X with product details" and ensure inventory counts are accurate with ACID transactions.'
},
{
id: 'nosql-document',
name: 'NoSQL (Document)',
color: '#22C55E',
icon: 'FileJson',
whenToUse: [
'Flexible, evolving schema',
'Hierarchical or nested data (JSON-like)',
'Rapid development with changing requirements',
'Content management and user profiles',
'Each document is largely self-contained'
],
whenNotToUse: [
'Heavy relational queries (many JOINs)',
'Strong consistency is critical',
'Complex transactions across documents',
'Data has clear tabular structure',
'Strict schema enforcement needed'
],
realWorldExample: 'Product catalog: Each product can have different attributes (electronics vs clothing). Schema evolves as you add new product types. MongoDB stores each product as a document with flexible fields.'
},
{
id: 'nosql-keyvalue',
name: 'NoSQL (Key-Value)',
color: '#EAB308',
icon: 'Key',
whenToUse: [
'Session storage and caching',
'Simple lookups by key at extreme speed',
'Leaderboards and counters',
'Shopping carts and temporary data',
'Real-time data with TTL (expiration)'
],
whenNotToUse: [
'Complex queries beyond key lookup',
'Relational data with JOINs',
'Full-text search requirements',
'Data needs to be queried by multiple fields',
'Complex data structures'
],
realWorldExample: 'Session storage in Redis: User logs in, you store session data with key "session:user123". Lightning-fast lookups. Automatic expiration after 30 minutes. Perfect for caching database query results.'
},
{
id: 'nosql-widecolumn',
name: 'NoSQL (Wide-Column)',
color: '#EC4899',
icon: 'LayoutGrid',
whenToUse: [
'Time-series data (IoT, metrics, logs)',
'Massive write throughput needed',
'Data partitioned by time or ID',
'Distributed across many nodes',
'Write-heavy with sequential reads'
],
whenNotToUse: [
'Ad-hoc queries on any column',
'Complex JOINs or aggregations',
'Small datasets (overkill)',
'Strong consistency requirements',
'Frequent schema changes'
],
realWorldExample: 'IoT sensor data in Cassandra: Millions of sensors writing data every second. Partition by sensor_id and cluster by timestamp. Query pattern: "get all readings for sensor X in the last hour."'
}
];
const features = [
{
feature: 'Schema',
description: 'How data structure is defined',
values: {
sql: 'Fixed, predefined schema',
'nosql-document': 'Flexible, dynamic schema',
'nosql-keyvalue': 'Schema-less (key-value pairs)',
'nosql-widecolumn': 'Flexible columns per row'
}
},
{
feature: 'Data Model',
description: 'How data is organized',
values: {
sql: 'Tables with rows and columns',
'nosql-document': 'JSON-like documents',
'nosql-keyvalue': 'Simple key-value pairs',
'nosql-widecolumn': 'Column families'
}
},
{
feature: 'Scaling',
description: 'How to handle growth',
values: {
sql: 'Vertical (bigger server)',
'nosql-document': 'Horizontal (more servers)',
'nosql-keyvalue': 'Horizontal (more servers)',
'nosql-widecolumn': 'Horizontal (more servers)'
}
},
{
feature: 'ACID Transactions',
description: 'Atomicity, Consistency, Isolation, Durability',
values: {
sql: true,
'nosql-document': 'Partial (single document)',
'nosql-keyvalue': false,
'nosql-widecolumn': 'Partial (row-level)'
}
},
{
feature: 'JOINs',
description: 'Combining data from multiple sources',
values: {
sql: true,
'nosql-document': false,
'nosql-keyvalue': false,
'nosql-widecolumn': false
}
},
{
feature: 'Query Language',
description: 'How you retrieve data',
values: {
sql: 'SQL (standardized)',
'nosql-document': 'Proprietary (varies)',
'nosql-keyvalue': 'GET/SET operations',
'nosql-widecolumn': 'CQL (SQL-like)'
}
},
{
feature: 'Consistency',
description: 'Data accuracy guarantees',
values: {
sql: 'Strong consistency',
'nosql-document': 'Tunable (eventual to strong)',
'nosql-keyvalue': 'Eventual consistency',
'nosql-widecolumn': 'Tunable consistency'
}
},
{
feature: 'Read Performance',
values: {
sql: 'Good (with indexes)',
'nosql-document': 'Excellent (by _id)',
'nosql-keyvalue': 'Excellent (by key)',
'nosql-widecolumn': 'Good (by partition key)'
}
},
{
feature: 'Write Performance',
values: {
sql: 'Good',
'nosql-document': 'Excellent',
'nosql-keyvalue': 'Excellent',
'nosql-widecolumn': 'Excellent'
}
},
{
feature: 'Examples',
values: {
sql: 'PostgreSQL, MySQL, SQL Server',
'nosql-document': 'MongoDB, Firestore, CouchDB',
'nosql-keyvalue': 'Redis, DynamoDB, Memcached',
'nosql-widecolumn': 'Cassandra, Bigtable, ScyllaDB'
}
}
];
</script>
<svelte:head>
<title>SQL vs NoSQL - System Design Explorer</title>
</svelte:head>
<div class="max-w-6xl mx-auto space-y-8">
<!-- Header -->
<div>
<div class="flex items-center gap-2 text-surface-500 text-sm mb-2">
<a href="/compare" class="hover:text-surface-300">Compare</a>
<Icons.ChevronRight class="w-4 h-4" />
<span class="text-surface-300">SQL vs NoSQL</span>
</div>
<h1 class="text-3xl font-bold text-surface-100">SQL vs NoSQL Databases</h1>
<p class="text-surface-400 mt-2">
Understanding when to use relational vs non-relational databases for your use case.
</p>
</div>
<!-- Quick Summary Cards -->
<div class="grid md:grid-cols-2 gap-4">
<div class="card border-blue-500/30">
<div class="flex items-center gap-3 mb-3">
<div class="w-10 h-10 rounded-lg bg-blue-500/20 flex items-center justify-center">
<Icons.Table class="w-5 h-5 text-blue-400" />
</div>
<span class="font-semibold text-surface-100">SQL in a Nutshell</span>
</div>
<p class="text-sm text-surface-400">
Structured data in tables with relationships. Strong consistency, ACID transactions,
complex queries with JOINs. Scale vertically. Best when data structure is known and
relationships matter.
</p>
</div>
<div class="card border-green-500/30">
<div class="flex items-center gap-3 mb-3">
<div class="w-10 h-10 rounded-lg bg-green-500/20 flex items-center justify-center">
<Icons.FileJson class="w-5 h-5 text-green-400" />
</div>
<span class="font-semibold text-surface-100">NoSQL in a Nutshell</span>
</div>
<p class="text-sm text-surface-400">
Flexible schemas, horizontal scaling, eventual consistency. Different types for
different needs: documents, key-value, wide-column, graph. Best for scale,
flexibility, and specific access patterns.
</p>
</div>
</div>
<!-- Comparison Table -->
<ComparisonTable
title="Feature Comparison"
subtitle="Click on a column header to see detailed guidance"
{options}
{features}
/>
<!-- The Real Question -->
<div class="card bg-gradient-to-br from-purple-500/10 to-pink-500/10 border-purple-500/30">
<div class="flex items-start gap-4">
<div class="w-10 h-10 rounded-lg bg-purple-500/20 flex items-center justify-center flex-shrink-0">
<Icons.Lightbulb class="w-5 h-5 text-purple-400" />
</div>
<div>
<h3 class="font-semibold text-surface-100 mb-2">The Real Question to Ask</h3>
<p class="text-surface-300 text-sm mb-4">
Instead of "SQL or NoSQL?", ask yourself these questions:
</p>
<div class="grid md:grid-cols-2 gap-4">
<div class="space-y-2">
<div class="flex items-start gap-2">
<Icons.HelpCircle class="w-4 h-4 text-purple-400 mt-0.5 flex-shrink-0" />
<span class="text-sm text-surface-300">What are my query patterns? (JOINs? Key lookups?)</span>
</div>
<div class="flex items-start gap-2">
<Icons.HelpCircle class="w-4 h-4 text-purple-400 mt-0.5 flex-shrink-0" />
<span class="text-sm text-surface-300">How important is consistency vs availability?</span>
</div>
</div>
<div class="space-y-2">
<div class="flex items-start gap-2">
<Icons.HelpCircle class="w-4 h-4 text-purple-400 mt-0.5 flex-shrink-0" />
<span class="text-sm text-surface-300">Will my schema evolve frequently?</span>
</div>
<div class="flex items-start gap-2">
<Icons.HelpCircle class="w-4 h-4 text-purple-400 mt-0.5 flex-shrink-0" />
<span class="text-sm text-surface-300">What scale do I need to handle?</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Common Patterns -->
<div class="card">
<h3 class="text-lg font-semibold text-surface-100 mb-4">Common Patterns in Production</h3>
<div class="space-y-4">
<div class="flex items-start gap-4 p-4 bg-surface-800 rounded-lg">
<div class="w-8 h-8 rounded-lg bg-blue-500/20 flex items-center justify-center flex-shrink-0">
<span class="text-sm font-bold text-blue-400">1</span>
</div>
<div>
<h4 class="font-medium text-surface-200">Polyglot Persistence</h4>
<p class="text-sm text-surface-400 mt-1">
Use multiple databases for different needs. PostgreSQL for transactions, Redis for caching,
Elasticsearch for search, MongoDB for flexible data. Match the database to the use case.
</p>
</div>
</div>
<div class="flex items-start gap-4 p-4 bg-surface-800 rounded-lg">
<div class="w-8 h-8 rounded-lg bg-green-500/20 flex items-center justify-center flex-shrink-0">
<span class="text-sm font-bold text-green-400">2</span>
</div>
<div>
<h4 class="font-medium text-surface-200">Start with SQL, Add NoSQL Later</h4>
<p class="text-sm text-surface-400 mt-1">
Begin with PostgreSQL - it's versatile, well-understood, and handles most use cases.
Add specialized databases when you hit specific scaling or performance needs.
</p>
</div>
</div>
<div class="flex items-start gap-4 p-4 bg-surface-800 rounded-lg">
<div class="w-8 h-8 rounded-lg bg-yellow-500/20 flex items-center justify-center flex-shrink-0">
<span class="text-sm font-bold text-yellow-400">3</span>
</div>
<div>
<h4 class="font-medium text-surface-200">Cache Hot Data</h4>
<p class="text-sm text-surface-400 mt-1">
Use Redis in front of your primary database. Most apps have hot data (frequently accessed)
and cold data. Cache the hot data for massive performance gains without changing your primary DB.
</p>
</div>
</div>
</div>
</div>
<!-- Navigation -->
<div class="flex justify-between items-center pt-4 border-t border-surface-800">
<a href="/compare" class="btn-secondary flex items-center gap-2">
<Icons.ArrowLeft class="w-4 h-4" />
All Comparisons
</a>
<a href="/decisions/which-database" class="btn-primary flex items-center gap-2">
Try Decision Tree
<Icons.GitBranch class="w-4 h-4" />
</a>
</div>
</div>

View file

@ -0,0 +1,397 @@
<script lang="ts">
let selectedComponent: string | null = null;
const controlPlaneComponents = [
{
id: 'api-server',
name: 'kube-apiserver',
description: 'Front door to the cluster - all communication goes through here',
details: [
'Exposes the Kubernetes API (REST)',
'Validates and processes API requests',
'Only component that talks to etcd',
'Handles authentication and authorization'
],
color: 'blue'
},
{
id: 'etcd',
name: 'etcd',
description: 'Distributed key-value store - the source of truth for cluster state',
details: [
'Stores all cluster configuration',
'Highly available (Raft consensus)',
'Only accessed by API server',
'Critical - backup regularly!'
],
color: 'purple'
},
{
id: 'scheduler',
name: 'kube-scheduler',
description: 'Decides which node runs each pod',
details: [
'Watches for unscheduled pods',
'Evaluates node resources and constraints',
'Considers affinity/anti-affinity rules',
'Assigns pod to best-fit node'
],
color: 'green'
},
{
id: 'controller-manager',
name: 'kube-controller-manager',
description: 'Runs control loops that maintain desired state',
details: [
'Node Controller - monitors node health',
'Deployment Controller - manages rollouts',
'ReplicaSet Controller - ensures pod count',
'Job Controller - runs one-off tasks'
],
color: 'orange'
},
{
id: 'cloud-controller',
name: 'cloud-controller-manager',
description: 'Integrates with cloud provider APIs',
details: [
'Creates load balancers for Services',
'Provisions cloud storage volumes',
'Manages node lifecycle in cloud',
'Only present in cloud deployments'
],
color: 'cyan'
}
];
const nodeComponents = [
{
id: 'kubelet',
name: 'kubelet',
description: 'Agent on each node - ensures containers are running',
details: [
'Receives pod specs from API server',
'Manages container lifecycle via CRI',
'Reports node and pod status',
'Handles liveness/readiness probes'
],
color: 'yellow'
},
{
id: 'kube-proxy',
name: 'kube-proxy',
description: 'Network proxy - implements Service networking',
details: [
'Maintains iptables/IPVS rules',
'Routes traffic to correct pods',
'Handles ClusterIP, NodePort, LoadBalancer',
'Can be replaced by CNI plugins'
],
color: 'pink'
},
{
id: 'container-runtime',
name: 'Container Runtime',
description: 'Actually runs containers (containerd, CRI-O)',
details: [
'Pulls container images',
'Creates and runs containers',
'Manages container lifecycle',
'Implements Container Runtime Interface (CRI)'
],
color: 'gray'
}
];
const podLifecycle = [
{ phase: 'Pending', desc: 'Pod accepted but not yet scheduled', color: 'yellow' },
{ phase: 'Scheduled', desc: 'Scheduler assigned pod to a node', color: 'blue' },
{ phase: 'ContainerCreating', desc: 'Pulling images, creating containers', color: 'purple' },
{ phase: 'Running', desc: 'All containers started', color: 'green' },
{ phase: 'Succeeded/Failed', desc: 'All containers terminated', color: 'gray' }
];
const requestFlow = [
{ step: 1, title: 'kubectl apply', desc: 'User submits manifest to API server' },
{ step: 2, title: 'Validation', desc: 'API server validates and stores in etcd' },
{ step: 3, title: 'Scheduling', desc: 'Scheduler picks optimal node for pod' },
{ step: 4, title: 'Kubelet notified', desc: 'Node kubelet sees new pod assignment' },
{ step: 5, title: 'Container creation', desc: 'Kubelet tells runtime to start containers' },
{ step: 6, title: 'Running', desc: 'Pod running, kube-proxy sets up networking' }
];
</script>
<svelte:head>
<title>Kubernetes Architecture</title>
</svelte:head>
<div class="max-w-6xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-2">Kubernetes Architecture</h1>
<p class="text-gray-400 mb-8">Understanding the control plane, worker nodes, and how they work together</p>
<!-- Overview Diagram -->
<section class="mb-12">
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="grid md:grid-cols-2 gap-8">
<!-- Control Plane -->
<div class="p-4 rounded-xl bg-blue-500/10 border border-blue-500/30">
<h3 class="text-lg font-semibold text-blue-400 mb-4 text-center">Control Plane</h3>
<p class="text-gray-400 text-sm text-center mb-4">The brain - makes decisions about the cluster</p>
<div class="grid grid-cols-2 gap-3">
{#each controlPlaneComponents.slice(0, 4) as component}
<button
class="p-3 rounded-lg bg-gray-800/50 border border-gray-600 hover:border-gray-400 transition-all text-left"
class:border-blue-500={selectedComponent === component.id}
on:click={() => selectedComponent = selectedComponent === component.id ? null : component.id}
>
<div class="text-white font-medium text-sm">{component.name}</div>
<div class="text-gray-500 text-xs mt-1 line-clamp-2">{component.description}</div>
</button>
{/each}
</div>
</div>
<!-- Worker Nodes -->
<div class="p-4 rounded-xl bg-green-500/10 border border-green-500/30">
<h3 class="text-lg font-semibold text-green-400 mb-4 text-center">Worker Nodes</h3>
<p class="text-gray-400 text-sm text-center mb-4">The muscle - runs your actual workloads</p>
<div class="space-y-3">
{#each nodeComponents as component}
<button
class="w-full p-3 rounded-lg bg-gray-800/50 border border-gray-600 hover:border-gray-400 transition-all text-left"
class:border-green-500={selectedComponent === component.id}
on:click={() => selectedComponent = selectedComponent === component.id ? null : component.id}
>
<div class="text-white font-medium text-sm">{component.name}</div>
<div class="text-gray-500 text-xs mt-1">{component.description}</div>
</button>
{/each}
</div>
</div>
</div>
<!-- Selected Component Details -->
{#if selectedComponent}
{@const allComponents = [...controlPlaneComponents, ...nodeComponents]}
{@const component = allComponents.find(c => c.id === selectedComponent)}
{#if component}
<div class="mt-6 p-4 bg-gray-700/50 rounded-lg border border-gray-600">
<h4 class="text-white font-semibold mb-3">{component.name}</h4>
<ul class="grid md:grid-cols-2 gap-2">
{#each component.details as detail}
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-blue-400 mt-0.5">-</span>
{detail}
</li>
{/each}
</ul>
</div>
{/if}
{/if}
</div>
</section>
<!-- Control Plane Deep Dive -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Control Plane Components</h2>
<div class="space-y-4">
{#each controlPlaneComponents as component}
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-lg font-semibold text-white">{component.name}</h3>
<p class="text-gray-400 text-sm">{component.description}</p>
</div>
<span class="px-3 py-1 rounded-full text-xs font-medium bg-{component.color}-500/20 text-{component.color}-400">
Control Plane
</span>
</div>
<div class="grid md:grid-cols-2 gap-3">
{#each component.details as detail}
<div class="flex items-center gap-2 p-2 bg-gray-700/30 rounded-lg text-sm text-gray-300">
<span class="w-2 h-2 rounded-full bg-{component.color}-500"></span>
{detail}
</div>
{/each}
</div>
</div>
{/each}
</div>
</section>
<!-- Request Flow -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">How a Pod Gets Scheduled</h2>
<p class="text-gray-400 mb-6">Follow the journey from kubectl apply to running pod</p>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="relative">
<div class="absolute left-6 top-0 bottom-0 w-0.5 bg-blue-500/30"></div>
<div class="space-y-6">
{#each requestFlow as step}
<div class="relative pl-16">
<div class="absolute left-0 w-12 h-12 rounded-full bg-blue-500/20 border-2 border-blue-500 flex items-center justify-center text-blue-400 font-bold">
{step.step}
</div>
<div class="bg-gray-700/50 rounded-lg p-4">
<div class="text-white font-medium">{step.title}</div>
<div class="text-gray-400 text-sm mt-1">{step.desc}</div>
</div>
</div>
{/each}
</div>
</div>
</div>
</section>
<!-- Pod Lifecycle -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Pod Lifecycle</h2>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="flex flex-wrap items-center justify-center gap-4">
{#each podLifecycle as phase, i}
<div class="flex items-center">
<div class="p-3 rounded-lg bg-{phase.color}-500/20 border border-{phase.color}-500/30 text-center min-w-[120px]">
<div class="text-{phase.color}-400 font-semibold text-sm">{phase.phase}</div>
<div class="text-gray-500 text-xs mt-1">{phase.desc}</div>
</div>
{#if i < podLifecycle.length - 1}
<div class="mx-2 text-gray-500">-></div>
{/if}
</div>
{/each}
</div>
</div>
</section>
<!-- Key Concepts -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Key Kubernetes Objects</h2>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-blue-400 font-semibold mb-2">Pod</div>
<p class="text-gray-400 text-sm">Smallest deployable unit. One or more containers that share network/storage.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-green-400 font-semibold mb-2">Deployment</div>
<p class="text-gray-400 text-sm">Manages ReplicaSets. Handles rolling updates and rollbacks.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-purple-400 font-semibold mb-2">Service</div>
<p class="text-gray-400 text-sm">Stable network endpoint for pods. ClusterIP, NodePort, LoadBalancer.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-orange-400 font-semibold mb-2">ConfigMap</div>
<p class="text-gray-400 text-sm">Non-sensitive configuration data. Injected as env vars or files.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-red-400 font-semibold mb-2">Secret</div>
<p class="text-gray-400 text-sm">Sensitive data like passwords, tokens. Base64 encoded (not encrypted!).</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-yellow-400 font-semibold mb-2">Ingress</div>
<p class="text-gray-400 text-sm">HTTP routing rules. Path-based routing to Services.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-cyan-400 font-semibold mb-2">StatefulSet</div>
<p class="text-gray-400 text-sm">For stateful apps. Stable network identity and persistent storage.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-pink-400 font-semibold mb-2">DaemonSet</div>
<p class="text-gray-400 text-sm">Run pod on every node. Used for logging, monitoring agents.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-indigo-400 font-semibold mb-2">HPA</div>
<p class="text-gray-400 text-sm">Horizontal Pod Autoscaler. Scale pods based on CPU/memory/custom metrics.</p>
</div>
</div>
</section>
<!-- Networking -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Kubernetes Networking Model</h2>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="grid md:grid-cols-3 gap-6">
<div class="p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
<h4 class="text-blue-400 font-semibold mb-2">Pod-to-Pod</h4>
<p class="text-gray-400 text-sm mb-2">Every pod gets a unique IP. Pods can communicate directly without NAT.</p>
<div class="text-xs text-gray-500">Implemented by CNI plugin (Calico, Cilium, etc.)</div>
</div>
<div class="p-4 bg-green-500/10 rounded-lg border border-green-500/30">
<h4 class="text-green-400 font-semibold mb-2">Pod-to-Service</h4>
<p class="text-gray-400 text-sm mb-2">Services provide stable virtual IPs (ClusterIP). kube-proxy routes to pods.</p>
<div class="text-xs text-gray-500">Uses iptables or IPVS rules</div>
</div>
<div class="p-4 bg-purple-500/10 rounded-lg border border-purple-500/30">
<h4 class="text-purple-400 font-semibold mb-2">External-to-Service</h4>
<p class="text-gray-400 text-sm mb-2">NodePort, LoadBalancer, or Ingress expose services externally.</p>
<div class="text-xs text-gray-500">Ingress for HTTP, LoadBalancer for TCP/UDP</div>
</div>
</div>
</div>
</section>
<!-- Common Patterns -->
<section>
<h2 class="text-xl font-semibold text-white mb-4">Production Best Practices</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h4 class="text-green-400 font-medium mb-3">DO</h4>
<ul class="space-y-2">
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400">+</span>
Set resource requests and limits on all pods
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400">+</span>
Use liveness and readiness probes
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400">+</span>
Run multiple replicas with anti-affinity
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400">+</span>
Use namespaces for isolation
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400">+</span>
Backup etcd regularly
</li>
</ul>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h4 class="text-red-400 font-medium mb-3">DON'T</h4>
<ul class="space-y-2">
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400">-</span>
Run workloads on control plane nodes
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400">-</span>
Use :latest image tags in production
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400">-</span>
Store secrets in ConfigMaps
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400">-</span>
Skip pod disruption budgets
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400">-</span>
Ignore resource quotas in multi-tenant clusters
</li>
</ul>
</div>
</div>
</section>
</div>

View file

@ -0,0 +1,390 @@
<script lang="ts">
import GuidedWalkthrough from '$lib/components/diagrams/GuidedWalkthrough.svelte';
let showColdStart = true;
const coldStartPhases = [
{ name: 'Container Provisioning', duration: '~100ms', desc: 'Allocate compute resources based on memory config' },
{ name: 'Runtime Init', duration: '~50-200ms', desc: 'Load language runtime (Node.js, Python, Java)' },
{ name: 'Code Loading', duration: 'Variable', desc: 'Download and unpack function code' },
{ name: 'Dependency Init', duration: 'Variable', desc: 'Load libraries and initialize connections' }
];
const optimizations = [
{
category: 'Code Optimization',
items: [
{ name: 'Minimize package size', desc: 'Remove unused dependencies, use tree shaking', impact: 'High' },
{ name: 'Use lightweight libraries', desc: 'Choose smaller alternatives (e.g., date-fns over moment)', impact: 'Medium' },
{ name: 'Lazy load dependencies', desc: 'Only import modules when needed', impact: 'Medium' },
{ name: 'Use compiled languages', desc: 'Rust, Go have faster cold starts than Java', impact: 'High' }
]
},
{
category: 'AWS Features',
items: [
{ name: 'Provisioned Concurrency', desc: 'Pre-warm instances that are always ready', impact: 'Eliminates' },
{ name: 'SnapStart (Java/Python/.NET)', desc: 'Snapshot initialized state for faster restore', impact: 'High' },
{ name: 'Increase memory', desc: 'More memory = more CPU = faster init', impact: 'Medium' },
{ name: 'Use ARM (Graviton)', desc: 'Better price/performance ratio', impact: 'Low' }
]
},
{
category: 'Architecture',
items: [
{ name: 'Keep functions warm', desc: 'Schedule periodic invocations', impact: 'Medium' },
{ name: 'Avoid VPC when possible', desc: 'VPC adds ENI setup time', impact: 'Medium' },
{ name: 'Connection pooling', desc: 'Reuse DB connections across invocations', impact: 'Medium' },
{ name: 'Smaller functions', desc: 'Split large functions into focused units', impact: 'Medium' }
]
}
];
const runtimeComparison = [
{ runtime: 'Python', coldStart: '~200-400ms', warmStart: '~1-5ms', notes: 'Fast init, good for most use cases' },
{ runtime: 'Node.js', coldStart: '~200-400ms', warmStart: '~1-5ms', notes: 'Fast init, large ecosystem' },
{ runtime: 'Go', coldStart: '~100-200ms', warmStart: '<1ms', notes: 'Compiled, very fast' },
{ runtime: 'Rust', coldStart: '~100-200ms', warmStart: '<1ms', notes: 'Compiled, fastest cold starts' },
{ runtime: 'Java', coldStart: '~1-3s', warmStart: '~5-20ms', notes: 'JVM warmup, use SnapStart' },
{ runtime: '.NET', coldStart: '~500ms-1s', warmStart: '~5-10ms', notes: 'CLR init, improving with AOT' }
];
const coldStartWalkthrough = {
title: 'Lambda Cold Start Flow',
steps: [
{
title: 'Invocation Request',
content: 'API Gateway triggers Lambda function',
details: 'A request arrives but no warm execution environment is available.',
tips: ['Cold starts affect ~1% of requests', 'More common during traffic spikes']
},
{
title: 'Container Provisioning',
content: 'Lambda allocates compute resources',
details: 'AWS provisions a micro-VM with the configured memory. More memory = more CPU.',
tips: ['Memory range: 128MB - 10GB', 'CPU scales proportionally with memory']
},
{
title: 'Runtime Initialization',
content: 'Load Python/Node.js/Java runtime',
details: 'The language runtime is loaded and initialized in the container.',
tips: ['Python/Node.js: ~100-200ms', 'Java: ~500ms-1s (JVM startup)']
},
{
title: 'Function Code Loading',
content: 'Download and extract deployment package',
details: 'Your code is downloaded from S3 and extracted. Larger packages take longer.',
tips: ['Keep deployment package small', 'Use Lambda Layers for shared code']
},
{
title: 'Static Initialization',
content: 'Run code outside handler (global scope)',
details: 'Code at module level runs once per container. Initialize DB connections here.',
tips: ['Initialize SDK clients outside handler', 'This code runs once per container lifecycle']
},
{
title: 'Handler Execution',
content: 'Your function code runs',
details: 'Finally, your handler function executes. Container is now "warm".',
tips: ['Subsequent invocations skip steps 1-5', 'Warm container reused for ~15 minutes']
}
]
};
</script>
<svelte:head>
<title>Serverless - Cold & Warm Starts</title>
</svelte:head>
<div class="max-w-6xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-2">Serverless Computing</h1>
<p class="text-gray-400 mb-8">Understanding cold starts, warm starts, and optimization strategies</p>
<!-- Cold vs Warm Visual -->
<section class="mb-12">
<div class="flex gap-4 mb-6">
<button
class="px-4 py-2 rounded-lg font-medium transition-all
{showColdStart ? 'bg-blue-500 text-white' : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
on:click={() => showColdStart = true}
>
Cold Start
</button>
<button
class="px-4 py-2 rounded-lg font-medium transition-all
{!showColdStart ? 'bg-green-500 text-white' : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
on:click={() => showColdStart = false}
>
Warm Start
</button>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
{#if showColdStart}
<div class="mb-4">
<h3 class="text-xl font-semibold text-blue-400 mb-2">Cold Start</h3>
<p class="text-gray-400">No existing execution environment - must provision new container</p>
</div>
<!-- Cold Start Timeline -->
<div class="relative">
<div class="absolute left-0 top-0 bottom-0 w-1 bg-blue-500/30 rounded"></div>
<div class="space-y-4 pl-8">
{#each coldStartPhases as phase, i}
<div class="relative">
<div class="absolute -left-8 w-4 h-4 rounded-full bg-blue-500 border-4 border-gray-800"></div>
<div class="bg-gray-700/50 rounded-lg p-4">
<div class="flex items-center justify-between mb-1">
<span class="text-white font-medium">{phase.name}</span>
<span class="text-blue-400 text-sm font-mono">{phase.duration}</span>
</div>
<p class="text-gray-400 text-sm">{phase.desc}</p>
</div>
</div>
{/each}
<div class="relative">
<div class="absolute -left-8 w-4 h-4 rounded-full bg-green-500 border-4 border-gray-800"></div>
<div class="bg-green-500/20 rounded-lg p-4 border border-green-500/30">
<div class="flex items-center justify-between mb-1">
<span class="text-white font-medium">Handler Execution</span>
<span class="text-green-400 text-sm font-mono">Your code</span>
</div>
<p class="text-gray-400 text-sm">Finally, your function handler runs</p>
</div>
</div>
</div>
</div>
<div class="mt-6 p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
<div class="text-blue-300 font-medium">Total Cold Start: 200ms - 3s+</div>
<div class="text-gray-400 text-sm mt-1">Depends on runtime, package size, and dependencies</div>
</div>
{:else}
<div class="mb-4">
<h3 class="text-xl font-semibold text-green-400 mb-2">Warm Start</h3>
<p class="text-gray-400">Reuses existing execution environment - skips initialization</p>
</div>
<div class="relative">
<div class="absolute left-0 top-0 bottom-0 w-1 bg-gray-600 rounded"></div>
<div class="space-y-4 pl-8">
{#each coldStartPhases as phase}
<div class="relative opacity-40">
<div class="absolute -left-8 w-4 h-4 rounded-full bg-gray-600 border-4 border-gray-800"></div>
<div class="bg-gray-700/30 rounded-lg p-4">
<div class="flex items-center justify-between">
<span class="text-gray-500 font-medium line-through">{phase.name}</span>
<span class="text-gray-600 text-sm">SKIPPED</span>
</div>
</div>
</div>
{/each}
<div class="relative">
<div class="absolute -left-8 w-4 h-4 rounded-full bg-green-500 border-4 border-gray-800"></div>
<div class="bg-green-500/20 rounded-lg p-4 border border-green-500/30">
<div class="flex items-center justify-between mb-1">
<span class="text-white font-medium">Handler Execution</span>
<span class="text-green-400 text-sm font-mono">~1-5ms</span>
</div>
<p class="text-gray-400 text-sm">Jump straight to your code!</p>
</div>
</div>
</div>
</div>
<div class="mt-6 p-4 bg-green-500/10 rounded-lg border border-green-500/30">
<div class="text-green-300 font-medium">Total Warm Start: 1-5ms</div>
<div class="text-gray-400 text-sm mt-1">Container reused for ~5-15 minutes of inactivity</div>
</div>
{/if}
</div>
</section>
<!-- Cold Start Walkthrough -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Cold Start Deep Dive</h2>
<GuidedWalkthrough {...coldStartWalkthrough} />
</section>
<!-- Runtime Comparison -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Runtime Comparison</h2>
<p class="text-gray-400 mb-6">Cold start times vary significantly by language runtime</p>
<div class="bg-gray-800/50 rounded-xl border border-gray-700 overflow-hidden">
<table class="w-full">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left p-4 text-gray-400 font-medium">Runtime</th>
<th class="text-left p-4 text-blue-400 font-medium">Cold Start</th>
<th class="text-left p-4 text-green-400 font-medium">Warm Start</th>
<th class="text-left p-4 text-gray-400 font-medium">Notes</th>
</tr>
</thead>
<tbody>
{#each runtimeComparison as rt, i}
<tr class="border-b border-gray-700/50 {i % 2 === 0 ? 'bg-gray-800/30' : ''}">
<td class="p-4 text-white font-medium">{rt.runtime}</td>
<td class="p-4 text-blue-300 font-mono">{rt.coldStart}</td>
<td class="p-4 text-green-300 font-mono">{rt.warmStart}</td>
<td class="p-4 text-gray-400 text-sm">{rt.notes}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</section>
<!-- Optimization Strategies -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Optimization Strategies</h2>
<div class="space-y-6">
{#each optimizations as category}
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h3 class="text-lg font-semibold text-white mb-4">{category.category}</h3>
<div class="grid md:grid-cols-2 gap-4">
{#each category.items as item}
<div class="p-4 bg-gray-700/30 rounded-lg">
<div class="flex items-center justify-between mb-2">
<span class="text-white font-medium">{item.name}</span>
<span class="px-2 py-0.5 rounded text-xs font-medium
{item.impact === 'Eliminates' ? 'bg-green-500/20 text-green-400' : ''}
{item.impact === 'High' ? 'bg-blue-500/20 text-blue-400' : ''}
{item.impact === 'Medium' ? 'bg-yellow-500/20 text-yellow-400' : ''}
{item.impact === 'Low' ? 'bg-gray-500/20 text-gray-400' : ''}">
{item.impact} impact
</span>
</div>
<p class="text-gray-400 text-sm">{item.desc}</p>
</div>
{/each}
</div>
</div>
{/each}
</div>
</section>
<!-- Provisioned Concurrency vs SnapStart -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Provisioned Concurrency vs SnapStart</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-purple-500/30">
<h3 class="text-lg font-semibold text-purple-400 mb-4">Provisioned Concurrency</h3>
<p class="text-gray-400 text-sm mb-4">
Pre-initialize a specified number of execution environments that are always ready.
</p>
<div class="space-y-2 mb-4">
<div class="flex items-center gap-2 text-sm">
<span class="text-green-400">+</span>
<span class="text-gray-300">Eliminates cold starts completely</span>
</div>
<div class="flex items-center gap-2 text-sm">
<span class="text-green-400">+</span>
<span class="text-gray-300">Works with any runtime</span>
</div>
<div class="flex items-center gap-2 text-sm">
<span class="text-red-400">-</span>
<span class="text-gray-300">Costs money even when idle</span>
</div>
<div class="flex items-center gap-2 text-sm">
<span class="text-red-400">-</span>
<span class="text-gray-300">Must estimate concurrency needs</span>
</div>
</div>
<div class="p-3 bg-purple-500/10 rounded-lg">
<div class="text-purple-300 text-sm">Best for: Consistent, predictable traffic patterns</div>
</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-orange-500/30">
<h3 class="text-lg font-semibold text-orange-400 mb-4">SnapStart</h3>
<p class="text-gray-400 text-sm mb-4">
Snapshot initialized state and restore from cache instead of re-initializing.
</p>
<div class="space-y-2 mb-4">
<div class="flex items-center gap-2 text-sm">
<span class="text-green-400">+</span>
<span class="text-gray-300">Up to 10x faster cold starts</span>
</div>
<div class="flex items-center gap-2 text-sm">
<span class="text-green-400">+</span>
<span class="text-gray-300">No additional cost</span>
</div>
<div class="flex items-center gap-2 text-sm">
<span class="text-red-400">-</span>
<span class="text-gray-300">Only Java, Python, .NET</span>
</div>
<div class="flex items-center gap-2 text-sm">
<span class="text-red-400">-</span>
<span class="text-gray-300">May need code changes for uniqueness</span>
</div>
</div>
<div class="p-3 bg-orange-500/10 rounded-lg">
<div class="text-orange-300 text-sm">Best for: Java/.NET with variable traffic</div>
</div>
</div>
</div>
</section>
<!-- Best Practices -->
<section>
<h2 class="text-xl font-semibold text-white mb-4">Best Practices</h2>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="grid md:grid-cols-2 gap-6">
<div>
<h4 class="text-green-400 font-medium mb-3">DO</h4>
<ul class="space-y-2">
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400 mt-0.5">+</span>
Initialize SDK clients outside the handler (global scope)
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400 mt-0.5">+</span>
Reuse database connections across invocations
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400 mt-0.5">+</span>
Use Lambda Layers for shared dependencies
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400 mt-0.5">+</span>
Monitor INIT duration in CloudWatch
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-green-400 mt-0.5">+</span>
Use ARM architecture for cost savings
</li>
</ul>
</div>
<div>
<h4 class="text-red-400 font-medium mb-3">DON'T</h4>
<ul class="space-y-2">
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400 mt-0.5">-</span>
Create new connections on every invocation
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400 mt-0.5">-</span>
Bundle unnecessary dependencies
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400 mt-0.5">-</span>
Use VPC unless you need private resources
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400 mt-0.5">-</span>
Ignore cold start metrics in production
</li>
<li class="flex items-start gap-2 text-gray-300 text-sm">
<span class="text-red-400 mt-0.5">-</span>
Use Java without SnapStart for user-facing APIs
</li>
</ul>
</div>
</div>
</div>
</section>
</div>

View file

@ -0,0 +1,442 @@
<script lang="ts">
import GuidedWalkthrough from '$lib/components/diagrams/GuidedWalkthrough.svelte';
let selectedPattern: string | null = null;
const patterns = [
{
id: 'cache-aside',
name: 'Cache-Aside (Lazy Loading)',
description: 'Application manages the cache - loads data on cache miss',
color: 'blue',
steps: [
'Application checks cache for data',
'If cache hit: return cached data',
'If cache miss: query database',
'Store result in cache',
'Return data to caller'
],
pros: [
'Only caches what is actually used',
'Simple to implement',
'Cache failure does not break the app',
'Works with any database'
],
cons: [
'First request always slow (cache miss)',
'Data can become stale',
'Application must handle cache logic'
],
useCase: 'Read-heavy workloads with infrequent updates'
},
{
id: 'write-through',
name: 'Write-Through',
description: 'Data written to cache and database simultaneously',
color: 'green',
steps: [
'Application writes data',
'Write to cache first',
'Write to database',
'Both writes must succeed',
'Return success to caller'
],
pros: [
'Cache always has fresh data',
'No stale data issues',
'Simple mental model'
],
cons: [
'Write latency increased (two writes)',
'Cache may have unused data',
'Risk of inconsistency if one write fails'
],
useCase: 'Data that is read immediately after writing'
},
{
id: 'write-behind',
name: 'Write-Behind (Write-Back)',
description: 'Write to cache immediately, async write to database later',
color: 'purple',
steps: [
'Application writes data',
'Write to cache immediately',
'Return success to caller',
'Async: batch writes to database',
'Confirm persistence'
],
pros: [
'Fastest write performance',
'Batches database writes',
'Reduces database load'
],
cons: [
'Risk of data loss if cache fails',
'Complex to implement correctly',
'Eventual consistency'
],
useCase: 'Write-heavy workloads where some data loss is acceptable'
},
{
id: 'read-through',
name: 'Read-Through',
description: 'Cache automatically fetches from database on miss',
color: 'orange',
steps: [
'Application requests data from cache',
'Cache checks if data exists',
'If miss: cache fetches from database',
'Cache stores and returns data',
'Subsequent reads served from cache'
],
pros: [
'Simpler application code',
'Cache handles all logic',
'Consistent caching behavior'
],
cons: [
'Cache must understand data source',
'First request still slow',
'Less flexible than cache-aside'
],
useCase: 'When you want caching logic abstracted from application'
},
{
id: 'refresh-ahead',
name: 'Refresh-Ahead (Pre-fetching)',
description: 'Proactively refresh cache before expiration',
color: 'pink',
steps: [
'Cache tracks TTL of entries',
'Before TTL expires, trigger refresh',
'Fetch fresh data from database',
'Update cache in background',
'Users always get cached data'
],
pros: [
'No cache miss latency',
'Always fresh data',
'Predictable performance'
],
cons: [
'Complex to implement',
'May refresh unused data',
'Higher database load'
],
useCase: 'Hot data that must always be fast and fresh'
}
];
const evictionStrategies = [
{
name: 'LRU (Least Recently Used)',
description: 'Evict the item that has not been accessed for the longest time',
best: 'General purpose, most common'
},
{
name: 'LFU (Least Frequently Used)',
description: 'Evict the item with the fewest accesses',
best: 'When access frequency matters more than recency'
},
{
name: 'FIFO (First In, First Out)',
description: 'Evict the oldest item regardless of access pattern',
best: 'Simple, predictable behavior'
},
{
name: 'TTL (Time To Live)',
description: 'Evict when time expires, regardless of space',
best: 'Data that becomes stale over time'
},
{
name: 'Random',
description: 'Evict a random item',
best: 'When access patterns are unpredictable'
}
];
const cacheAsideWalkthrough = {
title: 'Cache-Aside Pattern Flow',
steps: [
{
title: 'Application Requests Data',
content: 'User requests user profile for user_id=123',
details: 'The application receives a request that needs data from the database.',
tips: ['Common entry point: API endpoint, service method']
},
{
title: 'Check Cache First',
content: 'GET user:123 from Redis',
details: 'Application checks if the data exists in cache before hitting the database.',
tips: ['Use consistent key naming: entity:id', 'Handle cache connection failures gracefully']
},
{
title: 'Cache Hit - Return Immediately',
content: 'Data found! Return cached user profile',
details: 'If data is in cache, return it immediately. This is the fast path.',
tips: ['Typical cache hit latency: < 1ms', 'Log cache hits for monitoring']
},
{
title: 'Cache Miss - Query Database',
content: 'SELECT * FROM users WHERE id = 123',
details: 'If cache miss, query the primary database for the data.',
tips: ['This is the slow path', 'Consider query optimization']
},
{
title: 'Store in Cache',
content: 'SET user:123 with TTL 3600 seconds',
details: 'After getting data from DB, store it in cache for future requests.',
tips: ['Always set a TTL to prevent stale data', 'Consider cache size limits']
},
{
title: 'Return Data to Caller',
content: 'Return user profile to client',
details: 'Finally return the data. Next request will hit the cache.',
tips: ['Monitor cache hit rate', 'Aim for > 90% hit rate']
}
]
};
</script>
<svelte:head>
<title>Caching Patterns</title>
</svelte:head>
<div class="max-w-6xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-2">Caching Patterns</h1>
<p class="text-gray-400 mb-8">Understanding different caching strategies and when to use each</p>
<!-- Overview -->
<section class="mb-12">
<div class="bg-gradient-to-r from-blue-500/10 to-purple-500/10 rounded-xl p-6 border border-gray-700">
<h2 class="text-lg font-semibold text-white mb-4">Why Caching Matters</h2>
<div class="grid md:grid-cols-4 gap-4 text-center">
<div class="p-4 bg-gray-800/50 rounded-lg">
<div class="text-3xl font-bold text-blue-400">10-100x</div>
<div class="text-gray-400 text-sm">Faster than database</div>
</div>
<div class="p-4 bg-gray-800/50 rounded-lg">
<div class="text-3xl font-bold text-green-400">&lt;1ms</div>
<div class="text-gray-400 text-sm">Cache read latency</div>
</div>
<div class="p-4 bg-gray-800/50 rounded-lg">
<div class="text-3xl font-bold text-purple-400">90%+</div>
<div class="text-gray-400 text-sm">Target hit rate</div>
</div>
<div class="p-4 bg-gray-800/50 rounded-lg">
<div class="text-3xl font-bold text-orange-400">$$$</div>
<div class="text-gray-400 text-sm">Reduced DB costs</div>
</div>
</div>
</div>
</section>
<!-- Pattern Cards -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Caching Patterns</h2>
<div class="grid gap-6">
{#each patterns as pattern}
<div
class="bg-gray-800/50 rounded-xl border border-gray-700 overflow-hidden cursor-pointer transition-all hover:border-gray-500"
class:border-blue-500={selectedPattern === pattern.id && pattern.color === 'blue'}
class:border-green-500={selectedPattern === pattern.id && pattern.color === 'green'}
class:border-purple-500={selectedPattern === pattern.id && pattern.color === 'purple'}
class:border-orange-500={selectedPattern === pattern.id && pattern.color === 'orange'}
class:border-pink-500={selectedPattern === pattern.id && pattern.color === 'pink'}
on:click={() => selectedPattern = selectedPattern === pattern.id ? null : pattern.id}
on:keydown={(e) => e.key === 'Enter' && (selectedPattern = selectedPattern === pattern.id ? null : pattern.id)}
role="button"
tabindex="0"
>
<div class="p-6">
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-lg font-semibold text-white">{pattern.name}</h3>
<p class="text-gray-400 text-sm">{pattern.description}</p>
</div>
<span class="px-3 py-1 rounded-full text-xs font-medium
{pattern.color === 'blue' ? 'bg-blue-500/20 text-blue-400' : ''}
{pattern.color === 'green' ? 'bg-green-500/20 text-green-400' : ''}
{pattern.color === 'purple' ? 'bg-purple-500/20 text-purple-400' : ''}
{pattern.color === 'orange' ? 'bg-orange-500/20 text-orange-400' : ''}
{pattern.color === 'pink' ? 'bg-pink-500/20 text-pink-400' : ''}">
{pattern.useCase}
</span>
</div>
<!-- Flow Steps -->
<div class="flex items-center gap-2 mb-4 overflow-x-auto pb-2">
{#each pattern.steps as step, i}
<div class="flex items-center">
<div class="px-3 py-2 bg-gray-700/50 rounded-lg text-sm text-gray-300 whitespace-nowrap">
{step}
</div>
{#if i < pattern.steps.length - 1}
<span class="mx-2 text-gray-500">-></span>
{/if}
</div>
{/each}
</div>
{#if selectedPattern === pattern.id}
<div class="grid md:grid-cols-2 gap-4 pt-4 border-t border-gray-700">
<div>
<h4 class="text-green-400 font-medium mb-2">Pros</h4>
<ul class="space-y-1">
{#each pattern.pros as pro}
<li class="text-gray-300 text-sm flex items-start gap-2">
<span class="text-green-400">+</span>
{pro}
</li>
{/each}
</ul>
</div>
<div>
<h4 class="text-red-400 font-medium mb-2">Cons</h4>
<ul class="space-y-1">
{#each pattern.cons as con}
<li class="text-gray-300 text-sm flex items-start gap-2">
<span class="text-red-400">-</span>
{con}
</li>
{/each}
</ul>
</div>
</div>
{/if}
</div>
</div>
{/each}
</div>
</section>
<!-- Cache-Aside Walkthrough -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Cache-Aside in Detail</h2>
<p class="text-gray-400 mb-6">The most common caching pattern - walk through each step.</p>
<GuidedWalkthrough {...cacheAsideWalkthrough} />
</section>
<!-- Eviction Strategies -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Eviction Strategies</h2>
<p class="text-gray-400 mb-6">When cache is full, which items should be removed?</p>
<div class="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
{#each evictionStrategies as strategy}
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<h3 class="text-white font-semibold mb-2">{strategy.name}</h3>
<p class="text-gray-400 text-sm mb-3">{strategy.description}</p>
<div class="text-xs text-blue-400">Best for: {strategy.best}</div>
</div>
{/each}
</div>
</section>
<!-- Common Issues -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Common Caching Problems</h2>
<div class="grid md:grid-cols-3 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-red-500/30">
<h3 class="text-red-400 font-semibold mb-3">Cache Stampede</h3>
<p class="text-gray-400 text-sm mb-4">
Many requests hit an expired cache key simultaneously, all querying the database at once.
</p>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-green-400 text-sm font-medium mb-1">Solutions:</div>
<ul class="text-gray-300 text-sm space-y-1">
<li>- Lock/mutex on cache refresh</li>
<li>- Staggered TTLs</li>
<li>- Background refresh</li>
</ul>
</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-yellow-500/30">
<h3 class="text-yellow-400 font-semibold mb-3">Stale Data</h3>
<p class="text-gray-400 text-sm mb-4">
Cache contains outdated data that does not reflect current database state.
</p>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-green-400 text-sm font-medium mb-1">Solutions:</div>
<ul class="text-gray-300 text-sm space-y-1">
<li>- Appropriate TTL values</li>
<li>- Cache invalidation on writes</li>
<li>- Write-through pattern</li>
</ul>
</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-orange-500/30">
<h3 class="text-orange-400 font-semibold mb-3">Cache Penetration</h3>
<p class="text-gray-400 text-sm mb-4">
Requests for non-existent data bypass cache and always hit the database.
</p>
<div class="p-3 bg-gray-700/50 rounded-lg">
<div class="text-green-400 text-sm font-medium mb-1">Solutions:</div>
<ul class="text-gray-300 text-sm space-y-1">
<li>- Cache negative results</li>
<li>- Bloom filter for existence check</li>
<li>- Input validation</li>
</ul>
</div>
</div>
</div>
</section>
<!-- Redis vs Memcached -->
<section>
<h2 class="text-xl font-semibold text-white mb-4">Redis vs Memcached</h2>
<div class="bg-gray-800/50 rounded-xl border border-gray-700 overflow-hidden">
<table class="w-full">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left p-4 text-gray-400 font-medium">Feature</th>
<th class="text-left p-4 text-red-400 font-medium">Redis</th>
<th class="text-left p-4 text-green-400 font-medium">Memcached</th>
</tr>
</thead>
<tbody>
<tr class="border-b border-gray-700/50">
<td class="p-4 text-white">Data Structures</td>
<td class="p-4 text-gray-300">Strings, Lists, Sets, Hashes, Sorted Sets</td>
<td class="p-4 text-gray-300">Strings only</td>
</tr>
<tr class="border-b border-gray-700/50 bg-gray-800/30">
<td class="p-4 text-white">Persistence</td>
<td class="p-4 text-gray-300">Yes (RDB, AOF)</td>
<td class="p-4 text-gray-300">No</td>
</tr>
<tr class="border-b border-gray-700/50">
<td class="p-4 text-white">Replication</td>
<td class="p-4 text-gray-300">Built-in</td>
<td class="p-4 text-gray-300">No</td>
</tr>
<tr class="border-b border-gray-700/50 bg-gray-800/30">
<td class="p-4 text-white">Pub/Sub</td>
<td class="p-4 text-gray-300">Yes</td>
<td class="p-4 text-gray-300">No</td>
</tr>
<tr class="border-b border-gray-700/50">
<td class="p-4 text-white">Lua Scripting</td>
<td class="p-4 text-gray-300">Yes</td>
<td class="p-4 text-gray-300">No</td>
</tr>
<tr class="border-b border-gray-700/50 bg-gray-800/30">
<td class="p-4 text-white">Memory Efficiency</td>
<td class="p-4 text-gray-300">Good</td>
<td class="p-4 text-gray-300">Better (simpler)</td>
</tr>
<tr>
<td class="p-4 text-white">Best For</td>
<td class="p-4 text-gray-300">Feature-rich caching, sessions, queues</td>
<td class="p-4 text-gray-300">Simple, high-throughput key-value cache</td>
</tr>
</tbody>
</table>
</div>
</section>
</div>

View file

@ -0,0 +1,396 @@
<script lang="ts">
import ComparisonTable from '$lib/components/diagrams/ComparisonTable.svelte';
let selectedType: 'sync' | 'async' | null = null;
const replicationTypes = [
{
id: 'leader-follower',
name: 'Leader-Follower (Primary-Replica)',
description: 'One leader handles writes, followers replicate and serve reads',
pros: [
'Simple to understand and implement',
'Read scalability (add more followers)',
'Automatic failover possible'
],
cons: [
'Single point of failure for writes',
'Replication lag can cause stale reads',
'Failover can cause data loss'
],
useCase: 'Most common - PostgreSQL, MySQL, MongoDB'
},
{
id: 'multi-leader',
name: 'Multi-Leader (Master-Master)',
description: 'Multiple nodes accept writes, replicate to each other',
pros: [
'Write availability in multiple regions',
'Lower write latency (local writes)',
'No single point of failure'
],
cons: [
'Conflict resolution is complex',
'Harder to reason about consistency',
'More network traffic'
],
useCase: 'Multi-datacenter deployments, CRDTs'
},
{
id: 'leaderless',
name: 'Leaderless',
description: 'Any node can accept reads and writes (quorum-based)',
pros: [
'No single point of failure',
'High availability',
'Tunable consistency'
],
cons: [
'Quorum overhead on every write',
'Complex conflict resolution',
'Harder to achieve strong consistency'
],
useCase: 'DynamoDB, Cassandra, Riak'
}
];
const comparisonOptions = [
{
id: 'sync',
name: 'Synchronous Replication',
description: 'Wait for replica confirmation before acknowledging write',
whenToUse: [
'Zero data loss is critical (financial systems)',
'Strong consistency required',
'Regulatory compliance (GDPR, SOC2)',
'Hot standby for instant failover',
'Replicas in same datacenter (low latency)'
],
whenNotToUse: [
'Replicas are geographically distant',
'Write latency is critical',
'High write throughput needed',
'Some data loss is acceptable'
]
},
{
id: 'async',
name: 'Asynchronous Replication',
description: 'Acknowledge write immediately, replicate in background',
whenToUse: [
'Geographic distribution (cross-region)',
'Write performance is priority',
'Read scaling is the goal',
'Some data loss on failover is acceptable',
'Analytics/reporting replicas'
],
whenNotToUse: [
'Cannot tolerate any data loss',
'Need strong consistency guarantees',
'Regulatory requirements for durability',
'Reads must always be consistent'
]
}
];
const lagMetrics = [
{ name: 'Write Position', desc: 'Where leader has written (WAL/binlog position)' },
{ name: 'Sent Position', desc: 'What has been sent to replica' },
{ name: 'Flush Position', desc: 'What replica has written to disk' },
{ name: 'Replay Position', desc: 'What replica has applied to database' }
];
</script>
<svelte:head>
<title>Database Replication</title>
</svelte:head>
<div class="max-w-6xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-2">Database Replication</h1>
<p class="text-gray-400 mb-8">Understanding replication topologies, sync vs async, and consistency trade-offs</p>
<!-- Why Replication -->
<section class="mb-12">
<div class="bg-gradient-to-r from-blue-500/10 to-purple-500/10 rounded-xl p-6 border border-gray-700">
<h2 class="text-lg font-semibold text-white mb-4">Why Replicate?</h2>
<div class="grid md:grid-cols-4 gap-4">
<div class="p-4 bg-gray-800/50 rounded-lg text-center">
<div class="text-2xl mb-2">HA</div>
<div class="text-white font-medium">High Availability</div>
<div class="text-gray-400 text-sm mt-1">Survive node failures</div>
</div>
<div class="p-4 bg-gray-800/50 rounded-lg text-center">
<div class="text-2xl mb-2">R</div>
<div class="text-white font-medium">Read Scaling</div>
<div class="text-gray-400 text-sm mt-1">Distribute read load</div>
</div>
<div class="p-4 bg-gray-800/50 rounded-lg text-center">
<div class="text-2xl mb-2">GEO</div>
<div class="text-white font-medium">Geographic</div>
<div class="text-gray-400 text-sm mt-1">Data closer to users</div>
</div>
<div class="p-4 bg-gray-800/50 rounded-lg text-center">
<div class="text-2xl mb-2">DR</div>
<div class="text-white font-medium">Disaster Recovery</div>
<div class="text-gray-400 text-sm mt-1">Backup in another region</div>
</div>
</div>
</div>
</section>
<!-- Sync vs Async Toggle -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Synchronous vs Asynchronous</h2>
<div class="flex gap-4 mb-6">
<button
class="px-4 py-2 rounded-lg font-medium transition-all
{selectedType === 'sync' ? 'bg-blue-500 text-white' : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
on:click={() => selectedType = selectedType === 'sync' ? null : 'sync'}
>
Synchronous
</button>
<button
class="px-4 py-2 rounded-lg font-medium transition-all
{selectedType === 'async' ? 'bg-green-500 text-white' : 'bg-gray-700 text-gray-300 hover:bg-gray-600'}"
on:click={() => selectedType = selectedType === 'async' ? null : 'async'}
>
Asynchronous
</button>
</div>
<div class="grid md:grid-cols-2 gap-6">
<!-- Sync -->
<div class="bg-gray-800/50 rounded-xl p-6 border {selectedType === 'sync' ? 'border-blue-500' : 'border-gray-700'}">
<h3 class="text-lg font-semibold text-blue-400 mb-4">Synchronous Replication</h3>
<div class="space-y-3 mb-6">
<div class="flex items-center gap-3 p-3 bg-gray-700/50 rounded-lg">
<div class="w-10 h-10 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 font-bold">1</div>
<div class="flex-1">
<div class="text-white text-sm">Client writes to leader</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-gray-700/50 rounded-lg">
<div class="w-10 h-10 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 font-bold">2</div>
<div class="flex-1">
<div class="text-white text-sm">Leader sends to replica</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-blue-500/20 rounded-lg border border-blue-500/30">
<div class="w-10 h-10 rounded-full bg-blue-500/30 flex items-center justify-center text-blue-400 font-bold">3</div>
<div class="flex-1">
<div class="text-white text-sm font-medium">WAIT for replica confirmation</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-gray-700/50 rounded-lg">
<div class="w-10 h-10 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 font-bold">4</div>
<div class="flex-1">
<div class="text-white text-sm">Acknowledge to client</div>
</div>
</div>
</div>
<div class="p-3 bg-blue-500/10 rounded-lg border border-blue-500/30">
<div class="text-blue-300 text-sm">Guarantee: Data exists on N nodes before success</div>
</div>
</div>
<!-- Async -->
<div class="bg-gray-800/50 rounded-xl p-6 border {selectedType === 'async' ? 'border-green-500' : 'border-gray-700'}">
<h3 class="text-lg font-semibold text-green-400 mb-4">Asynchronous Replication</h3>
<div class="space-y-3 mb-6">
<div class="flex items-center gap-3 p-3 bg-gray-700/50 rounded-lg">
<div class="w-10 h-10 rounded-full bg-green-500/20 flex items-center justify-center text-green-400 font-bold">1</div>
<div class="flex-1">
<div class="text-white text-sm">Client writes to leader</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-green-500/20 rounded-lg border border-green-500/30">
<div class="w-10 h-10 rounded-full bg-green-500/30 flex items-center justify-center text-green-400 font-bold">2</div>
<div class="flex-1">
<div class="text-white text-sm font-medium">Acknowledge immediately</div>
</div>
</div>
<div class="flex items-center gap-3 p-3 bg-gray-700/50 rounded-lg opacity-60">
<div class="w-10 h-10 rounded-full bg-green-500/20 flex items-center justify-center text-green-400 font-bold">3</div>
<div class="flex-1">
<div class="text-white text-sm">Replicate in background</div>
<div class="text-gray-500 text-xs">May lag behind leader</div>
</div>
</div>
</div>
<div class="p-3 bg-green-500/10 rounded-lg border border-green-500/30">
<div class="text-green-300 text-sm">Trade-off: Faster writes, but possible data loss on failover</div>
</div>
</div>
</div>
</section>
<!-- When to Use Each -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">When to Use Each</h2>
<ComparisonTable options={comparisonOptions} />
</section>
<!-- Replication Topologies -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Replication Topologies</h2>
<div class="space-y-6">
{#each replicationTypes as type}
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="flex items-start justify-between mb-4">
<div>
<h3 class="text-lg font-semibold text-white">{type.name}</h3>
<p class="text-gray-400 text-sm">{type.description}</p>
</div>
<span class="px-3 py-1 rounded-full text-xs font-medium bg-purple-500/20 text-purple-400">
{type.useCase}
</span>
</div>
<div class="grid md:grid-cols-2 gap-4">
<div>
<h4 class="text-green-400 font-medium mb-2 text-sm">Pros</h4>
<ul class="space-y-1">
{#each type.pros as pro}
<li class="text-gray-300 text-sm flex items-start gap-2">
<span class="text-green-400">+</span>
{pro}
</li>
{/each}
</ul>
</div>
<div>
<h4 class="text-red-400 font-medium mb-2 text-sm">Cons</h4>
<ul class="space-y-1">
{#each type.cons as con}
<li class="text-gray-300 text-sm flex items-start gap-2">
<span class="text-red-400">-</span>
{con}
</li>
{/each}
</ul>
</div>
</div>
</div>
{/each}
</div>
</section>
<!-- Replication Lag -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Understanding Replication Lag</h2>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<p class="text-gray-400 mb-6">
Replication lag is the delay between when data is written to the leader and when it appears on replicas.
Multiple metrics help pinpoint where the lag is occurring.
</p>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
{#each lagMetrics as metric, i}
<div class="p-4 bg-gray-700/50 rounded-lg">
<div class="flex items-center gap-2 mb-2">
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-xs font-bold">{i + 1}</div>
<span class="text-white font-medium text-sm">{metric.name}</span>
</div>
<p class="text-gray-400 text-xs">{metric.desc}</p>
</div>
{/each}
</div>
<div class="bg-gray-700/50 rounded-lg p-4">
<h4 class="text-white font-medium mb-2">PostgreSQL: Check Replication Lag</h4>
<pre class="text-sm text-gray-300 overflow-x-auto"><code>SELECT client_addr, state,
pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes,
replay_lag
FROM pg_stat_replication;</code></pre>
</div>
</div>
</section>
<!-- Failover -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Failover Considerations</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-yellow-500/30">
<h3 class="text-yellow-400 font-semibold mb-4">Automatic Failover</h3>
<p class="text-gray-400 text-sm mb-4">
System automatically promotes replica when leader fails. Fast recovery but risks split-brain.
</p>
<ul class="space-y-2 text-sm">
<li class="flex items-start gap-2 text-gray-300">
<span class="text-yellow-400">!</span>
Requires proper fencing to prevent split-brain
</li>
<li class="flex items-start gap-2 text-gray-300">
<span class="text-yellow-400">!</span>
May lose uncommitted transactions (async)
</li>
<li class="flex items-start gap-2 text-gray-300">
<span class="text-yellow-400">!</span>
Applications may need reconnection logic
</li>
</ul>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-blue-500/30">
<h3 class="text-blue-400 font-semibold mb-4">Manual Failover</h3>
<p class="text-gray-400 text-sm mb-4">
Operator manually promotes replica after verifying state. Slower but safer.
</p>
<ul class="space-y-2 text-sm">
<li class="flex items-start gap-2 text-gray-300">
<span class="text-blue-400">-</span>
Verify replica is fully caught up
</li>
<li class="flex items-start gap-2 text-gray-300">
<span class="text-blue-400">-</span>
Stop writes to old leader first
</li>
<li class="flex items-start gap-2 text-gray-300">
<span class="text-blue-400">-</span>
Update DNS/connection strings
</li>
</ul>
</div>
</div>
</section>
<!-- Common Patterns -->
<section>
<h2 class="text-xl font-semibold text-white mb-4">Common Patterns</h2>
<div class="grid md:grid-cols-3 gap-4">
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-blue-400 font-semibold mb-2">Read Replicas</div>
<p class="text-gray-400 text-sm">Route reads to replicas, writes to leader. Scale read capacity horizontally.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-green-400 font-semibold mb-2">Hot Standby</div>
<p class="text-gray-400 text-sm">Sync replica ready for instant failover. Zero data loss guarantee.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-purple-400 font-semibold mb-2">Delayed Replica</div>
<p class="text-gray-400 text-sm">Intentionally lag behind. Protection against accidental deletes.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-orange-400 font-semibold mb-2">Cascading Replication</div>
<p class="text-gray-400 text-sm">Replicas replicate to other replicas. Reduce load on leader.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-red-400 font-semibold mb-2">Cross-Region Replica</div>
<p class="text-gray-400 text-sm">Async replica in another region. Disaster recovery and local reads.</p>
</div>
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700">
<div class="text-cyan-400 font-semibold mb-2">Logical Replication</div>
<p class="text-gray-400 text-sm">Replicate specific tables/databases. Schema changes, version upgrades.</p>
</div>
</div>
</section>
</div>

View file

@ -0,0 +1,350 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import DecisionTree from '$lib/components/diagrams/DecisionTree.svelte';
const nodes = [
// Start
{
id: 'start',
type: 'question' as const,
text: 'Do you need complex queries with JOINs across multiple tables?',
description: 'Think about your main query patterns. Will you frequently combine data from different sources?',
yes: 'acid-needed',
no: 'data-structure'
},
// SQL Path
{
id: 'acid-needed',
type: 'question' as const,
text: 'Do you need ACID transactions?',
description: 'Atomicity, Consistency, Isolation, Durability - critical for financial data, inventory, bookings',
yes: 'sql-answer',
no: 'read-heavy'
},
{
id: 'read-heavy',
type: 'question' as const,
text: 'Is your workload read-heavy with complex analytics?',
description: 'Reporting, dashboards, aggregations across large datasets',
yes: 'sql-analytics',
no: 'sql-general'
},
{
id: 'sql-answer',
type: 'answer' as const,
text: 'PostgreSQL',
icon: 'Database',
reasoning: 'You need JOINs AND ACID transactions. PostgreSQL is the gold standard - it handles complex queries, has excellent ACID support, JSON support for flexibility, and scales well with read replicas.',
alternatives: [
'MySQL - If you need simpler replication',
'CockroachDB - If you need global distribution',
'Cloud SQL / RDS - For managed hosting'
]
},
{
id: 'sql-analytics',
type: 'answer' as const,
text: 'PostgreSQL + Analytics DB',
icon: 'BarChart',
reasoning: 'For heavy analytics, consider PostgreSQL for OLTP (transactions) plus a specialized analytics database like ClickHouse or BigQuery for OLAP (analytics). Replicate data between them.',
alternatives: [
'TimescaleDB - If time-series analytics',
'Redshift/BigQuery - For massive data warehousing',
'DuckDB - For embedded analytics'
]
},
{
id: 'sql-general',
type: 'answer' as const,
text: 'PostgreSQL (or MySQL)',
icon: 'Table',
reasoning: 'You need JOINs but not strict ACID. PostgreSQL still works great here, or MySQL if your team knows it better. Both are excellent general-purpose relational databases.',
alternatives: [
'MySQL - Wide adoption, simpler',
'MariaDB - MySQL fork with more features',
'SQLite - For embedded/local use'
]
},
// NoSQL Path
{
id: 'data-structure',
type: 'question' as const,
text: 'Is your data hierarchical or document-like (JSON)?',
description: 'Think: user profiles, product catalogs, content with varying attributes',
yes: 'schema-flexibility',
no: 'access-pattern'
},
{
id: 'schema-flexibility',
type: 'question' as const,
text: 'Does your schema change frequently?',
description: 'Adding new fields often? Different documents have different structures?',
yes: 'document-answer',
no: 'document-consistency'
},
{
id: 'document-consistency',
type: 'question' as const,
text: 'Do you need strong consistency?',
description: 'Every read must return the latest write, no stale data acceptable',
yes: 'document-strong',
no: 'document-answer'
},
{
id: 'document-answer',
type: 'answer' as const,
text: 'MongoDB',
icon: 'FileJson',
reasoning: 'Flexible document structure, great for evolving schemas. Handles JSON-like data naturally. Good balance of flexibility and features.',
alternatives: [
'Firestore - If using Firebase/GCP',
'CouchDB - If you need offline sync',
'DocumentDB - AWS managed MongoDB-compatible'
]
},
{
id: 'document-strong',
type: 'answer' as const,
text: 'MongoDB or Firestore',
icon: 'FileJson',
reasoning: 'Both support configurable consistency levels. MongoDB with "majority" write concern and read concern gives you strong consistency. Firestore provides strong consistency by default.',
alternatives: [
'FaunaDB - Strong consistency by design',
'PostgreSQL with JSONB - Best of both worlds'
]
},
// Key-Value/Cache Path
{
id: 'access-pattern',
type: 'question' as const,
text: 'Is your primary access pattern key-based lookups?',
description: 'GET by ID, SET by ID - simple operations at high speed',
yes: 'persistence-needed',
no: 'time-series-check'
},
{
id: 'persistence-needed',
type: 'question' as const,
text: 'Do you need data persistence (survive restarts)?',
description: 'Or is this for caching/sessions that can be rebuilt?',
yes: 'distributed-keyvalue',
no: 'cache-answer'
},
{
id: 'cache-answer',
type: 'answer' as const,
text: 'Redis',
icon: 'Zap',
reasoning: 'Lightning-fast in-memory storage. Perfect for caching, sessions, leaderboards, rate limiting. Supports TTL for automatic expiration. Can persist to disk if needed.',
alternatives: [
'Memcached - Simpler, multi-threaded',
'KeyDB - Redis fork, multi-threaded',
'Dragonfly - Modern Redis alternative'
]
},
{
id: 'distributed-keyvalue',
type: 'question' as const,
text: 'Do you need global distribution and auto-scaling?',
description: 'Multiple regions, managed infrastructure, serverless scaling',
yes: 'dynamo-answer',
no: 'redis-persistent'
},
{
id: 'dynamo-answer',
type: 'answer' as const,
text: 'DynamoDB',
icon: 'Cloud',
reasoning: 'Fully managed, globally distributed, auto-scaling key-value store. Pay per request. Excellent for serverless architectures. Single-digit millisecond latency at any scale.',
alternatives: [
'Cosmos DB - If on Azure',
'Cloud Spanner - If need SQL with global distribution',
'ScyllaDB - Self-hosted Cassandra-compatible'
]
},
{
id: 'redis-persistent',
type: 'answer' as const,
text: 'Redis with Persistence',
icon: 'Database',
reasoning: 'Redis with AOF (Append-Only File) or RDB snapshots gives you persistence. Great for primary key-value storage with backup. Consider Redis Cluster for horizontal scaling.',
alternatives: [
'Valkey - Redis fork (open source)',
'KeyDB - Multi-threaded Redis',
'Managed: ElastiCache, MemoryDB'
]
},
// Time-Series/Wide-Column Path
{
id: 'time-series-check',
type: 'question' as const,
text: 'Is your data time-series or event-based?',
description: 'IoT sensors, metrics, logs, events with timestamps',
yes: 'time-series-scale',
no: 'graph-check'
},
{
id: 'time-series-scale',
type: 'question' as const,
text: 'Do you need massive write throughput (millions/sec)?',
description: 'IoT at scale, real-time analytics, high-frequency data',
yes: 'widecolumn-answer',
no: 'timeseries-answer'
},
{
id: 'timeseries-answer',
type: 'answer' as const,
text: 'TimescaleDB or InfluxDB',
icon: 'LineChart',
reasoning: 'Purpose-built for time-series data. TimescaleDB extends PostgreSQL (familiar SQL). InfluxDB is pure time-series with its own query language. Both handle time-based queries efficiently.',
alternatives: [
'QuestDB - Faster ingestion',
'Prometheus - For metrics specifically',
'ClickHouse - For analytics on time-series'
]
},
{
id: 'widecolumn-answer',
type: 'answer' as const,
text: 'Cassandra or ScyllaDB',
icon: 'LayoutGrid',
reasoning: 'Designed for massive write throughput across many nodes. Wide-column stores excel at time-series at scale. ScyllaDB is a faster C++ rewrite of Cassandra.',
alternatives: [
'Bigtable - GCP managed',
'HBase - Hadoop ecosystem',
'ClickHouse - For analytics focus'
]
},
// Graph Path
{
id: 'graph-check',
type: 'question' as const,
text: 'Do you need to query complex relationships?',
description: 'Social networks, recommendations, fraud detection, knowledge graphs',
yes: 'graph-answer',
no: 'search-check'
},
{
id: 'graph-answer',
type: 'answer' as const,
text: 'Neo4j',
icon: 'Share2',
reasoning: 'Native graph database - relationships are first-class citizens. Cypher query language makes traversing connections intuitive. Perfect for "friends of friends" or "shortest path" queries.',
alternatives: [
'Neptune - AWS managed',
'ArangoDB - Multi-model (graph + document)',
'Dgraph - Distributed graph'
]
},
// Search Path
{
id: 'search-check',
type: 'question' as const,
text: 'Do you need full-text search or fuzzy matching?',
description: 'Search boxes, autocomplete, finding documents by content',
yes: 'search-answer',
no: 'fallback-answer'
},
{
id: 'search-answer',
type: 'answer' as const,
text: 'Elasticsearch or Meilisearch',
icon: 'Search',
reasoning: 'Elasticsearch is the industry standard for full-text search, logging, and analytics. Meilisearch is simpler and faster for typical search use cases.',
alternatives: [
'OpenSearch - Open-source Elasticsearch',
'Algolia - Managed search service',
'Typesense - Simple, fast alternative'
]
},
// Fallback
{
id: 'fallback-answer',
type: 'answer' as const,
text: 'Start with PostgreSQL',
icon: 'Database',
reasoning: 'When in doubt, PostgreSQL is a safe choice. It handles JSON (document-like), has full-text search, and scales well. You can always add specialized databases later when you identify specific needs.',
alternatives: [
'SQLite - For embedded/local',
'Supabase - Managed PostgreSQL + extras',
'PlanetScale - Managed MySQL'
]
}
];
</script>
<svelte:head>
<title>Which Database? - System Design Explorer</title>
</svelte:head>
<div class="max-w-4xl mx-auto space-y-8">
<!-- Header -->
<div>
<div class="flex items-center gap-2 text-surface-500 text-sm mb-2">
<a href="/decisions" class="hover:text-surface-300">Decision Trees</a>
<Icons.ChevronRight class="w-4 h-4" />
<span class="text-surface-300">Which Database?</span>
</div>
<h1 class="text-3xl font-bold text-surface-100">Which Database Should I Use?</h1>
<p class="text-surface-400 mt-2">
Answer a few questions about your use case, and we'll recommend the best database for your needs.
</p>
</div>
<!-- Decision Tree -->
<DecisionTree
title=""
subtitle=""
{nodes}
startNode="start"
/>
<!-- Quick Reference -->
<div class="card">
<h3 class="text-lg font-semibold text-surface-100 mb-4">Quick Reference</h3>
<div class="grid md:grid-cols-2 gap-4">
<div class="space-y-3">
<h4 class="text-sm font-medium text-surface-400 uppercase tracking-wide">By Use Case</h4>
{#each [
{ case: 'E-commerce orders', db: 'PostgreSQL', color: 'blue' },
{ case: 'User profiles', db: 'MongoDB', color: 'green' },
{ case: 'Session caching', db: 'Redis', color: 'red' },
{ case: 'IoT sensor data', db: 'TimescaleDB', color: 'purple' },
{ case: 'Social graph', db: 'Neo4j', color: 'pink' }
] as item}
<div class="flex items-center justify-between p-2 bg-surface-800 rounded-lg">
<span class="text-sm text-surface-300">{item.case}</span>
<span class="text-sm font-medium text-{item.color}-400">{item.db}</span>
</div>
{/each}
</div>
<div class="space-y-3">
<h4 class="text-sm font-medium text-surface-400 uppercase tracking-wide">By Data Type</h4>
{#each [
{ type: 'Relational', db: 'PostgreSQL/MySQL', color: 'blue' },
{ type: 'Documents', db: 'MongoDB/Firestore', color: 'green' },
{ type: 'Key-Value', db: 'Redis/DynamoDB', color: 'yellow' },
{ type: 'Time-Series', db: 'TimescaleDB/InfluxDB', color: 'purple' },
{ type: 'Graph', db: 'Neo4j/Neptune', color: 'pink' }
] as item}
<div class="flex items-center justify-between p-2 bg-surface-800 rounded-lg">
<span class="text-sm text-surface-300">{item.type}</span>
<span class="text-sm font-medium text-{item.color}-400">{item.db}</span>
</div>
{/each}
</div>
</div>
</div>
<!-- Navigation -->
<div class="flex justify-between items-center pt-4 border-t border-surface-800">
<a href="/decisions" class="btn-secondary flex items-center gap-2">
<Icons.ArrowLeft class="w-4 h-4" />
All Decision Trees
</a>
<a href="/compare/sql-vs-nosql" class="btn-primary flex items-center gap-2">
Compare SQL vs NoSQL
<Icons.GitCompare class="w-4 h-4" />
</a>
</div>
</div>

View file

@ -0,0 +1,376 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import { fade, fly } from 'svelte/transition';
type CapChoice = 'C' | 'A' | 'P' | 'CA' | 'CP' | 'AP' | null;
let selectedChoice = $state<CapChoice>(null);
let hoveredVertex = $state<'C' | 'A' | 'P' | null>(null);
const vertices = {
C: {
x: 200,
y: 50,
label: 'Consistency',
short: 'C',
description: 'Every read receives the most recent write or an error',
detail: 'All nodes see the same data at the same time. When you write data, all subsequent reads return that data.',
color: '#3B82F6' // blue
},
A: {
x: 50,
y: 300,
label: 'Availability',
short: 'A',
description: 'Every request receives a response (success or failure)',
detail: 'The system remains operational and responsive, even if some nodes fail. Every request gets a response.',
color: '#22C55E' // green
},
P: {
x: 350,
y: 300,
label: 'Partition Tolerance',
short: 'P',
description: 'System continues to operate despite network partitions',
detail: 'The system continues to function even when network communication between nodes fails.',
color: '#EAB308' // yellow
}
};
const combinations = {
CA: {
name: 'CA (Consistency + Availability)',
description: 'NOT possible in distributed systems',
explanation: 'In a distributed system, network partitions are inevitable. When a partition occurs, you must choose between C and A.',
examples: ['Single-node databases (PostgreSQL, MySQL on one server)', 'Not achievable in distributed systems'],
color: '#94A3B8',
viable: false
},
CP: {
name: 'CP (Consistency + Partition Tolerance)',
description: 'Sacrifice availability for consistency',
explanation: 'During a partition, the system refuses to respond rather than return potentially stale data. Prioritizes data accuracy.',
examples: ['MongoDB (default)', 'HBase', 'Redis Cluster', 'Zookeeper', 'etcd'],
useCase: 'Financial systems, inventory management, booking systems - where incorrect data causes real problems',
color: '#8B5CF6',
viable: true
},
AP: {
name: 'AP (Availability + Partition Tolerance)',
description: 'Sacrifice consistency for availability',
explanation: 'During a partition, the system continues to respond but may return stale data. Prioritizes uptime.',
examples: ['Cassandra', 'DynamoDB', 'CouchDB', 'Riak'],
useCase: 'Social media feeds, shopping carts, DNS - where temporary inconsistency is acceptable',
color: '#EC4899',
viable: true
}
};
function selectCombination(choice: CapChoice) {
selectedChoice = selectedChoice === choice ? null : choice;
}
const activeVertex = $derived(hoveredVertex);
</script>
<svelte:head>
<title>CAP Theorem - System Design Explorer</title>
</svelte:head>
<div class="max-w-5xl mx-auto space-y-8">
<!-- Header -->
<div>
<div class="flex items-center gap-2 text-surface-500 text-sm mb-2">
<a href="/fundamentals" class="hover:text-surface-300">Fundamentals</a>
<Icons.ChevronRight class="w-4 h-4" />
<span class="text-surface-300">CAP Theorem</span>
</div>
<h1 class="text-3xl font-bold text-surface-100">CAP Theorem</h1>
<p class="text-surface-400 mt-2">
The fundamental trade-off in distributed systems: you can only guarantee two of three properties.
</p>
</div>
<!-- Interactive Triangle -->
<div class="card">
<div class="flex flex-col lg:flex-row gap-8">
<!-- SVG Triangle -->
<div class="flex-1">
<svg viewBox="0 0 400 380" class="w-full max-w-md mx-auto">
<!-- Triangle edges -->
<line x1={vertices.C.x} y1={vertices.C.y} x2={vertices.A.x} y2={vertices.A.y}
stroke="#334155" stroke-width="2" />
<line x1={vertices.A.x} y1={vertices.A.y} x2={vertices.P.x} y2={vertices.P.y}
stroke="#334155" stroke-width="2" />
<line x1={vertices.P.x} y1={vertices.P.y} x2={vertices.C.x} y2={vertices.C.y}
stroke="#334155" stroke-width="2" />
<!-- Edge labels (combinations) -->
<!-- CA Edge -->
<g
class="cursor-pointer"
onclick={() => selectCombination('CA')}
onmouseenter={() => {}}
>
<line x1={vertices.C.x} y1={vertices.C.y} x2={vertices.A.x} y2={vertices.A.y}
stroke={selectedChoice === 'CA' ? '#94A3B8' : 'transparent'}
stroke-width="8"
class="transition-all duration-200" />
<text x={(vertices.C.x + vertices.A.x) / 2 - 30} y={(vertices.C.y + vertices.A.y) / 2}
fill={selectedChoice === 'CA' ? '#94A3B8' : '#64748B'}
class="text-sm font-medium transition-colors duration-200">
CA
</text>
<text x={(vertices.C.x + vertices.A.x) / 2 - 30} y={(vertices.C.y + vertices.A.y) / 2 + 16}
fill="#EF4444"
class="text-[10px]">
(Not viable)
</text>
</g>
<!-- CP Edge -->
<g
class="cursor-pointer"
onclick={() => selectCombination('CP')}
>
<line x1={vertices.C.x} y1={vertices.C.y} x2={vertices.P.x} y2={vertices.P.y}
stroke={selectedChoice === 'CP' ? '#8B5CF6' : 'transparent'}
stroke-width="8"
class="transition-all duration-200" />
<text x={(vertices.C.x + vertices.P.x) / 2 + 15} y={(vertices.C.y + vertices.P.y) / 2}
fill={selectedChoice === 'CP' ? '#8B5CF6' : '#64748B'}
class="text-sm font-medium transition-colors duration-200">
CP
</text>
</g>
<!-- AP Edge -->
<g
class="cursor-pointer"
onclick={() => selectCombination('AP')}
>
<line x1={vertices.A.x} y1={vertices.A.y} x2={vertices.P.x} y2={vertices.P.y}
stroke={selectedChoice === 'AP' ? '#EC4899' : 'transparent'}
stroke-width="8"
class="transition-all duration-200" />
<text x={(vertices.A.x + vertices.P.x) / 2} y={(vertices.A.y + vertices.P.y) / 2 + 30}
fill={selectedChoice === 'AP' ? '#EC4899' : '#64748B'}
class="text-sm font-medium transition-colors duration-200">
AP
</text>
</g>
<!-- Vertices -->
{#each Object.entries(vertices) as [key, vertex]}
<g
class="cursor-pointer"
onmouseenter={() => hoveredVertex = key as 'C' | 'A' | 'P'}
onmouseleave={() => hoveredVertex = null}
>
<circle
cx={vertex.x}
cy={vertex.y}
r={hoveredVertex === key ? 35 : 30}
fill={vertex.color}
class="transition-all duration-200"
opacity={hoveredVertex === key ? 1 : 0.8}
/>
<text
x={vertex.x}
y={vertex.y + 6}
text-anchor="middle"
fill="white"
class="text-lg font-bold pointer-events-none"
>
{vertex.short}
</text>
</g>
{/each}
<!-- Center text -->
<text x="200" y="200" text-anchor="middle" fill="#64748B" class="text-xs">
Click an edge to learn more
</text>
</svg>
</div>
<!-- Vertex Info Panel -->
<div class="lg:w-80 space-y-4">
<h3 class="font-semibold text-surface-200">The Three Properties</h3>
{#each Object.entries(vertices) as [key, vertex]}
<div
class="p-4 rounded-lg border transition-all duration-200"
class:bg-surface-800={hoveredVertex !== key}
class:border-surface-700={hoveredVertex !== key}
style:background-color={hoveredVertex === key ? `${vertex.color}20` : undefined}
style:border-color={hoveredVertex === key ? vertex.color : undefined}
onmouseenter={() => hoveredVertex = key as 'C' | 'A' | 'P'}
onmouseleave={() => hoveredVertex = null}
>
<div class="flex items-center gap-2 mb-2">
<div
class="w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold text-white"
style:background-color={vertex.color}
>
{vertex.short}
</div>
<span class="font-medium text-surface-100">{vertex.label}</span>
</div>
<p class="text-sm text-surface-400">{vertex.description}</p>
</div>
{/each}
</div>
</div>
</div>
<!-- Selected Combination Details -->
{#if selectedChoice && selectedChoice !== 'C' && selectedChoice !== 'A' && selectedChoice !== 'P'}
{@const combo = combinations[selectedChoice]}
<div
class="card border-2 transition-all duration-300"
style:border-color={combo.color}
in:fly={{ y: 20, duration: 300 }}
>
<div class="flex items-start gap-4">
<div
class="w-12 h-12 rounded-xl flex items-center justify-center text-white font-bold flex-shrink-0"
style:background-color={combo.color}
>
{selectedChoice}
</div>
<div class="flex-1">
<div class="flex items-center gap-3 mb-2">
<h3 class="text-xl font-semibold text-surface-100">{combo.name}</h3>
{#if !combo.viable}
<span class="tag bg-red-500/20 text-red-400">Not Achievable</span>
{/if}
</div>
<p class="text-surface-300 mb-4">{combo.explanation}</p>
{#if combo.viable}
<div class="grid md:grid-cols-2 gap-4">
<!-- Examples -->
<div class="bg-surface-800 rounded-lg p-4">
<div class="flex items-center gap-2 mb-3">
<Icons.Database class="w-4 h-4 text-blue-400" />
<span class="text-sm font-medium text-blue-400">Examples</span>
</div>
<ul class="space-y-2">
{#each combo.examples as example}
<li class="text-sm text-surface-300 flex items-start gap-2">
<Icons.ChevronRight class="w-4 h-4 mt-0.5 text-surface-500 flex-shrink-0" />
{example}
</li>
{/each}
</ul>
</div>
<!-- Use Case -->
{#if combo.useCase}
<div class="bg-surface-800 rounded-lg p-4">
<div class="flex items-center gap-2 mb-3">
<Icons.Lightbulb class="w-4 h-4 text-yellow-400" />
<span class="text-sm font-medium text-yellow-400">Best For</span>
</div>
<p class="text-sm text-surface-300">{combo.useCase}</p>
</div>
{/if}
</div>
{:else}
<div class="bg-red-500/10 border border-red-500/30 rounded-lg p-4">
<div class="flex items-center gap-2 mb-2">
<Icons.AlertTriangle class="w-4 h-4 text-red-400" />
<span class="text-sm font-medium text-red-400">Why Not Achievable?</span>
</div>
<p class="text-sm text-surface-300">
In any distributed system, network partitions can and will occur. When they do,
you MUST choose between consistency and availability - you cannot have both.
CA systems only exist as single-node databases.
</p>
</div>
{/if}
</div>
</div>
</div>
{/if}
<!-- Key Insight -->
<div class="card bg-gradient-to-br from-blue-500/10 to-purple-500/10 border-blue-500/30">
<div class="flex items-start gap-4">
<div class="w-10 h-10 rounded-lg bg-blue-500/20 flex items-center justify-center flex-shrink-0">
<Icons.Sparkles class="w-5 h-5 text-blue-400" />
</div>
<div>
<h3 class="font-semibold text-surface-100 mb-2">The Key Insight</h3>
<p class="text-surface-300 text-sm">
In practice, <strong class="text-yellow-400">Partition Tolerance (P) is mandatory</strong> for any
distributed system - network failures are a reality. This means your real choice is between
<strong class="text-purple-400">CP</strong> (consistency over availability) and
<strong class="text-pink-400">AP</strong> (availability over consistency).
</p>
</div>
</div>
</div>
<!-- Real-World Decision Guide -->
<div class="card">
<h3 class="text-lg font-semibold text-surface-100 mb-4">Choosing Between CP and AP</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="space-y-3">
<div class="flex items-center gap-2">
<div class="w-8 h-8 rounded-lg bg-purple-500/20 flex items-center justify-center">
<span class="text-sm font-bold text-purple-400">CP</span>
</div>
<span class="font-medium text-surface-200">Choose CP when...</span>
</div>
<ul class="space-y-2 ml-10">
{#each [
'Wrong data causes real harm (financial transactions)',
'Users expect immediate consistency (booking systems)',
'You need strong guarantees (inventory counts)',
'Data conflicts are hard to resolve'
] as item}
<li class="text-sm text-surface-400 flex items-start gap-2">
<Icons.Check class="w-4 h-4 text-purple-400 mt-0.5 flex-shrink-0" />
{item}
</li>
{/each}
</ul>
</div>
<div class="space-y-3">
<div class="flex items-center gap-2">
<div class="w-8 h-8 rounded-lg bg-pink-500/20 flex items-center justify-center">
<span class="text-sm font-bold text-pink-400">AP</span>
</div>
<span class="font-medium text-surface-200">Choose AP when...</span>
</div>
<ul class="space-y-2 ml-10">
{#each [
'Availability is critical (always-on services)',
'Eventual consistency is acceptable (social feeds)',
'You can merge conflicts automatically',
'Stale data is better than no data'
] as item}
<li class="text-sm text-surface-400 flex items-start gap-2">
<Icons.Check class="w-4 h-4 text-pink-400 mt-0.5 flex-shrink-0" />
{item}
</li>
{/each}
</ul>
</div>
</div>
</div>
<!-- Navigation -->
<div class="flex justify-between items-center pt-4 border-t border-surface-800">
<a href="/fundamentals" class="btn-secondary flex items-center gap-2">
<Icons.ArrowLeft class="w-4 h-4" />
Back to Fundamentals
</a>
<a href="/fundamentals/consistency" class="btn-primary flex items-center gap-2">
Next: Consistency Models
<Icons.ArrowRight class="w-4 h-4" />
</a>
</div>
</div>

View file

@ -0,0 +1,306 @@
<script lang="ts">
import * as Icons from 'lucide-svelte';
import GuidedWalkthrough from '$lib/components/diagrams/GuidedWalkthrough.svelte';
const dnsSteps = [
{
id: 'step1',
title: 'User Types URL',
description: 'You type "example.com" into your browser. The browser needs to find the IP address of this domain to connect to it.',
icon: 'Globe',
details: 'The browser first checks its own cache. If you visited this site recently, it may already know the IP address.',
tip: 'Browser caches typically last a few minutes to hours, depending on the TTL (Time To Live) set by the domain.'
},
{
id: 'step2',
title: 'Check OS Cache',
description: 'If not in browser cache, the OS (Operating System) cache is checked. Your computer maintains its own DNS cache.',
icon: 'Monitor',
details: 'On macOS: "dscacheutil -q host -a name example.com". On Windows: "ipconfig /displaydns". On Linux: check systemd-resolved.',
tip: 'You can flush your OS DNS cache if you need to force a fresh lookup.'
},
{
id: 'step3',
title: 'Query Recursive Resolver',
description: 'If still not found, your computer asks the Recursive DNS Resolver (usually your ISP or a service like 8.8.8.8).',
icon: 'Search',
details: 'The recursive resolver is like a librarian - it will do all the work to find the answer for you. Popular resolvers: Google (8.8.8.8), Cloudflare (1.1.1.1), OpenDNS.',
tip: 'Using a different DNS resolver can improve speed and privacy. Cloudflare 1.1.1.1 is known for speed, while 9.9.9.9 (Quad9) blocks malicious domains.'
},
{
id: 'step4',
title: 'Query Root Nameserver',
description: 'The resolver asks a Root Nameserver: "Who knows about .com domains?" There are 13 root server clusters worldwide.',
icon: 'Server',
details: 'Root servers don\'t know specific domains - they only know which servers handle each TLD (.com, .org, .net, etc.). They respond with the address of the TLD nameserver.',
tip: 'The 13 root server clusters (A through M) are actually hundreds of servers using Anycast to appear as one.'
},
{
id: 'step5',
title: 'Query TLD Nameserver',
description: 'The resolver asks the .com TLD Nameserver: "Who knows about example.com?" It responds with the authoritative nameserver.',
icon: 'Building',
details: 'TLD (Top Level Domain) servers are managed by registries. Verisign manages .com and .net. Different organizations manage country codes like .uk, .de, etc.',
tip: 'When you buy a domain, your registrar tells the TLD servers which nameservers are authoritative for your domain.'
},
{
id: 'step6',
title: 'Query Authoritative Nameserver',
description: 'The resolver asks the Authoritative Nameserver: "What is the IP address of example.com?" This server has the actual answer.',
icon: 'Database',
details: 'The authoritative nameserver is typically run by your DNS provider (Cloudflare, Route53, etc.) or hosting provider. It contains the actual DNS records you configured.',
tip: 'This is where your A records, CNAME records, MX records, and TXT records live. Changes here propagate based on TTL.'
},
{
id: 'step7',
title: 'Return IP Address',
description: 'The authoritative nameserver responds with the IP address (e.g., 93.184.216.34). The resolver caches this and returns it to your browser.',
icon: 'ArrowDown',
details: 'The response includes a TTL (Time To Live) - how long this answer can be cached. Typical TTLs range from 5 minutes to 24 hours.',
tip: 'Lower TTL = faster propagation of changes but more DNS queries. Higher TTL = better caching but slower updates.'
},
{
id: 'step8',
title: 'Browser Connects',
description: 'Your browser now has the IP address and can establish a TCP connection to the web server and load the page.',
icon: 'Wifi',
details: 'The entire DNS lookup typically takes 20-120ms. After that, the browser opens a TCP connection (another round trip) and sends the HTTP request.',
tip: 'This whole process happens every time you visit a new domain. Caching at multiple levels makes repeat visits much faster.'
}
];
const recordTypes = [
{
type: 'A',
description: 'Maps domain to IPv4 address',
example: 'example.com -> 93.184.216.34',
useCase: 'Primary record for websites'
},
{
type: 'AAAA',
description: 'Maps domain to IPv6 address',
example: 'example.com -> 2606:2800:220:1:248:1893:25c8:1946',
useCase: 'IPv6 support'
},
{
type: 'CNAME',
description: 'Alias pointing to another domain',
example: 'www.example.com -> example.com',
useCase: 'Subdomains, CDN integration'
},
{
type: 'MX',
description: 'Mail server for the domain',
example: 'example.com -> mail.example.com (priority 10)',
useCase: 'Email routing'
},
{
type: 'TXT',
description: 'Arbitrary text data',
example: 'v=spf1 include:_spf.google.com ~all',
useCase: 'SPF, DKIM, domain verification'
},
{
type: 'NS',
description: 'Nameservers for the domain',
example: 'example.com -> ns1.cloudflare.com',
useCase: 'Delegate DNS authority'
}
];
</script>
<svelte:head>
<title>DNS - How it Works - System Design Explorer</title>
</svelte:head>
<div class="max-w-5xl mx-auto space-y-8">
<!-- Header -->
<div>
<div class="flex items-center gap-2 text-surface-500 text-sm mb-2">
<a href="/networking" class="hover:text-surface-300">Networking</a>
<Icons.ChevronRight class="w-4 h-4" />
<span class="text-surface-300">DNS</span>
</div>
<h1 class="text-3xl font-bold text-surface-100">DNS: Domain Name System</h1>
<p class="text-surface-400 mt-2">
The internet's phonebook - translating human-readable domain names to IP addresses.
</p>
</div>
<!-- Quick Summary -->
<div class="grid md:grid-cols-3 gap-4">
<div class="card text-center">
<div class="w-12 h-12 mx-auto rounded-xl bg-blue-500/20 flex items-center justify-center mb-3">
<Icons.Globe class="w-6 h-6 text-blue-400" />
</div>
<h3 class="font-medium text-surface-200">What</h3>
<p class="text-sm text-surface-400 mt-1">Translates domain names to IP addresses</p>
</div>
<div class="card text-center">
<div class="w-12 h-12 mx-auto rounded-xl bg-green-500/20 flex items-center justify-center mb-3">
<Icons.Zap class="w-6 h-6 text-green-400" />
</div>
<h3 class="font-medium text-surface-200">Speed</h3>
<p class="text-sm text-surface-400 mt-1">20-120ms for uncached lookups</p>
</div>
<div class="card text-center">
<div class="w-12 h-12 mx-auto rounded-xl bg-purple-500/20 flex items-center justify-center mb-3">
<Icons.Layers class="w-6 h-6 text-purple-400" />
</div>
<h3 class="font-medium text-surface-200">Layers</h3>
<p class="text-sm text-surface-400 mt-1">Multiple caching layers for efficiency</p>
</div>
</div>
<!-- Walkthrough -->
<GuidedWalkthrough
title="How DNS Resolution Works"
subtitle="Follow a DNS query from your browser to the answer"
steps={dnsSteps}
autoPlay={false}
/>
<!-- DNS Record Types -->
<div class="card">
<h3 class="text-lg font-semibold text-surface-100 mb-4">DNS Record Types</h3>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="border-b border-surface-700">
<th class="text-left p-3 text-surface-400 font-medium">Type</th>
<th class="text-left p-3 text-surface-400 font-medium">Description</th>
<th class="text-left p-3 text-surface-400 font-medium">Example</th>
<th class="text-left p-3 text-surface-400 font-medium">Use Case</th>
</tr>
</thead>
<tbody>
{#each recordTypes as record}
<tr class="border-b border-surface-800 hover:bg-surface-800/50">
<td class="p-3">
<span class="px-2 py-1 rounded bg-blue-500/20 text-blue-400 font-mono text-sm">
{record.type}
</span>
</td>
<td class="p-3 text-surface-300 text-sm">{record.description}</td>
<td class="p-3 text-surface-400 text-sm font-mono">{record.example}</td>
<td class="p-3 text-surface-400 text-sm">{record.useCase}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</div>
<!-- A vs CNAME -->
<div class="card">
<h3 class="text-lg font-semibold text-surface-100 mb-4">A Record vs CNAME: When to Use Each</h3>
<div class="grid md:grid-cols-2 gap-6">
<div class="space-y-4">
<div class="flex items-center gap-3">
<span class="px-3 py-1 rounded-lg bg-blue-500/20 text-blue-400 font-bold">A</span>
<span class="font-medium text-surface-200">Direct IP Mapping</span>
</div>
<ul class="space-y-2 text-sm">
<li class="flex items-start gap-2 text-surface-300">
<Icons.Check class="w-4 h-4 text-green-400 mt-0.5 flex-shrink-0" />
Use for root domain (example.com)
</li>
<li class="flex items-start gap-2 text-surface-300">
<Icons.Check class="w-4 h-4 text-green-400 mt-0.5 flex-shrink-0" />
Slightly faster (no extra lookup)
</li>
<li class="flex items-start gap-2 text-surface-300">
<Icons.X class="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
Must update if IP changes
</li>
<li class="flex items-start gap-2 text-surface-300">
<Icons.X class="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
Can't point to another domain
</li>
</ul>
</div>
<div class="space-y-4">
<div class="flex items-center gap-3">
<span class="px-3 py-1 rounded-lg bg-green-500/20 text-green-400 font-bold">CNAME</span>
<span class="font-medium text-surface-200">Alias to Another Domain</span>
</div>
<ul class="space-y-2 text-sm">
<li class="flex items-start gap-2 text-surface-300">
<Icons.Check class="w-4 h-4 text-green-400 mt-0.5 flex-shrink-0" />
Use for subdomains (www.example.com)
</li>
<li class="flex items-start gap-2 text-surface-300">
<Icons.Check class="w-4 h-4 text-green-400 mt-0.5 flex-shrink-0" />
Great for CDNs and load balancers
</li>
<li class="flex items-start gap-2 text-surface-300">
<Icons.Check class="w-4 h-4 text-green-400 mt-0.5 flex-shrink-0" />
Target can change IP without updating
</li>
<li class="flex items-start gap-2 text-surface-300">
<Icons.X class="w-4 h-4 text-red-400 mt-0.5 flex-shrink-0" />
Cannot use at root domain (usually)
</li>
</ul>
</div>
</div>
</div>
<!-- Popular DNS Providers -->
<div class="card">
<h3 class="text-lg font-semibold text-surface-100 mb-4">DNS Providers Comparison</h3>
<div class="grid md:grid-cols-3 gap-4">
{#each [
{
name: 'Cloudflare',
icon: 'Cloud',
color: 'orange',
pros: ['Free tier', 'Fast propagation', 'DDoS protection'],
bestFor: 'Most websites'
},
{
name: 'Route 53',
icon: 'Compass',
color: 'yellow',
pros: ['AWS integration', 'Health checks', 'Geo-routing'],
bestFor: 'AWS infrastructure'
},
{
name: 'Cloud DNS',
icon: 'Cloud',
color: 'blue',
pros: ['GCP integration', '100% SLA', 'Global anycast'],
bestFor: 'GCP infrastructure'
}
] as provider}
<div class="bg-surface-800 rounded-lg p-4">
<div class="flex items-center gap-2 mb-3">
<Icons.Cloud class="w-5 h-5 text-{provider.color}-400" />
<span class="font-medium text-surface-200">{provider.name}</span>
</div>
<ul class="space-y-1 mb-3">
{#each provider.pros as pro}
<li class="text-sm text-surface-400 flex items-center gap-2">
<Icons.Check class="w-3 h-3 text-green-400" />
{pro}
</li>
{/each}
</ul>
<div class="text-xs text-surface-500">Best for: {provider.bestFor}</div>
</div>
{/each}
</div>
</div>
<!-- Navigation -->
<div class="flex justify-between items-center pt-4 border-t border-surface-800">
<a href="/networking" class="btn-secondary flex items-center gap-2">
<Icons.ArrowLeft class="w-4 h-4" />
All Networking
</a>
<a href="/networking/load-balancing" class="btn-primary flex items-center gap-2">
Next: Load Balancing
<Icons.ArrowRight class="w-4 h-4" />
</a>
</div>
</div>

View file

@ -0,0 +1,368 @@
<script lang="ts">
import ComparisonTable from '$lib/components/diagrams/ComparisonTable.svelte';
let selectedLayer: 'l4' | 'l7' | null = null;
let showOsiModel = true;
const osiLayers = [
{ num: 7, name: 'Application', protocols: 'HTTP, HTTPS, gRPC', lb: 'ALB' },
{ num: 6, name: 'Presentation', protocols: 'SSL/TLS', lb: '-' },
{ num: 5, name: 'Session', protocols: 'Sockets', lb: '-' },
{ num: 4, name: 'Transport', protocols: 'TCP, UDP', lb: 'NLB' },
{ num: 3, name: 'Network', protocols: 'IP', lb: 'GLB' },
{ num: 2, name: 'Data Link', protocols: 'Ethernet', lb: '-' },
{ num: 1, name: 'Physical', protocols: 'Cables', lb: '-' }
];
const comparisonOptions = [
{
id: 'l4',
name: 'Layer 4 (NLB)',
description: 'Transport layer load balancing based on IP and port',
whenToUse: [
'High-performance, low-latency requirements (gaming, trading)',
'Non-HTTP protocols (databases, custom TCP/UDP)',
'Need for static IP addresses',
'Simple round-robin or hash-based distribution',
'TLS passthrough without termination',
'Millions of connections per second'
],
whenNotToUse: [
'Need content-based routing (URL paths, headers)',
'Require sticky sessions based on cookies',
'Want to inspect or modify HTTP traffic',
'Need Web Application Firewall (WAF) integration',
'Microservices with path-based routing'
]
},
{
id: 'l7',
name: 'Layer 7 (ALB)',
description: 'Application layer load balancing with content inspection',
whenToUse: [
'HTTP/HTTPS web applications',
'Microservices with path-based routing (/api, /web)',
'Content-based routing (headers, cookies, query strings)',
'SSL termination at the load balancer',
'WebSocket support',
'Integration with WAF for security'
],
whenNotToUse: [
'Non-HTTP protocols (databases, gaming servers)',
'Ultra-low latency requirements',
'Need static IP addresses',
'Simple TCP/UDP load balancing',
'Want to avoid SSL termination overhead'
]
}
];
const features = [
{ name: 'OSI Layer', l4: 'Layer 4 (Transport)', l7: 'Layer 7 (Application)' },
{ name: 'Protocols', l4: 'TCP, UDP, TLS', l7: 'HTTP, HTTPS, gRPC, WebSocket' },
{ name: 'Routing Decision', l4: 'IP + Port (5-tuple)', l7: 'URL path, headers, cookies' },
{ name: 'SSL/TLS', l4: 'Passthrough or terminate', l7: 'Always terminates' },
{ name: 'Performance', l4: 'Millions req/sec, microseconds', l7: 'Lower throughput, milliseconds' },
{ name: 'Connection', l4: 'Preserves client connection', l7: 'Terminates and creates new' },
{ name: 'Algorithm', l4: 'Flow hash', l7: 'Round robin, least connections' },
{ name: 'Static IP', l4: 'Yes (per AZ)', l7: 'No (DNS-based)' },
{ name: 'Health Checks', l4: 'TCP/UDP port check', l7: 'HTTP status codes' },
{ name: 'WAF Support', l4: 'No', l7: 'Yes' }
];
const awsServices = [
{ name: 'ALB', layer: 'L7', use: 'Web apps, microservices, APIs' },
{ name: 'NLB', layer: 'L4', use: 'High performance, TCP/UDP, static IPs' },
{ name: 'GLB', layer: 'L3/L4', use: 'Network appliances, firewalls, inspection' },
{ name: 'CLB', layer: 'L4/L7', use: 'Legacy (deprecated)' }
];
</script>
<svelte:head>
<title>Load Balancing - L4 vs L7</title>
</svelte:head>
<div class="max-w-6xl mx-auto">
<h1 class="text-3xl font-bold text-white mb-2">Load Balancing</h1>
<p class="text-gray-400 mb-8">Understanding Layer 4 vs Layer 7 load balancers and when to use each</p>
<!-- OSI Model Visual -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">OSI Model & Load Balancer Layers</h2>
<p class="text-gray-400 mb-6">Load balancers operate at different OSI layers, determining what information they can see and use for routing decisions.</p>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="grid gap-2">
{#each osiLayers as layer}
<div
class="flex items-center gap-4 p-3 rounded-lg transition-all cursor-pointer
{layer.num === 7 ? 'bg-blue-500/20 border border-blue-500/50' : ''}
{layer.num === 4 ? 'bg-purple-500/20 border border-purple-500/50' : ''}
{layer.num === 3 ? 'bg-orange-500/20 border border-orange-500/50' : ''}
{layer.num !== 7 && layer.num !== 4 && layer.num !== 3 ? 'bg-gray-700/30' : ''}"
on:click={() => {
if (layer.num === 7) selectedLayer = 'l7';
else if (layer.num === 4) selectedLayer = 'l4';
}}
on:keydown={(e) => e.key === 'Enter' && (layer.num === 7 ? selectedLayer = 'l7' : layer.num === 4 ? selectedLayer = 'l4' : null)}
role="button"
tabindex="0"
>
<div class="w-8 h-8 rounded-full flex items-center justify-center font-bold
{layer.num === 7 ? 'bg-blue-500 text-white' : ''}
{layer.num === 4 ? 'bg-purple-500 text-white' : ''}
{layer.num === 3 ? 'bg-orange-500 text-white' : ''}
{layer.num !== 7 && layer.num !== 4 && layer.num !== 3 ? 'bg-gray-600 text-gray-300' : ''}">
{layer.num}
</div>
<div class="flex-1">
<span class="text-white font-medium">{layer.name}</span>
<span class="text-gray-500 ml-2">- {layer.protocols}</span>
</div>
{#if layer.lb !== '-'}
<span class="px-3 py-1 rounded-full text-sm font-medium
{layer.num === 7 ? 'bg-blue-500/30 text-blue-300' : ''}
{layer.num === 4 ? 'bg-purple-500/30 text-purple-300' : ''}
{layer.num === 3 ? 'bg-orange-500/30 text-orange-300' : ''}">
{layer.lb}
</span>
{/if}
</div>
{/each}
</div>
<div class="mt-6 grid grid-cols-3 gap-4 text-center">
<div class="p-3 rounded-lg bg-blue-500/10 border border-blue-500/30">
<div class="text-blue-400 font-semibold">Layer 7 (ALB)</div>
<div class="text-gray-400 text-sm mt-1">Sees HTTP content</div>
</div>
<div class="p-3 rounded-lg bg-purple-500/10 border border-purple-500/30">
<div class="text-purple-400 font-semibold">Layer 4 (NLB)</div>
<div class="text-gray-400 text-sm mt-1">Sees IP + Port</div>
</div>
<div class="p-3 rounded-lg bg-orange-500/10 border border-orange-500/30">
<div class="text-orange-400 font-semibold">Layer 3 (GLB)</div>
<div class="text-gray-400 text-sm mt-1">Network gateway</div>
</div>
</div>
</div>
</section>
<!-- How They Work -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">How They Work</h2>
<div class="grid md:grid-cols-2 gap-6">
<!-- L4 -->
<div class="bg-gray-800/50 rounded-xl p-6 border border-purple-500/30">
<h3 class="text-lg font-semibold text-purple-400 mb-4">Layer 4 Load Balancer</h3>
<div class="space-y-4">
<div class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400 text-sm font-bold">1</div>
<div>
<div class="text-white font-medium">Extract 5-tuple</div>
<div class="text-gray-400 text-sm">Source IP, Source Port, Dest IP, Dest Port, Protocol</div>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400 text-sm font-bold">2</div>
<div>
<div class="text-white font-medium">Apply hash algorithm</div>
<div class="text-gray-400 text-sm">Deterministic routing based on connection info</div>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-purple-500/20 flex items-center justify-center text-purple-400 text-sm font-bold">3</div>
<div>
<div class="text-white font-medium">Forward entire connection</div>
<div class="text-gray-400 text-sm">All packets in session go to same backend</div>
</div>
</div>
</div>
<div class="mt-4 p-3 bg-purple-500/10 rounded-lg">
<div class="text-purple-300 text-sm font-medium">Key insight:</div>
<div class="text-gray-400 text-sm">Cannot see inside the packet payload - only network headers</div>
</div>
</div>
<!-- L7 -->
<div class="bg-gray-800/50 rounded-xl p-6 border border-blue-500/30">
<h3 class="text-lg font-semibold text-blue-400 mb-4">Layer 7 Load Balancer</h3>
<div class="space-y-4">
<div class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-sm font-bold">1</div>
<div>
<div class="text-white font-medium">Terminate connection</div>
<div class="text-gray-400 text-sm">Client connects to LB, LB connects to backend</div>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-sm font-bold">2</div>
<div>
<div class="text-white font-medium">Parse HTTP request</div>
<div class="text-gray-400 text-sm">Read URL path, headers, cookies, body</div>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-6 h-6 rounded-full bg-blue-500/20 flex items-center justify-center text-blue-400 text-sm font-bold">3</div>
<div>
<div class="text-white font-medium">Apply routing rules</div>
<div class="text-gray-400 text-sm">/api/* to API servers, /static/* to CDN</div>
</div>
</div>
</div>
<div class="mt-4 p-3 bg-blue-500/10 rounded-lg">
<div class="text-blue-300 text-sm font-medium">Key insight:</div>
<div class="text-gray-400 text-sm">Full visibility into application data - can route by content</div>
</div>
</div>
</div>
</section>
<!-- Feature Comparison Table -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Feature Comparison</h2>
<div class="bg-gray-800/50 rounded-xl border border-gray-700 overflow-hidden">
<table class="w-full">
<thead>
<tr class="border-b border-gray-700">
<th class="text-left p-4 text-gray-400 font-medium">Feature</th>
<th class="text-left p-4 text-purple-400 font-medium">Layer 4 (NLB)</th>
<th class="text-left p-4 text-blue-400 font-medium">Layer 7 (ALB)</th>
</tr>
</thead>
<tbody>
{#each features as feature, i}
<tr class="border-b border-gray-700/50 {i % 2 === 0 ? 'bg-gray-800/30' : ''}">
<td class="p-4 text-white font-medium">{feature.name}</td>
<td class="p-4 text-gray-300">{feature.l4}</td>
<td class="p-4 text-gray-300">{feature.l7}</td>
</tr>
{/each}
</tbody>
</table>
</div>
</section>
<!-- When to Use -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">When to Use Each</h2>
<ComparisonTable options={comparisonOptions} />
</section>
<!-- AWS Services -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">AWS Load Balancer Types</h2>
<div class="grid md:grid-cols-2 lg:grid-cols-4 gap-4">
{#each awsServices as service}
<div class="bg-gray-800/50 rounded-xl p-4 border border-gray-700 hover:border-gray-500 transition-colors">
<div class="flex items-center gap-2 mb-2">
<span class="text-lg font-bold text-white">{service.name}</span>
<span class="px-2 py-0.5 rounded text-xs font-medium
{service.layer === 'L7' ? 'bg-blue-500/20 text-blue-400' : ''}
{service.layer === 'L4' ? 'bg-purple-500/20 text-purple-400' : ''}
{service.layer === 'L3/L4' ? 'bg-orange-500/20 text-orange-400' : ''}
{service.layer === 'L4/L7' ? 'bg-gray-500/20 text-gray-400' : ''}">
{service.layer}
</span>
</div>
<p class="text-gray-400 text-sm">{service.use}</p>
</div>
{/each}
</div>
</section>
<!-- Decision Guide -->
<section class="mb-12">
<h2 class="text-xl font-semibold text-white mb-4">Quick Decision Guide</h2>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<div class="space-y-4">
<div class="flex items-center gap-4 p-4 bg-purple-500/10 rounded-lg border border-purple-500/30">
<div class="text-2xl">1M+</div>
<div>
<div class="text-white font-medium">Need millions of requests/second?</div>
<div class="text-gray-400 text-sm">Use NLB (Layer 4) - designed for extreme performance</div>
</div>
</div>
<div class="flex items-center gap-4 p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
<div class="text-2xl">/api</div>
<div>
<div class="text-white font-medium">Need path-based routing?</div>
<div class="text-gray-400 text-sm">Use ALB (Layer 7) - can route by URL, headers, cookies</div>
</div>
</div>
<div class="flex items-center gap-4 p-4 bg-purple-500/10 rounded-lg border border-purple-500/30">
<div class="text-2xl">IP</div>
<div>
<div class="text-white font-medium">Need static IP addresses?</div>
<div class="text-gray-400 text-sm">Use NLB (Layer 4) - provides static IPs per AZ</div>
</div>
</div>
<div class="flex items-center gap-4 p-4 bg-blue-500/10 rounded-lg border border-blue-500/30">
<div class="text-2xl">WAF</div>
<div>
<div class="text-white font-medium">Need WAF or advanced security?</div>
<div class="text-gray-400 text-sm">Use ALB (Layer 7) - integrates with AWS WAF</div>
</div>
</div>
<div class="flex items-center gap-4 p-4 bg-gray-700/50 rounded-lg border border-gray-600">
<div class="text-2xl">DB</div>
<div>
<div class="text-white font-medium">Load balancing databases or non-HTTP?</div>
<div class="text-gray-400 text-sm">Use NLB (Layer 4) - protocol agnostic</div>
</div>
</div>
</div>
</div>
</section>
<!-- Real World Examples -->
<section>
<h2 class="text-xl font-semibold text-white mb-4">Real World Examples</h2>
<div class="grid md:grid-cols-2 gap-6">
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h3 class="text-lg font-semibold text-white mb-3">E-commerce Platform</h3>
<div class="space-y-2 text-sm">
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 rounded bg-blue-500/20 text-blue-400">ALB</span>
<span class="text-gray-300">/api/* to API servers</span>
</div>
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 rounded bg-blue-500/20 text-blue-400">ALB</span>
<span class="text-gray-300">/static/* to static content servers</span>
</div>
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 rounded bg-blue-500/20 text-blue-400">ALB</span>
<span class="text-gray-300">Sticky sessions for shopping cart</span>
</div>
</div>
</div>
<div class="bg-gray-800/50 rounded-xl p-6 border border-gray-700">
<h3 class="text-lg font-semibold text-white mb-3">Gaming Server</h3>
<div class="space-y-2 text-sm">
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 rounded bg-purple-500/20 text-purple-400">NLB</span>
<span class="text-gray-300">UDP game traffic (low latency)</span>
</div>
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 rounded bg-purple-500/20 text-purple-400">NLB</span>
<span class="text-gray-300">TCP matchmaking servers</span>
</div>
<div class="flex items-center gap-2">
<span class="px-2 py-0.5 rounded bg-purple-500/20 text-purple-400">NLB</span>
<span class="text-gray-300">Static IPs for firewall rules</span>
</div>
</div>
</div>
</div>
</section>
</div>

12
svelte.config.js Normal file
View file

@ -0,0 +1,12 @@
import adapter from '@sveltejs/adapter-auto';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter()
}
};
export default config;

58
tailwind.config.js Normal file
View file

@ -0,0 +1,58 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
darkMode: 'class',
theme: {
extend: {
colors: {
// AWS colors
aws: {
orange: '#FF9900',
dark: '#232F3E',
light: '#37475A'
},
// GCP colors
gcp: {
blue: '#4285F4',
red: '#EA4335',
yellow: '#FBBC04',
green: '#34A853'
},
// Custom theme colors
surface: {
50: '#f8fafc',
100: '#f1f5f9',
200: '#e2e8f0',
300: '#cbd5e1',
400: '#94a3b8',
500: '#64748b',
600: '#475569',
700: '#334155',
800: '#1e293b',
900: '#0f172a',
950: '#020617'
}
},
fontFamily: {
sans: ['Inter', 'system-ui', 'sans-serif'],
mono: ['JetBrains Mono', 'Fira Code', 'monospace']
},
animation: {
'flow': 'flow 2s ease-in-out infinite',
'pulse-slow': 'pulse 3s ease-in-out infinite',
'fade-in': 'fadeIn 0.5s ease-out'
},
keyframes: {
flow: {
'0%, 100%': { strokeDashoffset: '0' },
'50%': { strokeDashoffset: '20' }
},
fadeIn: {
'0%': { opacity: '0', transform: 'translateY(10px)' },
'100%': { opacity: '1', transform: 'translateY(0)' }
}
}
}
},
plugins: []
};

14
tsconfig.json Normal file
View file

@ -0,0 +1,14 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"moduleResolution": "bundler"
}
}

6
vite.config.ts Normal file
View file

@ -0,0 +1,6 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [sveltekit()]
});