Chào bạn, mình là Thái 🚀

Nơi mình chia sẻ về Hugo, PaperMod và những hành trình công nghệ.
Hai dòng thời gian 3 giây của bộ đếm redo trên MariaDB 11.8 ở flush_log_at_trx_commit=0: Innodb_os_log_written nhảy 526 lần, mỗi 5,6 ms; Innodb_lsn_flushed nhảy 3 lần, mỗi 1002 ms, giữa các lần nhảy redo vẫn nằm trong RAM; bên dưới, kill -9 năm lần làm mất 9.123 commit đã báo thành công

Bộ đếm nói MariaDB ghi redo log mỗi 5,6 ms. Thật ra mỗi giây nó mới ghi một lần

Trên dashboard giám sát của bạn chắc có một biểu đồ vẽ từ bộ đếm trạng thái (status counter) mà bạn chọn vì cái tên của nó. Có khi câu truy vấn được chép từ dashboard của một database khác, hoặc của một nhánh khác cùng họ. Đã bao giờ bạn lấy mẫu tay bộ đếm đó, đặt cạnh một bộ đếm mình đã hiểu rõ, để xem hai thứ có tăng như mình nghĩ không? ...

5 tháng 10, 2026 · 9 phút · Hoang Nguyen Thai
Hai biểu đồ cột chi phí đọc ở độ sâu chuỗi 60. Bên trái, gạch chéo đỏ: điều lời giải thích của tôi dự báo, cột oldest cao hơn hẳn newest. Bên phải: điều bảng số đã nói sẵn, newest 1450 ns và oldest 1480 ns bằng nhau, tỉ số 1,02 được đóng khung.

Tôi giải thích một chỗ chậm, ghi lời giải thích vào nhật ký, trong khi tỉ số bác bỏ nó đã nằm sẵn trong bảng

Bạn có một benchmark nói rằng chỗ này chậm. Bạn cũng có sẵn lời giải thích vì sao, và nó nghe rất hợp lý. Nếu bạn đã kịp viết lời giải thích đó vào tài liệu thiết kế hay vào ticket, thì bài này kể về một bước kiểm tra tôi đã bỏ qua ngay trước khi làm y như vậy, và cái giá của nó. Hệ thống trong bài là minidb, một database quan hệ nhỏ tôi tự viết bằng Go để học về tầng lưu trữ. Sai lầm trong bài xảy ra được với bất kỳ benchmark nào có hai dòng số. ...

5 tháng 10, 2026 · 10 phút · Hoang Nguyen Thai
Biểu đồ log-log: đường ngoại suy chấm chấm kết thúc gần 10 ms, hai đường đo thật không index bỏ xa nó (MariaDB 10.11 ở 968 ms và 13.0 ở 148 ms tại 1M dòng), vạch gạch 300 ms, và một đường phẳng dưới 1 ms cho trường hợp có index

Tôi viết ngưỡng thêm index thành con số, rồi tự đo lại và thấy nó lệch 15 đến 100 lần

Bạn đã từng ký duyệt một tài liệu thiết kế. Trong đó thế nào cũng có một câu kiểu “khi vượt X thì làm Y”. Có ai từng sinh thử đúng X dòng để xem con số đó có đúng như nó nói không? Cuối tháng trước, tôi duyệt một thiết kế cho phép tra cứu trên một cột không có index. Bảng chứa cột đó không thuộc team tôi. Tôi viết điều kiện để sửa lại thành một câu có hai con số: “khi bảng vượt khoảng 1.000.000 dòng, hoặc p95 của truy vấn lọc theo voucher vượt 300 ms, thì gửi yêu cầu thêm index non-unique cho team sở hữu.” Câu đó có đúng một nhiệm vụ: đưa phương án “ship mà không thêm index” qua vòng review thiết kế. Nó biến lời hứa “sẽ thêm sau” thành một điều kiện mà người review có thể ký. Và nó đã hoàn thành nhiệm vụ đó. ...

28 tháng 9, 2026 · 12 phút · Hoang Nguyen Thai
Một merge commit có hai cha, và một nhánh thứ ba đẩy thêm một dòng vào file đã resolve

Merge chỉ có đúng hai cha. Một dòng lấy từ nhánh thứ ba làm sập toàn bộ route API

