import pino from "pino";
export const logger = pino({
redact: ["req.headers.authorization", "*.password", "*.cardNumber"],
base: { service: "order-api", version: process.env.GIT_SHA },
});
logger.info({ orderId, tenantId, durationMs }, "order created");
Observability, Reliability & Incident Response
draftedLogs/metrics/traces, structured logging & correlation ID, OpenTelemetry, RED/USE, SLI/SLO/error budget, alert theo triệu chứng, load testing (k6), capacity planning, debug production BE/DB/FE, incident & postmortem.
0/45 rated · mastery 0%
Bài học
12 bài · ~139 phút đọc. Học theo thứ tự, rồi làm câu hỏi.- 01~14 phút· 4 câuverify
Monitoring, observability và ba tín hiệu
Monitoring khác observability ở đâu, logs/metrics/traces mỗi loại trả lời câu hỏi gì và tốn chi phí thế nào, cách nối chúng bằng trace_id và exemplar, và kế hoạch 90 ngày cho một team chỉ có console.log.
- 02~12 phút· 5 câuverify
Structured logging, correlation ID và AsyncLocalStorage
Log JSON với pino (level, child logger, redact), correlation ID qua HTTP header và message header, vì sao biến global gắn nhầm ID khi có request đồng thời, AsyncLocalStorage trong Node, và thiết kế logging multi-tenant không lộ PII.
- 03~11 phút· 4 câuverify
Metrics, histogram, percentile và cardinality
Counter/gauge/histogram/summary trong Prometheus, vì sao average latency và trung bình các p99 nói dối, histogram_quantile gộp đúng giữa instance, cardinality explosion khi gắn userId/url làm label, và cách đặt và đo latency target.
- 04~13 phút· 6 câuverify
RED, USE, golden signals và runtime metrics của Node
RED cho service, USE cho resource, bốn golden signals; saturation trong Node (event loop delay, ELU, connection pool, libuv); đo event loop bị block, tìm memory leak bằng heap snapshot, consumer lag của Kafka và metric cho Socket.IO.
- 05~12 phút· 7 câuverify
Distributed tracing, W3C Trace Context và OpenTelemetry trong Node
Span, trace, traceparent; các thành phần của OpenTelemetry (API, SDK, instrumentation, OTLP, Collector, semantic conventions); instrument Express đúng thứ tự load và lỗi khi load SDK muộn; propagation qua Kafka và span links; vì sao trace trông đầy đủ nhưng giấu thời gian; frontend RUM và so sánh đường cũ/mới khi tách monolith.
- 06~10 phút· 4 câuverify
Sampling, OTel Collector và chi phí telemetry
Head sampling (ParentBased TraceIdRatio) và tail sampling ở Collector, đo thật bao nhiêu trace lỗi bị mất; pipeline của Collector (receiver, processor, exporter, redaction, memory_limiter, load-balancing exporter); chọn stack self-hosted/SaaS/cloud và giữ chi phí khi traffic tăng 10x.
- 07~11 phút· 4 câuverify
SLI, SLO, SLA và error budget
SLI dạng good/total đo từ góc người dùng, SLO là mục tiêu trong cửa sổ, SLA là hợp đồng; tính error budget theo thời gian và theo request, error budget policy, chọn user journey cho nền tảng B2B2C multi-tenant, và trả lời câu hỏi latency target bằng số đo.
- 08~11 phút· 3 câuverify
Alert theo triệu chứng và burn-rate alert
Vì sao page theo triệu chứng chứ không theo nguyên nhân, burn rate là gì, multi-window multi-burn-rate theo SRE Workbook, viết recording/alerting rule Prometheus và unit test bằng promtool (đo thật thời điểm alert bắn và tắt), sửa một rule set tệ, và giảm alert fatigue.
- 09~10 phút· 5 câuverify
Load testing với k6: open vs closed model
Các loại test (smoke, average-load, stress, spike, soak, breakpoint), closed model và coordinated omission, executor constant-arrival-rate và thresholds trong k6, đo thật cùng một server bằng hai mô hình, đo gì ngoài response time, và những cách kết quả load test đánh lừa bạn.
- 10~11 phút· 4 câuverify
Capacity planning, Little's Law và flash sale
Capacity planning dựa trên số đo, Little's Law (L = λ × W) để tính concurrency, pool và số instance, knee của đường latency theo throughput (đo thật bằng k6), headroom và giới hạn dùng chung (DB, third-party), chiến lược load test và chuẩn bị cho flash sale 20x, và kiểm chứng hiệu năng khi tách monolith.
- 11~13 phút· 6 câuverify
Phương pháp debug sự cố production (BE, DB, FE)
Một phương pháp có cấu trúc: xác nhận tác động, tìm thay đổi gần đây, khoanh vùng theo chiều và theo tầng, giả thuyết và bằng chứng. Áp dụng cho p99 tăng mà p50 không đổi, lỗi 500 ngẫu nhiên 1/200 (tái hiện thật ECONNRESET do keep-alive), query 20 ms thành 8 s (tái hiện lock blocking trong Postgres), dashboard xanh mà một region kêu hỏng, và cách kể câu chuyện debug trong phỏng vấn.
- 12~11 phút· 5 câuverify
Incident response, on-call và postmortem blameless
Vai trò trong incident (Incident Commander, Ops/SME, Communications, scribe), 15 phút đầu, mức độ SEV, mitigate trước root cause, cập nhật định kỳ; postmortem blameless gồm gì, contributing factors, action item có owner và deadline; đo MTTD/MTTR và budget đã tiêu; văn hoá on-call bền vững và cách kể một incident trong phỏng vấn.
Tóm tắt & cheat sheet của track
TL;DR
- Monitoring trả lời "có hỏng không?" (câu hỏi biết trước); observability trả lời "tại sao hỏng?" cho cả câu hỏi chưa biết trước — nhờ telemetry giàu ngữ cảnh (high-cardinality, liên kết được với nhau).
- Ba tín hiệu: metrics (rẻ, tổng hợp, để alert), logs (chi tiết từng sự kiện), traces (đường đi của một request qua nhiều service). Chìa khoá là nối chúng bằng
trace_id/ correlation ID. - OpenTelemetry là chuẩn vendor-neutral để instrument (API + SDK + Collector + OTLP) — instrument một lần, gửi tới backend nào cũng được.
- Reliability đo bằng SLI → SLO → error budget; alert theo triệu chứng người dùng thấy (burn rate), không theo mọi nguyên nhân (CPU 80%).
- Senior interview hay hỏi: "Kể sự cố khó nhất bạn debug" → cần phương pháp (khoanh vùng, giả thuyết, bằng chứng), không phải may mắn. Kết thúc bằng postmortem blameless có action item.
Khái niệm cốt lõi
| Khái niệm | Một câu | Ví dụ ngắn |
|---|---|---|
| Structured logging | Log là JSON có field cố định, query được | {"level":"error","trace_id":"4bf9…","tenant_id":"t_42","msg":"payment failed"} |
| Correlation ID | ID đi theo request qua mọi service/log | Header traceparent / x-request-id → AsyncLocalStorage → mọi log line |
| Span / trace | Span = một đơn vị công việc có start/end + attributes; trace = cây span của một request | GET /checkout → span SELECT orders 120 ms → span POST stripe 800 ms |
| Cardinality | Số giá trị khác nhau của một label | Label user_id trên metric → hàng triệu time series → backend nổ |
| RED | Rate, Errors, Duration — cho service (request-driven) | API: 1.2k rps, 0.3% 5xx, p99 450 ms |
| USE | Utilization, Saturation, Errors — cho resource | DB CPU 85%, 40 connection chờ, 0 disk error |
| Golden signals | Latency, Traffic, Errors, Saturation (Google SRE) | Dashboard đầu tiên của mọi service |
| SLI | Tỉ lệ sự kiện tốt / tổng sự kiện | requests < 300ms và không 5xx / total requests |
| SLO | Mục tiêu cho SLI trong cửa sổ thời gian | 99.9% trong 30 ngày |
| Error budget | 1 − SLO — lượng "được phép hỏng" | 99.9%/30 ngày ≈ 43 phút downtime tương đương |
| Burn rate | Tốc độ tiêu budget so với mức "vừa đủ hết đúng hạn" | Burn rate 14.4 trong 1h = tiêu 2% budget tháng trong 1 giờ |
| Percentile | p50/p95/p99 — không bao giờ lấy trung bình của percentile | p99 = 1% request chậm hơn giá trị này |
Cơ chế hoạt động
Telemetry pipeline với OpenTelemetry:
Phương pháp debug sự cố production (BE / DB / FE):
Trade-offs
| Tín hiệu | Mạnh | Yếu | Chi phí |
|---|---|---|---|
| Metrics | Rẻ, lưu lâu, alert, xu hướng | Mất chi tiết; high-cardinality đắt | Theo số time series |
| Logs | Chi tiết đầy đủ, ngữ cảnh | Đắt khi volume lớn, khó tổng hợp nếu không structured | Theo GB ingest + retention |
| Traces | Thấy latency qua nhiều service, critical path | Cần propagation đúng; phải sampling | Theo span; sampling quyết định |
| Head sampling | Đơn giản, rẻ, quyết định ở đầu request | Có thể bỏ mất trace lỗi/chậm hiếm | Thấp |
| Tail sampling | Giữ trace lỗi/chậm, bỏ trace bình thường | Collector phải buffer toàn bộ trace, phức tạp | Cao hơn |
| Load test type | Mục đích | Hình dạng tải |
|---|---|---|
| Smoke | Script chạy đúng, baseline | Rất ít VU, ngắn |
| Average-load | Hành vi ở tải bình thường | Ramp tới mức trung bình, giữ |
| Stress | Hành vi trên mức bình thường | Tải cao hơn peak |
| Spike | Tăng đột ngột (flash sale) | Nhảy vọt trong vài giây |
| Soak / endurance | Leak, degrade theo thời gian | Tải vừa trong nhiều giờ |
| Breakpoint | Tìm giới hạn | Tăng dần tới khi vỡ |
Pitfalls
- Log text tự do, không có
trace_id/tenant_id→ không nối được log giữa các service. - Log PII/secret (token, password, số thẻ) → sự cố bảo mật + compliance.
- Label metric high-cardinality (
user_id, URL có ID) → chi phí và backend quá tải. - Dùng average latency hoặc lấy trung bình của p99 giữa các instance.
- Alert theo nguyên nhân (CPU, memory) thay vì triệu chứng → alert fatigue, on-call bỏ qua alert thật.
- SLO 100% hoặc SLO không ai dùng để ra quyết định.
- Load test từ laptop, closed model (VU chờ response) → coordinated omission, số đẹp giả tạo.
- Postmortem tìm "người có lỗi" → mọi người giấu thông tin lần sau.
- OTel SDK khởi tạo sau khi đã import
express/pg→ auto-instrumentation không patch được.
Cheat sheet
- Log: JSON, level đúng,
trace_id,span_id,request_id,tenant_id,service,version; không PII; ra stdout. - Node:
AsyncLocalStoragegiữ context request; pino (nhanh) + OTel log correlation. - W3C Trace Context:
traceparent: 00-<trace-id 32 hex>-<span-id 16 hex>-<flags>. - RED cho service, USE cho resource; bốn golden signals cho dashboard mặc định.
- 99.9% / 30 ngày ≈ 43.2 phút · 99.95% ≈ 21.6 phút · 99.99% ≈ 4.3 phút.
- Alert: multi-window multi-burn-rate (ví dụ page khi 2% budget cháy trong 1h, ticket khi 10% cháy trong 3 ngày).
- Load test: open model (arrival-rate) khi mô phỏng user thật; đo p95/p99, error rate, throughput, và saturation của DB/pool.
- Little's Law:
concurrency = throughput × latency. - Incident: phân vai (incident commander, comms, ops), mitigate trước, root cause sau, timeline, postmortem blameless trong vài ngày.
References
- OpenTelemetry — Concepts (signals, context propagation) ↗
- OpenTelemetry — Node.js getting started ↗
- OpenTelemetry — Sampling ↗
- W3C Trace Context ↗
- Google SRE Book — Monitoring distributed systems (golden signals) ↗
- Google SRE Book — Service level objectives ↗
- Google SRE Workbook — Alerting on SLOs ↗
- Google SRE Book — Postmortem culture ↗
- Brendan Gregg — The USE Method ↗
- Grafana k6 — Load test types ↗
- Grafana k6 — Open and closed models ↗
- Prometheus — Metric and label naming / cardinality ↗
- Node.js — AsyncLocalStorage ↗
Personal notes (Notion)
Ghi chú cá nhân — có thể sai hoặc lỗi thời, dùng để tham khảo.
Interview Questions
Showing 45 / 45
app.use((req, _res, next) => {
(global as any).requestId = req.headers["x-request-id"] ?? crypto.randomUUID();
next();
});
export function log(msg: string) {
console.log(`${new Date().toISOString()} [${(global as any).requestId}] ${msg}`);
}
// tracing.ts — load with: node --import ./dist/tracing.js dist/server.js
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
const sdk = new NodeSDK({
serviceName: "order-api",
traceExporter: new OTLPTraceExporter(),
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
process.on("SIGTERM", () => sdk.shutdown());
import http from "k6/http";
import { sleep } from "k6";
export const options = { vus: 500, duration: "5m" };
export default function () {
http.get("https://staging.example.com/api/products");
sleep(1);
}
- alert: HighCPU
expr: avg(rate(process_cpu_seconds_total[1m])) > 0.8
for: 0m
labels: { severity: page }
- alert: AnyError
expr: increase(http_requests_total{status=~"5.."}[1m]) > 0
labels: { severity: page }
- alert: SlowRequests
expr: avg(http_request_duration_seconds_sum / http_request_duration_seconds_count) > 0.5
labels: { severity: page }