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

Replication và ISR trong Kafka: cách không mất dữ liệu khi broker chết, và vì sao single-node không demo nổi

Độ bền của Kafka đến từ replication — mỗi partition có nhiều bản sao trên nhiều broker, với ISR là tập bản sao đang theo kịp. Bài này đo thật trên lab single-node và dùng chính các giới hạn làm bằng chứng: RF=1 chỉ có một bản sao, thử RF=2 báo lỗi vì chỉ một broker, và một phát hiện trung thực về min.insync.replicas mà single-node không tái hiện nổi. Hiểu RF, ISR, min.insync.replicas và acks=all để cấu hình độ bền đúng trên cluster thật.

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

Sao chép PostgreSQL primary–replica: đo thật — lag 0,32 ms khi rảnh và 52,67 ms p99 khi bận, đọc ngay từ replica không thấy thứ vừa ghi 500/500 lần, và hai cách chữa với giá của từng cách

Một primary PostgreSQL 16 và một streaming replica dựng bằng pg_basebackup. Lag đo bằng cách ghi rồi poll replica: p50 0,32 ms khi primary rảnh, 3,26 ms và p99 52,67 ms khi hai luồng chèn lô nền. Đọc ngay từ replica sau khi ghi: 500/500 lần không thấy, kể cả khi rảnh. Chữa phía đọc bằng cách chờ replica replay tới LSN của lần ghi: 0/500 — với điều kiện LSN lấy sau commit, lấy trong cùng câu INSERT thì vẫn 187/500 sai. Chữa phía ghi bằng synchronous_commit: remote_apply cho 0/300 với giá 0,24 so với 0,07 ms mỗi commit, và khi replica đồng bộ dừng, primary treo cho tới khi đổi tham số.

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

CRDT: đo thật ba bản sao tách mạng rồi gặp lại — G-Counter hội tụ đúng 30.000 sau 2 vòng, bộ đếm hợp nhất bằng max mất 20.000, và OR-Set tốn gấp 34 lần một tập thường

Ba bản sao Go đồng bộ kiểu anti-entropy qua HTTP, mỗi bản sao tăng bộ đếm 10.000 lần trong khi bản sao thứ ba bị cắt mạng. Nối lại: G-Counter hội tụ về đúng 30.000 ở cả ba sau 2 vòng đồng bộ và 1,3 ms; bộ đếm kiểu một số hợp nhất bằng max cho 10.000, mất 20.000 lần đếm; LWW-register giữ bản của đồng hồ nhanh nhất, cũng mất hai phần ba. Giỏ hàng OR-Set với thêm và xoá đồng thời ở hai bản sao hội tụ giống hệt nhau theo luật add-wins, kèm tombstone không bao giờ tự mất. Giá: OR-Set 1.000 phần tử chiếm 150,8 KB so với 4,4 KB của tập thuần, còn G-Counter chỉ 13 byte.

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

Streaming replication đo thật: replica 700 MB streaming sau 1,7 giây, lag dưới tải trung vị 9 KB và tối đa 82 ms, số đo giây lên 20 giây khi primary im lặng dù lag thật bằng 0, tắt replica 20 giây thì slot giữ 476 MB và bắt kịp sau 5,2 giây

Dựng replica PostgreSQL 16 bằng pg_basebackup -R với replication slot: 961 ms chép 700 MB, trả lời truy vấn sau 1,7 giây, lag 0. Dưới pgbench 8 client sinh 25 MB WAL mỗi giây, lag byte trung vị 9 KB, tối đa 50 KB, replay_lag trung vị 1 ms và tối đa 82 ms. Khi primary im lặng, now trừ pg_last_xact_replay_timestamp tăng 5, 10, 20 giây trong khi lag byte bằng 0. Tắt replica 20 giây giữa tải: slot giữ 476 MB WAL, bật lại bắt kịp sau 5,2 giây. Không slot và wal_keep_size 0: 16 segment bị dọn, replica báo WAL segment has already been removed, phải dựng lại từ basebackup trong 1,6 giây.

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

Đọc trên replica đo thật: truy vấn dài bị huỷ sau 2,2 giây hoặc 30,9 giây tuỳ max_standby_streaming_delay, chờ mãi thì lag phình 454 MB trong 12 giây, feedback hết huỷ nhưng để lại 400.000 dead tuple trên primary, đọc ngay sau ghi trượt 299/300

Trên replica PostgreSQL 16 ở chế độ hot standby, một truy vấn 40 giây giữ snapshot gặp VACUUM từ primary: với max_standby_streaming_delay 2 giây bị huỷ sau 2,2 giây, với 30 giây mặc định bị huỷ sau 30,9 giây, với -1 chạy xong nhưng lag replica lên 454 MB và 12 giây dưới tải. Bật hot_standby_feedback thì truy vấn chạy xong, xmin của replica xuất hiện trong slot và VACUUM VERBOSE trên primary báo 400.000 dead tuple chưa dọn được. Đọc ngay sau ghi bằng Go pgx: 299 trong 300 lượt chưa thấy dòng vừa commit, thấy sau 1,4 ms trung vị và tối đa 120 ms dưới tải, dù lag báo 0 byte.