Trong 17 tiếng 55 phút, mọi route dưới /api/* trên staging đều trả về lỗi fatal. Lần merge gây ra chuyện đó lên nhánh lúc 17:18 chiều; bản fix chấm dứt nó lên lúc 11:13 sáng hôm sau. Con số này lấy từ repo riêng và log server, bạn không kiểm chứng được, nên cứ đọc nó như lời kể của tôi. Bản thân lần merge rất bình thường: một nhánh feature vào staging, một conflict, ở file đăng ký HTTP middleware. Conflict là thật, hai bên cùng thêm một dòng ở đúng một vị trí. Thứ hỏng không phải là conflict. Thứ hỏng là cách conflict được “giải quyết”, rồi cách kết quả được kiểm và được coi là xong. ...

23 tháng 9, 2026 · 8 phút · Hoang Nguyen Thai
Thanh 200 đơn vị chia thành 110 đã phân bổ và 90 kho tổng, 10 đơn vị đã bán từ quota tô đỏ bên trong khối 110, và hai phép trừ bên dưới: đọc nghĩa đen ra 80, màn hình production ra 90

Công thức trong ticket trừ hai lần, và chỉ hai ảnh chụp cùng một phút mới bác được nó

Một mã hàng. Tổng nhập kho 200 đơn vị. Ba chương trình khuyến mãi được cấp quota lần lượt 10, 88 và 12, và đã bán ra từ quota đó 1, 9 và 0. Còn bao nhiêu đơn vị khả dụng để bán? Ticket viết: tồn khả dụng = tổng nhập − tổng đã phân bổ − tổng đã bán. Thay số vào thì ra 200 − 110 − 10 = 80. Màn hình production của đúng mã hàng đó, cùng thời điểm, hiện 90. ...

23 tháng 9, 2026 · 8 phút · Hoang Nguyen Thai
Hai bảng có id auto-increment trùng nhau, file import trỏ vào bảng này còn code đọc bảng kia

Import sửa nhầm dòng suốt nhiều năm vì hai bảng có id trùng nhau

Tính năng import điều chỉnh quota chương trình khuyến mãi từ file bảng tính đã chạy trên production nhiều năm. Lần kiểm thử thủ công nào cũng giống nhau: tải một file lên, mở chương trình ra, thấy một quota đã đổi, đóng ticket. Handler này không có một test tự động nào. Nó đang sửa nhầm dòng. Không phải thỉnh thoảng, mà về cấu trúc: lần chạy nào mà hai id trùng nhau thì lần đó sửa sai, và trên staging thì chúng trùng thật. Trong những năm đó nó đã đụng vào bao nhiêu dòng trên production, tôi chưa đo và sẽ không đoán ở đây. Thứ tôi chỉ ra được là cơ chế, vì sao ba lớp bảo vệ khác nhau đều để nó đi qua, và một test duy nhất mà nếu có từ ngày đầu thì đã bắt được. ...

22 tháng 9, 2026 · 8 phút · Hoang Nguyen Thai
Một dòng mồ côi vẫn bị tính vào kho dùng chung, mà không có màn hình nào giải phóng được

Quota ma: thiếu một foreign key âm thầm hạ trần bán

Bảng campaign_product_variants không có foreign key (khóa ngoại), không ON DELETE CASCADE, cũng không có deleted_at. Trước khi có kho dùng chung, đó chỉ là vấn đề thẩm mỹ: vài dòng trỏ tới một SKU đã không còn tồn tại làm báo cáo hiện ra con số nhỏ hơn thực tế một chút. Tôi không thấy ai coi đó là bug, và tôi cũng không lật lại ticket cũ để kiểm cho chắc. ...

22 tháng 9, 2026 · 6 phút · Hoang Nguyen Thai
Thông báo lỗi validation được vẽ vào một tab đang ẩn, người dùng không nhìn thấy

Mọi lần bấm Tăng đều bị 422, và người dùng không thấy gì hết

Báo lỗi của QC chỉ có hai câu. Bấm Tăng trong modal điều chỉnh số lượng thì không có gì xảy ra. Bấm lần nữa cũng không có gì xảy ra. “Không có gì” là triệu chứng khó nhận nhất. Sập thì còn stack trace, sai kết quả thì còn con số sai; màn hình không đổi gì thì không để lại chỗ nào để bắt đầu. Chuyện thật sự hỏng ở đâu Màn hình này là trang quản trị nội bộ của một hệ thống bán hàng có chương trình khuyến mãi. Người vận hành mở modal để điều chỉnh số lượng đã phân bổ cho một SKU. Modal có hai tab — Tăng và Giảm — dùng chung một nút gửi, tab nào đang mở thì quyết định gửi đi thao tác nào. ...

22 tháng 9, 2026 · 9 phút · Hoang Nguyen Thai
Sơ đồ feature flag làm cổng chặn giữa deploy của team A và team B

Feature flag như cổng release liên team

Có một loại bug tôi chưa bao giờ thấy trong log, vì nó chưa từng chạy: tôi đọc ra nó từ đúng hai dòng code, trước khi viết dòng code đầu tiên của mình. Câu hỏi không phải “sửa thế nào” — mà là “ship một tính năng biết chắc sẽ fail 100% nếu bật sớm thì tính là xong hay chưa xong?” Bối cảnh Đầu tháng 9 năm nay, tôi phụ trách phần chức năng cho một chế độ nguồn hàng thứ hai trong chương trình khuyến mãi của một hệ thống bán hàng. Chế độ đang chạy: mỗi sản phẩm muốn bán trong chương trình phải được cấp trước một quota cố định. Chế độ mới ngược lại: sản phẩm không có quota riêng, trần bán là tồn khả dụng của kho tổng ngay lúc đặt hàng. Tính năng có lịch ship định sẵn, phần việc của tôi nằm trong lịch đó. ...

22 tháng 9, 2026 · 6 phút · Hoang Nguyen Thai
Câu lệnh SELECT FOR UPDATE tan thành chuỗi rỗng trên driver sqlite

Cái lock tôi đánh rơi khi refactor, và 1184 test xanh không hề hay biết

QA gửi đúng một dòng: chưa có test chạy đồng thời trên kho tồn khuyến mãi. Tôi mở file ra, định viết bốn cái test rồi đóng ticket trong buổi chiều. Nhưng cái khóa mà bốn test đó sinh ra để bảo vệ thì đã biến mất khỏi code từ lúc nào không hay. Chuyện gì có thể hỏng ở đây Hệ thống là phần backend của mảng khuyến mãi. Giả sử một mã hàng có 200 sản phẩm trong kho: nhiều chương trình khuyến mãi cùng lấy hàng từ đúng 200 cái đó. Trước khi một chương trình giữ chỗ cho một đơn, service phải làm ba việc theo thứ tự: đọc xem kho còn bao nhiêu, trừ đi phần các chương trình khác đã giữ, rồi ghi phần của mình. ...

21 tháng 9, 2026 · 9 phút · Hoang Nguyen Thai