Newsletter #113

Mời bạn thưởng thức Newsletter #113.

Migrating from Go to Rust

Matthias Endler viết một hướng dẫn dài cho các nhóm backend đang cân nhắc chuyển dịch vụ từ Go sang Rust. Thông điệp chính không phải “Rust luôn nhanh hơn Go”, mà là Rust đẩy nhiều loại rủi ro vào hệ thống kiểu để trình biên dịch bắt trước khi lên môi trường production: giá trị nil, xử lý lỗi, data race, vòng đời tài nguyên và quyền sở hữu (ownership). Go tối ưu cho tốc độ làm việc của cả nhóm: biên dịch nhanh, thư viện chuẩn mạnh, goroutine dễ dùng. Rust đổi lại bằng trình biên dịch khắt khe hơn, không có GC, cùng Result, Option, trait và kiểm tra Send/Sync. Các thói quen của Go cần được ánh xạ lại: if err != nil thành Result với ?, nil thành Option, interface thành trait, goroutine thành task khi thật sự cần.

Phần thực tế nhất là chiến lược chuyển đổi: đừng viết lại toàn bộ. Hãy bắt đầu từ hot path, worker hoặc dịch vụ có ranh giới rõ, giữ nguyên API rồi chuyển lưu lượng dần từng endpoint qua gateway theo kiểu strangler pattern. cgo dùng được nhưng thường phức tạp hơn so với tách phần Rust thành dịch vụ riêng. Tác giả cũng thẳng thắn về chi phí: borrow checker khiến tháng đầu vất vả, biên dịch chậm hơn, async coloring gây phiền và một số mảng hệ sinh thái còn nhỏ. Vì vậy không cần bỏ Go hoàn toàn: Go vẫn hợp cho công cụ Kubernetes, CLI và các dịch vụ kết nối “nhàm chán”, còn Rust đáng đầu tư cho dịch vụ nền tảng nơi độ tin cậy, độ trễ P99 và các lỗi như nil hay data race gây chi phí vận hành lớn hơn chi phí học và xây dựng.

AI Engineering for Developers

Luca Cavallin viết một hướng dẫn dài về AI engineering cho lập trình viên đã quen backend, HTTP, hàng đợi, Kubernetes và vận hành production. Luận điểm chính: khi đưa LLM vào sản phẩm, mô hình không phải toàn bộ sản phẩm; thứ thực sự tạo ra giá trị là hệ thống bao quanh nó, gồm prompt, RAG, eval, agent, suy luận (inference), khả năng quan sát, bảo mật và chi phí. Vì vậy công việc này gần kỹ thuật backend hơn huấn luyện mô hình. Đầu ra của AI không đúng/sai tuyệt đối và có thể đổi theo mô hình, prompt hay dữ liệu, nên một bản demo chưa đủ để phát hành: cần bộ dữ liệu eval, kiểm tra hồi quy mỗi lần đổi prompt hoặc mô hình, triển khai canary và giám sát ngay từ đầu.

Bài viết đi từ nền tảng tới các phần thực dụng: chọn mô hình theo tác vụ, chi phí và độ trễ; tận dụng prompt engineering trước khi nghĩ tới RAG; coi RAG chủ yếu là bài toán tìm kiếm, nơi hybrid search, reranker, cách chia nhỏ tài liệu và trích dẫn nguồn quan trọng hơn việc nhồi thêm ngữ cảnh; chỉ finetune khi prompt và RAG đã hết tác dụng; tối ưu suy luận bằng cache, gom lô và định tuyến giữa các mô hình. Agent được mô tả như một vòng lặp gọi công cụ cần schema rõ ràng, quyền hạn hẹp, tracing xuyên suốt và bước phê duyệt cho hành động nguy hiểm. Phần production nhấn mạnh guardrail, quy trình CI/CD riêng cho thay đổi prompt và mô hình, phân bổ chi phí theo từng tính năng, và nguyên tắc xem mỗi agent như một dịch vụ có danh tính, quyền tối thiểu và nhật ký kiểm toán.

The invisible engineering behind Lambda’s network

Bài viết trên All Things Distributed kể lại gần một thập kỷ tối ưu hạ tầng mạng phía sau AWS Lambda, loại công việc “vô hình” mà khách hàng chỉ cảm nhận qua việc hàm khởi động nhanh và ổn định hơn. Vấn đề ban đầu là cold start khi Lambda kết nối vào VPC: ngoài tạo microVM Firecracker và khởi động runtime, hệ thống còn phải dựng đường mạng riêng vào VPC của khách hàng, trong đó việc tạo Geneve tunnel từng nằm trên hot path. Thay vì duy trì bản vá kernel riêng, đội Lambda dùng eBPF: tạo sẵn tunnel với VNI giả, rồi ghi đè header khi VNI thật xuất hiện, nhờ đó độ trễ tạo tunnel giảm từ khoảng 150ms xuống 200 micro giây.

