Bài viết mới nhất

Tổng 2300 bài
Lập trình 22/09/2026 9 phút

Timeout, retry và deadline xuyên suốt: đo thật chuỗi gateway → A → B — retry ở mỗi tầng đẩy p99 lên 771 ms và 98 lần A làm việc vào hư không, deadline truyền xuống giữ p99 255 ms

Ba service trong một chuỗi gọi, B chậm 400 ms ở 30% request, người dùng chịu được 250 ms. Không timeout: p99 410 ms. Mỗi tầng tự đặt timeout: gateway bỏ đi lúc 250 ms nhưng A vẫn chờ B tới cùng — 61 lần làm việc vào hư không, 139/200 thành công. Mỗi tầng tự retry: 194/200 thành công nhưng p99 771 ms, B nhận gấp 2,1 lần request, 98 lần A làm việc vô ích. Deadline 250 ms truyền qua header và context: 180/200, p99 255 ms, 0 việc vô ích, và khi gateway huỷ thì cả A lẫn B dừng theo — 80 lần B dừng sớm thay vì chạy hết 400 ms.

Lập trình 22/09/2026 8 phút

Cache-Control trên HTTP API: đo thật qua nginx — max-age=5 cắt 99% request tới origin, và lúc hết hạn stale-while-revalidate cho 50 client đồng thời chỉ lọt 1

Origin Go mất 40 ms mỗi request, nginx proxy_cache đứng trước làm cache dùng chung, client bắn 300 request trong 12 giây cho từng chỉ thị Cache-Control. Không header, no-store và private: cả 300 tới origin, p50 44 ms. max-age=5: 3 tới origin, p50 0,8 ms, dữ liệu cũ tối đa 5,93 giây. Thứ max-age không chữa được là khoảnh khắc hết hạn: 50 client gọi đúng lúc đó thì 50 request lọt tới origin; thêm stale-while-revalidate=30 thì chỉ 1 lọt, 49 client còn lại nhận bản cũ trong 4 ms. Kèm chuyện private bị cache dùng chung bỏ qua, và s-maxage để tách hạn cho cache dùng chung với trình duyệt.

Lập trình Go 22/09/2026 10 phút

Prepared statement caching trong Go: chuẩn bị một lần, chạy nhiều lần

Một câu lệnh có tham số đi qua hai pha ở DB: PREPARE (phân tích + lập kế hoạch, đắt) và EXECUTE (rẻ). Chạy cùng câu lệnh N lần mà prepare lại mỗi lần là lãng phí. Đo thật trong Go: 500 truy vấn với prepare mỗi lần mất 1.98s (500 prepare), prepare một lần rồi tái dùng chỉ 673ms (1 prepare) — nhanh 2.9 lần.

Lập trình 22/09/2026 8 phút

Hot key: đo thật một key hút 20,8% lưu lượng và dồn 4,44× tải lên đúng một shard

Hit ratio 100%, mọi biểu đồ xanh, mà một node Redis nóng rực còn các node khác ngồi chơi. Bài này đo thật trên 1 triệu request phân bố Zipf qua 10.000 key, dùng chính thuật toán CRC16 của Redis Cluster: key nóng nhất chiếm 20,81% toàn bộ lưu lượng, top 10 key chiếm 51,46%, và shard nặng nhất trong 16 shard gánh 4,44 lần mức trung bình. Thêm shard không chữa được vì một key luôn thuộc đúng một slot. Tách key nóng thành 16 bản sao kéo độ lệch xuống 2,44× và tách 100 key nóng nhất xuống 1,63×.

Lập trình 22/09/2026 9 phút

Failover PostgreSQL đo thật: kill primary dưới tải, promote replica trong 179 ms, bất đồng bộ mất 22 giao dịch đã COMMIT trong 144.531, synchronous_commit=on mất 0 nhưng thừa 1 commit client không được báo, pg_rewind đưa primary cũ về trong 2,5 giây

Dựng thật trên PostgreSQL 16 với writer Go đếm từng COMMIT được xác nhận: SIGKILL primary ở giây thứ 10 dưới pgbench 8 client, pg_promote replica xong sau 179 ms. Bất đồng bộ: 144.531 commit được xác nhận, 144.509 có trên primary mới, mất 22 với lag chỉ 10 KB. Đồng bộ với synchronous_commit on: 13.480 xác nhận, 13.481 có mặt, mất 0 và thừa 1 commit client chưa được báo. Giá của từng nấc: INSERT 0,05 ms ở off, 0,23 ms ở on, TPS từ 19.534 xuống 3.736 ở remote_apply; replica tắt thì INSERT treo 9 phút và statement_timeout không cắt được. pg_rewind chép 1,4 GB trong 2,5 giây.

Lập trình 22/09/2026 9 phút

VACUUM, bloat và wraparound đo thật: mười lần UPDATE toàn bảng biến 27 MB thành 296 MB, VACUUM giải phóng 90% chỗ nhưng không trả đĩa, autovacuum mặc định không chạy lần nào trong 60 giây tải, và một giao dịch mở giữ 600.000 dead tuple không dọn được

Đo trên PostgreSQL 16 với pgstattuple: bảng 200.000 dòng sau 10 UPDATE toàn bảng phình từ 27 MB lên 296 MB, VACUUM 599 ms đưa dead về 0,13% và free lên 90% nhưng kích thước giữ nguyên, VACUUM FULL 278 ms trả về 27 MB. Dưới pgbench 60 giây trên 3 triệu dòng, autovacuum mặc định chạy 0 lần và dead tuple lên 96.000; chỉnh scale 0.01, naptime 5 giây, cost_delay 0 thì chạy 4 lần. Một giao dịch mở 40 giây làm VACUUM báo 600.000 dead but not yet removable và bảng phình 108 lên 189 MB dù vacuum đều. Hạ freeze_max_age xuống 100.000 rồi đốt 160.000 XID: 773 lần aggressive vacuum giữ age dưới ngưỡng.