System Design

Stateful vs Stateless arxitektura

Horizontal scaling uchun stateless bo’lish muhim. Lekin bu nima demak?

Stateful vs Stateless

Stateful

Server o’zida state (holat) saqlaydi:

Request 1: User login
Server 1: Session saqladi (memory/disk)

Request 2: Get profile
Server 1: Session'dan user_id oldi 
Server 2: Session yo'q! 

Muammo: User bitta serverga “bog’langan” (sticky).

Stateless

Server hech narsa saqlamaydi:

Request 1: User login
Server: JWT token qaytardi
Client: Token saqladi

Request 2: Get profile (+ token)
Server 1: Token'dan user_id oldi 
Server 2: Token'dan user_id oldi 

Foyda: Istalgan serverga yuborish mumkin.

Stateful tizim muammolari

1. Horizontal scaling qiyin

┌──────────────┐
│ Load Balancer│
└───────┬──────┘

   ┌────┴─────┐
   ▼          ▼
┌──────┐  ┌──────┐
│Srv 1 │  │Srv 2 │
├──────┤  ├──────┤
│User A│  │User B│ ← Har bir user bitta serverga bog'langan
│sess  │  │sess  │
└──────┘  └──────┘

Agar User A → Server 2 yuborilsa:

2. Server failure = data loss

┌──────┐
│Srv 1 │ ←  Crash!
├──────┤
│User A│
│User B│ ← Ikkala user session yo'qoldi!
│User C│
└──────┘

3. Deployment qiyin

Server 1 yangilash kerak:
1. Server to'xtatish
2. Active userlar logout bo'ladi 
3. Code deploy
4. Server qayta ishga tushirish

Zero-downtime deployment qiyin.

4. Auto-scaling ishlamaydi

CPU low → Scale down
Server 3 o'chirish kerak
Lekin Server 3'da 1000 active session! 

Serverni bemalol o’chira olmaysiz.

Stateless tizimga o’tish

Yechim 1: Client-side storage

State client’da saqlash:

// Login
const token = jwt.sign({ userId: 123 }, SECRET);
res.json({ token });

// Client localStorage'da saqlaydi
localStorage.setItem('token', token);

// Keyingi requestlarda yuboradi
fetch('/api/profile', {
  headers: { Authorization: `Bearer ${token}` }
});

JWT (JSON Web Token):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOjEyMywiaWF0IjoxNjQwOTk1MjAwfQ.
4Hb-tH_3zVx9Gx_XfHl8Pz_yxQ1wF_g8p3C

Server:

// Har qanday server verify qila oladi
const decoded = jwt.verify(token, SECRET);
console.log(decoded.userId); // 123

Stateless: Server hech narsa saqlamaydi

Yechim 2: Distributed cache

State external store’da:

┌──────────────┐
│ Load Balancer│
└───────┬──────┘

   ┌────┴─────┐
   ▼          ▼
┌──────┐  ┌──────┐
│Srv 1 │  │Srv 2 │ ← Stateless serverlar
└──┬───┘  └───┬──┘
   └───┐  ┌───┘
       ▼  ▼
    ┌────────┐
    │ Redis  │ ← Session store
    └────────┘

Code:

// Login - session Redis'da
app.post('/login', async (req, res) => {
  const sessionId = uuid();
  await redis.set(
    `session:${sessionId}`,
    JSON.stringify({ userId: 123 }),
    'EX', 3600 // 1 hour
  );
  res.cookie('sessionId', sessionId);
});

// Profile - istalgan server
app.get('/profile', async (req, res) => {
  const sessionId = req.cookies.sessionId;
  const session = await redis.get(`session:${sessionId}`);
  const { userId } = JSON.parse(session);
  // ...
});

Stateless server + Stateful Redis

Yechim 3: Database session

Session database’da saqlash:

CREATE TABLE sessions (
  id UUID PRIMARY KEY,
  user_id INTEGER,
  data JSONB,
  expires_at TIMESTAMP
);

Kamchilik:

Redis yaxshiroq: 1-2ms, memory-based

JWT vs Session: Taqqoslash

JWT (Stateless)

// Server
const token = jwt.sign(
  { userId: 123, role: 'admin' },
  SECRET,
  { expiresIn: '1h' }
);

// Client
localStorage.setItem('token', token);

// Har requestda
headers: { Authorization: `Bearer ${token}` }

Afzalliklari:

Kamchiliklari:

Session (Stateful)

// Server
const sessionId = generateId();
await redis.set(`sess:${sessionId}`, { userId: 123 });

// Client
cookie: sessionId=abc123

// Har requestda
const session = await redis.get(`sess:${sessionId}`);

Afzalliklari:

Kamchiliklari:

Stateless Best Practices

1. Request’da barcha kerakli ma’lumot

// Bad: Server state'ga bog'liq
let uploadProgress = {};
app.post('/upload', (req, res) => {
  uploadProgress[req.sessionId] = 0;
  // ...
});

// Good: Har request mustaqil
app.post('/upload', (req, res) => {
  const { fileName, chunkIndex, totalChunks } = req.body;
  const progress = chunkIndex / totalChunks;
  // ...
});

2. External state storage

Stateless:
- Serverlar (no state)

Stateful:
- Redis (session, cache)
- Database (permanent data)
- S3 (files)

3. Idempotent operations

Bir xil requestni qayta yuborish xavfsiz bo’lishi kerak:

// Bad: counter serverda
let counter = 0;
app.post('/increment', (req, res) => {
  counter++;
  res.json({ counter });
});

// Good: Database'da atomic
app.post('/increment', async (req, res) => {
  const result = await db.query(
    'UPDATE counters SET value = value + 1 WHERE id = $1 RETURNING value',
    [1]
  );
  res.json({ counter: result.rows[0].value });
});

4. Sticky session’dan qochish

upstream backend {
    ip_hash;
    server srv1;
    server srv2;
}

upstream backend {
    server srv1;
    server srv2;
}

Real-world misollar

Instagram (Stateless)

- Django serverlar: Stateless
- Memcached: Session storage
- PostgreSQL: User data
- Result: 1000+ server instances

Netflix (Stateless)

- Spring Boot microservices: Stateless
- EVCache (Memcached): Cache
- Cassandra: Permanent data
- Result: 100,000+ container instances

WhatsApp (Mostly Stateless)

- Erlang servers: Connection state (unavoidable)
- Mnesia: Distributed database
- FreeBSD servers: Message routing

Izoh: Real-time chat uchun WebSocket connection state kerak, lekin minimal.

Qachon Stateful qabul qilish mumkin?

Ba’zan stateful unavoidable:

1. WebSocket/Long polling

// WebSocket connection = stateful
const connections = new Map();

wss.on('connection', (ws, req) => {
  const userId = getUserId(req);
  connections.set(userId, ws);
});

// Message yuborish
function sendToUser(userId, message) {
  const ws = connections.get(userId);
  ws.send(message);
}

Yechim:

2. File upload (multipart)

// Upload progress = stateful
const uploads = new Map();

app.post('/upload', upload.single('file'), (req, res) => {
  // Temporary state
});

Yechim:

3. Real-time gaming

Game server:
- Player positions
- Game state
- Physics engine

← Bu state server'da bo'lishi shart

Yechim:

Xulosa

Stateful:

Stateless:

Strategiya:

  1. Default: Stateless qiling
  2. State kerak bo’lsa: External store (Redis)
  3. Auth uchun: JWT yoki Redis session
  4. Real-time uchun: Sticky session + backup plan

Keyingi dars: CDN (Content Delivery Network) - global tezlik.