Phần đáng học hơn là cách họ xử lý quy mô 4.000 mạng trên mỗi worker. Thay vì tạo tap, veth, namespace và rule mỗi khi hàm được gọi, họ tạo sẵn toàn bộ lúc worker khởi động, biến chi phí thay đổi theo tải thành chi phí cố định trả một lần. Họ thay NAT có trạng thái bằng thao tác sửa gói tin không trạng thái bằng eBPF, giảm độ trễ thiết lập NAT 100 lần; chuyển hơn 125.000 rule iptables ở root namespace thành 144 rule tĩnh bằng cách đưa rule riêng của từng slot vào namespace tương ứng; rồi giảm tranh chấp khóa RTNL bằng cách đổi thứ tự tạo mạng và gắn eBPF theo lô. Ở quy mô lớn, rule iptables, conntrack hay một khóa kernel đều có thể thành nút thắt. Kết quả là một topology thống nhất cho cả workload truyền thống lẫn SnapStart, tăng năng lực mạng cho snapshot 20 lần, và được đóng gói lại để Aurora DSQL dùng chung.

Selective Test Execution at Stripe: Fast CI for a 50M-line Ruby monorepo

Aditya Anchuri mô tả cách Stripe giữ CI đủ nhanh cho một monorepo Ruby khoảng 50 triệu dòng, với gần 100.000 tệp kiểm thử và 1,2 triệu đơn vị kiểm thử. Chạy tuần tự toàn bộ sẽ mất khoảng bốn tháng, trong khi Stripe chạy khoảng 50.000 lần build mỗi tuần. Hệ thống Selective Test Execution (STE) giải quyết bằng cách trung bình chỉ chạy khoảng 5% bộ kiểm thử cho mỗi lần build (trung vị dưới 0,5%) và tốn chưa tới 10% tài nguyên tính toán so với chiến lược luôn chạy tất cả.

Điểm hay là Stripe không cố phân tích phụ thuộc tĩnh cho Ruby, vì metaprogramming, dynamic dispatch, cấu hình và tệp sinh tự động khiến phân tích tĩnh hoặc bỏ sót, hoặc quá bảo thủ. Thay vào đó họ quan sát lúc chạy: một thư viện C++ nội bộ nạp qua LD_PRELOAD chặn mọi lần mở tệp, gắn tệp được đọc với kiểm thử đang chạy và lan sang cả tiến trình con. Đường xử lý open chỉ ghi log tối thiểu; việc tổng hợp và lập chỉ mục nằm ngoài hot path. Log được gom thành chỉ mục roaring bitmap ánh xạ từ tệp thay đổi sang kiểm thử cần chạy, lưu khoảng ba tỷ điểm dữ liệu mà vẫn truy vấn nhanh. Thay đổi được phát hiện bằng hashdeep để bao phủ cả tệp sinh tự động, không chỉ tệp trong git. STE còn cần dữ liệu nền tái lập được và cơ chế an toàn cho CI thực tế: luôn chạy lại kiểm thử đang lỗi, luôn chạy kiểm thử dò tệp theo glob, xử lý linter theo danh sách tệp đổi, và lưu metadata trong MongoDB kèm Monotonic Revision ID để chọn nhanh bản nền theo thứ tự commit.

Being oncall taught me everything

Yao Yue nhìn lại 7,5 năm trực oncall cho hệ thống cache phân tán ở Twitter, dịch vụ có thông lượng cao nhất và cũng dính nhiều sự cố nghiêm trọng nhất. Bài viết không tô hồng oncall, nhưng cho rằng chính việc sống cùng production đã hình thành tư duy kỹ sư hạ tầng của tác giả. Oncall dạy rằng ở quy mô lớn, tính dự đoán được quan trọng hơn việc nhanh trong đa số trường hợp, tail latency quan trọng hơn trung vị, kiến trúc đơn giản rõ ràng là cứu cánh lúc khủng hoảng, và sự xuất sắc trong vận hành (quan sát kỹ, cấu hình nhất quán, sẵn sàng tự động hóa, giá trị mặc định hợp lý) đến từ thiết kế ban đầu chứ không phải vá vội hai tuần trước khi ra mắt.

Phần đáng nhớ hơn là bài học về con người. Việc định kỳ khởi động lại Memcached để tránh sập toàn trang đôi khi lại tự gây sự cố, nên tác giả học cách chọn thời điểm ít rủi ro, báo trước cho mọi người và quan trọng nhất là nhận trách nhiệm khi mắc lỗi, vì đó là cách nhanh nhất để lấy lại niềm tin của đồng nghiệp. Nhiều sự cố chỉ được giải quyết nhờ sự kiên trì thu hẹp khả năng và sự giúp đỡ của đồng nghiệp ở mọi mảng hạ tầng: kỹ sư vận hành, kernel, chủ dịch vụ upstream, quản lý và đồng đội cùng trực chiến. Kết luận rất thẳng: kỹ sư chưa thật sự hiểu phần mềm cho tới khi thấy nó chạy và hỏng trong production, và người viết phần mềm không nên nghĩ mình đứng ngoài việc triển khai, giám sát hay gỡ lỗi chính thứ mình tạo ra.

Claude as your performance analysis partner

Archana Ravindar thử dùng Claude để hỗ trợ phân tích hiệu năng Go qua CPU profile và trace, tập trung vào bộ thu gom rác Green Tea trên kiến trúc POWER10 với bộ benchmark sweet. Giá trị của Claude không nằm ở việc tự tạo ngay bản vá đúng, mà ở khả năng đọc nhanh những tệp rất lớn và khó nhìn như pprof, bản dump assembly hay dòng thời gian trace. Ví dụ, Claude chỉ ra hot path trong runtime.tryDeferToSpanScan, phát hiện mẫu atomic Load8 rồi Or8 và đề xuất gộp thành một lệnh Or32. Thử nghiệm này lại chậm hơn, và Claude cũng giúp giải thích nguyên nhân là false sharing và tranh chấp giữa các worker GC, một minh họa rõ rằng giảm số lệnh atomic chưa chắc đã nhanh hơn.

Claude còn hữu ích khi phân tích ở mức thấp: đối chiếu assembly để thấy trình biên dịch không cache lại q.class.sizeclass, xếp hạng các TODO trong GC theo độ nóng của profile, và gợi ý các mẫu cần tìm trong trace như processor nhàn rỗi khi GC, khoảng trống mark assist, GC chạy quá dày hoặc gcMarkDone kéo dài. Tác giả phân biệt rõ vai trò: profile cho biết chi phí nằm ở đâu, còn trace giúp hiểu vì sao hệ thống bị nghẽn, nhất là với GC và lập lịch. Tuy vậy mọi gợi ý đều phải đo lại, vì tối ưu phụ thuộc kiến trúc, cache line, tập lệnh hay áp lực thanh ghi rất dễ sai khi thiếu ngữ cảnh; cách dùng tốt nhất là để thu hẹp vùng nghi vấn và lập danh sách điều tra, không phải thay thế benchmark.

Things you didn’t know about indexes

Jon Charter giải thích index trong Postgres bằng một ví dụ dễ hiểu: index giống mục lục sách, giúp cơ sở dữ liệu tìm dữ liệu qua một cấu trúc đã sắp xếp thay vì quét toàn bảng. Nhưng đi kèm là một đánh đổi quan trọng: đọc nhanh hơn thì ghi chậm hơn. Mỗi lệnh INSERT, UPDATE, DELETE phải cập nhật thêm index; index còn tốn dung lượng, chiếm cache và khiến bộ lập kế hoạch truy vấn có thêm phương án phải cân nhắc. Vì vậy “đánh index cho mọi thứ” hiếm khi là chiến lược tốt.

Phần hữu ích nhất là những lỗi phổ biến khiến index không được dùng. Composite index phụ thuộc thứ tự cột, nên thứ tự phải bám theo mẫu truy vấn thật: (type_1, type_2) giúp truy vấn theo type_1 hoặc cả hai cột, nhưng hầu như vô ích nếu chỉ lọc theo type_2. Áp hàm lên cột như lower(name) khiến index thường trên name bị bỏ qua, vì cơ sở dữ liệu cần cấu trúc sắp xếp theo đúng biểu thức lower(name); phép chuyển kiểu ngầm định cũng gây hiệu ứng tương tự. Cách kiểm tra đúng là dùng EXPLAIN hoặc EXPLAIN ANALYZE để xác nhận bộ lập kế hoạch thực sự chọn index nào, thay vì đoán. Bài viết cũng giới thiệu functional index, partial index cho những lát dữ liệu nhỏ như bản ghi xóa mềm hay is_legendary = true, và covering index với INCLUDE để một số truy vấn chạy bằng Index Only Scan.


Bài viết đã được viết lại bởi Claude Code với Opus 5.5 vào ngày 27/09/2026.

Made by miti99 with ❤️
Built with Hugo
Theme Stack thiết kế bởi Jimmy