Syllabus software engineering (5)
PHẦN C: C++ TRACK (Ngày 546–581)
gRPC (Ngày 546–553)
Ngày 546: gRPC C++ - Setup
Mục tiêu: Bắt đầu track C++ bằng công cụ liên hệ trực tiếp Giai đoạn 6 và 9.
Lý thuyết: Ôn lại gRPC dùng HTTP/2 + Protobuf đã học ở Giai đoạn 6/9, giờ thực hành bằng C++.
Thực hành: Cài
protoc+ gRPC C++ plugin, generate code từ file.protođã viết ở Ngày 452.
Ngày 547: Unary RPC
Mục tiêu: Implement loại RPC đơn giản nhất.
Lý thuyết: Unary RPC - 1 request, 1 response, giống REST endpoint thông thường.
Thực hành: Implement server và client cho method
ChargePayment(unary) bằng C++.
Ngày 548: Server Streaming RPC
Mục tiêu: Học loại RPC trả về nhiều response cho 1 request.
Lý thuyết: Server streaming - client gửi 1 request, server trả về stream nhiều response (VD: theo dõi trạng thái giao dịch theo thời gian thực).
Thực hành: Implement
StreamPaymentStatus- server liên tục gửi cập nhật trạng thái cho tới khi giao dịch hoàn tất.
Ngày 549: Client & Bidirectional Streaming
Mục tiêu: Học 2 loại streaming còn lại.
Lý thuyết: Client streaming (client gửi nhiều request, server trả 1 response cuối), Bidirectional streaming (cả 2 bên gửi stream độc lập).
Thực hành: Implement 1 ví dụ bidirectional streaming đơn giản (VD: chat 2 chiều giữa client-server).
Ngày 550: gRPC Interceptor
Mục tiêu: Liên hệ lại Middleware/AOP đã học.
Lý thuyết: Interceptor cho phép xen vào trước/sau mỗi RPC call, tương tự Middleware (Giai đoạn 3) và AOP (Ngày 476-483).
Thực hành: Viết 1 Interceptor log thời gian xử lý mỗi RPC call.
Ngày 551: gRPC Internals - Liên hệ HTTP/2
Mục tiêu: Hiểu gRPC thực sự chạy trên nền tảng đã học ở Giai đoạn 6.
Lý thuyết: Mỗi RPC call là 1 HTTP/2 stream, multiplexing cho phép nhiều RPC call chạy song song trên 1 connection - chính là lý do gRPC nhanh hơn REST/HTTP1.1 cho giao tiếp nội bộ.
Thực hành: Dùng Wireshark bắt gói tin của 1 gRPC call, xác nhận nó chạy trên HTTP/2 frame.
Ngày 552: Lab - Payment Service bằng gRPC C++
Mục tiêu: Có 1 phiên bản C++ thật của service đã xây bằng Java/Python.
Lý thuyết: Ôn lại domain logic Payment Service (Giai đoạn 7-8) - chỉ viết lại API layer bằng gRPC C++.
Thực hành: Implement Payment Service API layer bằng gRPC C++, tái sử dụng ý tưởng domain logic.
Ngày 553: Review gRPC
Mục tiêu: Chốt lại nhóm gRPC.
Lý thuyết: Tổng hợp: 4 loại RPC, Interceptor, nền tảng HTTP/2.
Thực hành: Viết tổng kết so sánh trải nghiệm implement cùng use case bằng REST (FastAPI), Spring MVC, và gRPC C++.
Poco (Ngày 554–559)
Ngày 554: Poco C++ Libraries - Giới thiệu
Mục tiêu: Làm quen bộ thư viện C++ toàn diện, ít phổ biến hơn Boost nhưng thực dụng cho networking.
Lý thuyết: Poco gồm nhiều module: Foundation (utility), Net (networking), Data (database), XML/JSON - thiết kế hướng tới ứng dụng network thực tế.
Thực hành: Cài đặt Poco, xem qua cấu trúc thư mục source, xác định module nào sẽ dùng cho lab tuần này.
Ngày 555: Poco::Net - HTTP Server/Client
Mục tiêu: Xây HTTP server bằng thư viện cấp cao hơn socket thô đã dùng ở Giai đoạn 6.
Lý thuyết:
Poco::Net::HTTPServer,HTTPRequestHandler- abstraction cao hơn socket API trực tiếp.Thực hành: Đọc source
HTTPServerđể thấy nó vẫn dùng socket + thread pool bên dưới (liên hệ Giai đoạn 5-6).
Ngày 556: Poco::Foundation
Mục tiêu: Làm quen utility class hữu ích, liên hệ Concurrency (Giai đoạn 5).
Lý thuyết:
Poco::Thread,Poco::Mutex,Poco::StringTokenizer- nhiều class tương đương những gì đã tự viết/học ởstd::thread/std::mutex.Thực hành: So sánh API
Poco::Threadvớistd::threadđã học Giai đoạn 5.
Ngày 557: Lab - HTTP Server bằng Poco
Mục tiêu: Thực hành xây dựng server thật.
Lý thuyết: Ôn lại
HTTPRequestHandlerFactorypattern (thực chất là Factory Method, Giai đoạn 3).Thực hành: Viết HTTP server đơn giản bằng Poco, phục vụ 1 endpoint GET trả JSON.
Ngày 558: Đọc Source Code Poco
Mục tiêu: Luyện kỹ năng đọc source thư viện thật.
Lý thuyết: Ôn lại phương pháp đọc source đã học Ngày 457.
Thực hành: Chọn 1 class Poco (VD:
Poco::Net::HTTPServer), trace luồng từ lúcstart()tới lúc nhận request đầu tiên.
Ngày 559: Review Poco
Mục tiêu: Chốt lại nhóm Poco.
Lý thuyết: Tổng hợp lại vai trò của Poco trong hệ sinh thái C++ network programming.
Thực hành: Viết nhận xét ngắn: Poco::Net so với việc tự viết bằng raw socket (Giai đoạn 6) tiết kiệm được gì.
Boost (Ngày 560–567)
Ngày 560: Boost - Giới thiệu
Mục tiêu: Hiểu vai trò lịch sử và hiện tại của Boost trong C++.
Lý thuyết: Boost là nơi thử nghiệm nhiều tính năng trước khi vào chuẩn C++ (
shared_ptr,function,threadđều bắt nguồn từ Boost) - nhiều phần đã "tốt nghiệp" vào STL, phần còn lại vẫn hữu ích.Thực hành: Liệt kê 5 tính năng Boost đã trở thành 1 phần của STL hiện đại (từ C++11 trở đi).
Ngày 561: Boost.Asio
Mục tiêu: Học thư viện async I/O quan trọng nhất của C++, liên hệ trực tiếp Giai đoạn 6.
Lý thuyết: Boost.Asio cung cấp abstraction cho async I/O trên nhiều platform (dùng epoll trên Linux, IOCP trên Windows bên dưới) - chính là nền tảng nhiều network library C++ khác dựa vào.
Thực hành: Đọc ví dụ Boost.Asio echo server, chỉ ra điểm tương đồng với mini event loop tự viết bằng epoll ở Giai đoạn 6 (Ngày 293).
Ngày 562: Lab - TCP Server Bất đồng bộ bằng Boost.Asio
Mục tiêu: Thực hành trực tiếp.
Lý thuyết: Ôn lại
io_context,async_read/async_write, callback-based programming.Thực hành: Viết TCP echo server bất đồng bộ bằng Boost.Asio, xử lý nhiều connection đồng thời trên 1 thread.
Ngày 563: Boost.Smart_ptr
Mục tiêu: Liên hệ lại RAII và memory management (Giai đoạn 0/1).
Lý thuyết: Boost.Smart_ptr là nguồn gốc của
std::shared_ptr/std::unique_ptrhiện đại - vẫn còn hữu ích cho vài trường hợp đặc biệt STL chưa hỗ trợ.Thực hành: So sánh API
boost::shared_ptrvớistd::shared_ptr, xác nhận chúng gần như giống hệt.
Ngày 564: Boost.Bind & Boost.Function
Mục tiêu: Hiểu C++ đã làm gì trước khi có lambda/
std::function(C++11).Lý thuyết: Trước C++11, Boost.Bind/Boost.Function là cách chính để làm functional programming trong C++ - hiểu lịch sử giúp đọc code C++ cũ dễ hơn.
Thực hành: Viết 1 ví dụ dùng
boost::bind+boost::function, viết lại bằng lambda hiện đại, so sánh.
Ngày 565: Boost.Serialization
Mục tiêu: Học thư viện serialize object, liên hệ lại Event Sourcing (Giai đoạn 8).
Lý thuyết: Boost.Serialization cho phép serialize/deserialize object C++ thành binary/XML/text.
Thực hành: Viết ví dụ serialize 1 struct đơn giản (tương tự Domain Event) ra file, đọc lại và verify dữ liệu khớp.
Ngày 566: Lab - Boost.Asio cho Plugin Framework
Mục tiêu: Chuẩn bị thành phần cho project cuối giai đoạn.
Lý thuyết: Ôn lại Plugin System (Giai đoạn 1) - giờ mở rộng để plugin có thể network-aware.
Thực hành: Thiết kế 1 phần Plugin Framework (sắp xây ở project cuối) có khả năng nhận request qua network dùng Boost.Asio.
Ngày 567: Review Boost
Mục tiêu: Chốt lại nhóm Boost.
Lý thuyết: Tổng hợp lại: Boost.Asio (network), Boost.Smart_ptr (memory), Boost.Bind/Function (functional), Boost.Serialization (persistence).
Thực hành: Viết tổng kết ngắn về vai trò Boost trong hệ sinh thái C++ hiện đại.
Envoy (Ngày 568–575)
Ngày 568: Envoy Proxy - Giới thiệu
Mục tiêu: Học 1 proxy C++ production-grade thực sự, đóng vai trò quan trọng trong service mesh.
Lý thuyết: Envoy là L7 proxy viết bằng C++, đóng vai trò sidecar trong kiến trúc microservices (liên hệ trực tiếp 3-service project Giai đoạn 8).
Thực hành: Đọc tài liệu giới thiệu Envoy, xác định vai trò nó có thể đóng trong 3-service project (routing, load balancing, observability).
Ngày 569: Envoy Architecture
Mục tiêu: Hiểu các khối kiến trúc chính.
Lý thuyết: Listener (nhận connection), Filter Chain (xử lý request qua nhiều filter, liên hệ Chain of Responsibility), Cluster (nhóm upstream service để route tới).
Thực hành: Vẽ sơ đồ Envoy Architecture, ánh xạ vào 3-service project (Order/Payment/Inventory là 3 cluster).
Ngày 570: Envoy - xDS API
Mục tiêu: Hiểu cách Envoy được cấu hình động.
Lý thuyết: xDS (discovery service) API cho phép cập nhật config Envoy (route, cluster, endpoint) mà không cần restart - nền tảng cho service mesh động như Istio.
Thực hành: Đọc tổng quan về CDS/EDS/RDS/LDS (4 loại discovery service chính), tóm tắt vai trò từng loại.
Ngày 571: Envoy Filter
Mục tiêu: Hiểu cách mở rộng hành vi Envoy.
Lý thuyết: Filter xử lý request/response qua Filter Chain - tương tự Middleware/Interceptor đã học nhiều lần, filter có thể viết bằng C++ (native) hoặc WASM.
Thực hành: Đọc source 1 filter đơn giản có sẵn trong Envoy (VD:
routerfilter), tóm tắt logic chính.
Ngày 572: Đọc Source Code Envoy - 1 Filter đơn giản
Mục tiêu: Luyện đọc source production-grade thực sự (phức tạp hơn nhiều so với ví dụ học tập).
Lý thuyết: Ôn lại phương pháp đọc source (Ngày 457) - tập trung vào interface chính trước khi đọc chi tiết implementation.
Thực hành: Chọn 1 filter nhỏ trong repo Envoy, đọc và ghi chú lại luồng xử lý chính (không cần hiểu 100%).
Ngày 573: Lab - Cấu hình Envoy cho 3-Service Project
Mục tiêu: Áp dụng Envoy thực tế vào project đã xây từ Giai đoạn 8.
Lý thuyết: Ôn lại 3 service (Order/Payment/Inventory) - dùng Envoy làm proxy phía trước.
Thực hành: Viết file cấu hình Envoy (YAML) định nghĩa listener + route tới 3 service, chạy Envoy làm gateway phía trước 3-service project.
Ngày 574: Envoy - Observability tích hợp sẵn
Mục tiêu: Thấy trước lợi ích sẽ đào sâu ở Giai đoạn 13.
Lý thuyết: Envoy tự sinh metrics (request count, latency, error rate) cho mọi traffic đi qua mà không cần sửa code service - lợi ích lớn của kiến trúc sidecar/service mesh.
Thực hành: Bật Envoy admin interface, quan sát metrics tự động sinh ra khi gọi request qua 3-service project.
Ngày 575: Review Envoy
Mục tiêu: Chốt lại nhóm Envoy.
Lý thuyết: Tổng hợp: Listener/Filter Chain/Cluster, xDS API, lợi ích observability tự động.
Thực hành: Viết nhận xét: Envoy giải quyết vấn đề gì mà tự viết Router/Middleware (Giai đoạn 3) không giải quyết được ở quy mô nhiều service.
Project: Plugin Framework (Ngày 576–581)
Ngày 576: Thiết kế Plugin Framework bằng C++
Mục tiêu: Kết hợp toàn bộ kiến thức C++ Track vào 1 project cuối cùng.
Lý thuyết: Ôn lại Plugin System đã xây ở Giai đoạn 1 (Java/Python) - giờ xây phiên bản C++ dùng
dlopenđể load plugin động (.sofile).Thực hành: Vẽ sơ đồ kiến trúc Plugin Framework C++: Plugin interface, Plugin Loader (dlopen), Plugin network-aware (Boost.Asio).
Ngày 577: Implement Plugin Interface & Dynamic Loading
Mục tiêu: Xây thành phần lõi.
Lý thuyết: Ôn lại
dlopen/dlsym/dlclose- cách C++ load shared library lúc runtime.Thực hành: Cài đặt
Plugininterface (abstract class vớivirtualmethod),PluginLoaderdùngdlopenđể load.sofile và tạo instance qua factory function xuất khẩu (extern "C").
Ngày 578: Implement Plugin Lifecycle
Mục tiêu: Áp dụng lại lifecycle đã học (Giai đoạn 1) cho phiên bản C++.
Lý thuyết: Ôn lại init/start/stop lifecycle, giờ cần đảm bảo cleanup đúng cách qua RAII khi unload plugin.
Thực hành: Thêm lifecycle method vào
Plugininterface, đảm bảoPluginLoadergọi đúng thứ tự init → start → (chạy) → stop → dlclose.
Ngày 579: Tích hợp Boost.Asio cho Plugin Network-aware
Mục tiêu: Hoàn thiện phần đã thiết kế ở Ngày 566.
Lý thuyết: Ôn lại cách Boost.Asio xử lý async I/O.
Thực hành: Viết 1 plugin mẫu nhận request qua TCP (dùng Boost.Asio), xử lý và trả response - chạy được trong Plugin Framework vừa xây.
Ngày 580: Test Plugin Framework
Mục tiêu: Đảm bảo framework đáng tin cậy.
Lý thuyết: Ôn lại nguyên tắc test framework code (Ngày 506 - tương tự Mini Spring).
Thực hành: Viết test cho
PluginLoader: load plugin thành công, load plugin lỗi (file không tồn tại), unload đúng thứ tự.
Ngày 581: Retrospective Giai đoạn 10
Mục tiêu: Tổng kết toàn bộ giai đoạn dài nhất trong roadmap (126 ngày, 3 track).
Lý thuyết: Nhìn lại toàn bộ hành trình: Spring (Java) → FastAPI/SQLAlchemy/Django (Python) → gRPC/Poco/Boost/Envoy (C++) - mỗi track đều quay lại dùng những khái niệm nền tảng đã học từ Giai đoạn 0 tới 9.
Thực hành: Viết báo cáo tổng kết lớn: với mỗi track, liệt kê 3 "khoảnh khắc a-ha" (lúc thấy 1 khái niệm cũ tái xuất hiện trong framework thật), và tự đánh giá track nào bạn tự tin nhất sau 126 ngày này.
Giai đoạn 11 - Database Internals (Ngày 582–644)
Đây là thứ framework engineer phải hiểu - mọi ORM đã học ở Giai đoạn 10 chỉ là lớp vỏ bọc quanh những cơ chế sẽ học ở đây.
Nhóm 1: Giới thiệu (Ngày 582–583)
Ngày 582: Vì sao cần hiểu Database Internals
Mục tiêu: Thấy rõ khoảng cách giữa "biết dùng SQL" và "hiểu database hoạt động thế nào".
Lý thuyết: N+1 problem, transaction isolation, index performance - mọi vấn đề đã gặp ở Giai đoạn 10 (Spring Data/SQLAlchemy) đều bắt nguồn từ cơ chế bên dưới sẽ học trong giai đoạn này.
Thực hành: Liệt kê 3 câu hỏi về hiệu năng database bạn từng gặp nhưng chưa thực sự hiểu nguyên nhân gốc rễ (VD: "tại sao query này chậm dù đã có index").
Ngày 583: Kiến trúc tổng quan 1 Database Engine
Mục tiêu: Có bức tranh tổng thể trước khi đi sâu từng thành phần.
Lý thuyết: Query Processor (parse, optimize, execute) → Transaction Manager (đảm bảo ACID) → Storage Engine (Buffer Pool, Page, WAL) → Disk.
Thực hành: Vẽ sơ đồ kiến trúc tổng quan, đánh dấu 3 nhóm sẽ học sâu trong giai đoạn này (Storage Engine, Index, Transaction).
Nhóm 2: Storage Engine (Ngày 584–593)
Ngày 584: Page - Đơn vị lưu trữ cơ bản
Mục tiêu: Hiểu đơn vị nhỏ nhất database đọc/ghi từ disk.
Lý thuyết: Page (thường 4KB/8KB/16KB) là đơn vị I/O cơ bản - database không đọc từng row riêng lẻ mà đọc cả page chứa nó, liên hệ lại khái niệm page ở tầng OS (Giai đoạn 0).
Thực hành: Vẽ sơ đồ minh hoạ: 1 page 8KB chứa nhiều row của bảng
orders, giải thích vì sao đọc 1 row luôn kéo theo đọc cả page.
Ngày 585: Page Layout
Mục tiêu: Hiểu cấu trúc bên trong 1 page.
Lý thuyết: Slot array (mảng con trỏ trỏ tới vị trí từng row trong page), tuple storage, cách xử lý khi row bị xoá (tombstone) hoặc row có kích thước thay đổi.
Thực hành: Thiết kế layout đơn giản cho 1 page: header + slot array + data area, tính toán có thể chứa bao nhiêu row cỡ 100 byte trong 1 page 8KB.
Ngày 586: Buffer Pool
Mục tiêu: Hiểu cơ chế cache page trong memory.
Lý thuyết: Buffer Pool giữ các page đã đọc trong RAM để tránh I/O disk lặp lại - liên hệ trực tiếp Cache đã học ở Giai đoạn 0 nhưng ở tầng ứng dụng thay vì tầng CPU.
Thực hành: Vẽ sơ đồ luồng: query cần page X → kiểm tra Buffer Pool → nếu miss thì đọc từ disk và nạp vào pool.
Ngày 587: Buffer Pool - Replacement Policy
Mục tiêu: Hiểu cách chọn page nào bị đuổi khi Buffer Pool đầy.
Lý thuyết: LRU (Least Recently Used), Clock algorithm (xấp xỉ LRU với chi phí thấp hơn) - trade-off giữa độ chính xác và overhead.
Thực hành: Mô phỏng thủ công LRU trên giấy với 1 chuỗi truy cập page cho trước, xác định page nào bị evict ở mỗi bước.
Ngày 588: Buffer Pool - Dirty Page & Flush
Mục tiêu: Hiểu cách xử lý page đã bị sửa trong memory nhưng chưa ghi xuống disk.
Lý thuyết: Dirty page (page đã sửa trong Buffer Pool nhưng chưa flush) - không thể evict dirty page mà không flush trước, nếu không sẽ mất dữ liệu.
Thực hành: Vẽ sơ đồ minh hoạ tình huống dirty page bị evict sai cách gây mất dữ liệu, và cách flush đúng trước khi evict.
Ngày 589: Write-Ahead Log (WAL)
Mục tiêu: Hiểu cơ chế đảm bảo Durability (1 trong 4 tính chất ACID sẽ học sâu sau).
Lý thuyết: WAL - mọi thay đổi phải được ghi vào log (tuần tự, nhanh) TRƯỚC khi ghi vào page thật (ngẫu nhiên, chậm hơn) - đảm bảo không mất dữ liệu dù crash giữa chừng.
Thực hành: Vẽ sơ đồ minh hoạ: tại sao ghi WAL trước rồi mới ghi page giúp phục hồi đúng dữ liệu sau crash, còn ghi ngược lại thì không.
Ngày 590: WAL - Redo Log & Recovery
Mục tiêu: Hiểu cách WAL được dùng để phục hồi sau crash.
Lý thuyết: Sau crash, database replay WAL từ điểm gần nhất đã biết chắc đã ghi xuống disk (redo), đưa hệ thống về đúng trạng thái trước khi crash.
Thực hành: Mô phỏng: có 5 thao tác ghi vào WAL, giả lập crash ở thao tác thứ 3 (page chưa flush), viết lại quy trình phục hồi khi khởi động lại.
Ngày 591: Checkpoint
Mục tiêu: Hiểu cách tối ưu thời gian recovery.
Lý thuyết: Checkpoint đánh dấu 1 điểm mà mọi dirty page trước đó đã được flush xuống disk - recovery chỉ cần replay WAL từ checkpoint gần nhất, không cần replay từ đầu.
Thực hành: Vẽ sơ đồ timeline minh hoạ: không có checkpoint (phải replay toàn bộ WAL) vs có checkpoint định kỳ (chỉ replay từ checkpoint gần nhất).
Ngày 592: Lab - Implement Page + Buffer Pool
Mục tiêu: Thực hành hoá toàn bộ lý thuyết tuần này.
Lý thuyết: Ôn lại Page Layout, Buffer Pool, Replacement Policy.
Thực hành: Cài đặt
Page(fixed-size byte array) vàBufferPool(LRU cache của Page) bằng C++, hỗ trợgetPage(id)/markDirty(id)/flush().
Ngày 593: Review Storage Engine
Mục tiêu: Củng cố toàn bộ nhóm.
Lý thuyết: Tổng hợp lại: Page → Buffer Pool → WAL → Checkpoint, đây chính là nền tảng cho Durability trong ACID.
Thực hành: Vẽ sơ đồ tổng thể luồng ghi dữ liệu: Write request → WAL → Buffer Pool (dirty page) → Flush → Disk.
Nhóm 3: Index (Ngày 594–605)
Ngày 594: Index - Giới thiệu
Mục tiêu: Hiểu vấn đề index giải quyết và cái giá phải trả.
Lý thuyết: Không có index, tìm 1 row phải quét toàn bộ bảng (full table scan, O(n)) - index đánh đổi thêm dung lượng lưu trữ và chi phí ghi (phải cập nhật index khi insert/update) để đổi lấy tốc độ đọc nhanh hơn.
Thực hành: Với bảng
orders1 triệu row, ước tính thời gian full table scan so với tra cứu qua index (giả định), giải thích tại sao chênh lệch lớn.
Ngày 595: B-Tree - Cấu trúc cơ bản
Mục tiêu: Hiểu cấu trúc cây cân bằng dùng phổ biến nhất cho index.
Lý thuyết: B-Tree là cây cân bằng đa nhánh (không chỉ 2 nhánh như BST đã học Giai đoạn 0), mỗi node chứa nhiều key, độ sâu cây thấp hơn nhiều so với BST nhị phân với cùng số lượng dữ liệu.
Thực hành: So sánh độ sâu cây cần thiết để lưu 1 triệu key giữa BST nhị phân (Giai đoạn 0) và B-Tree bậc 100, giải thích ý nghĩa với số lần đọc disk.
Ngày 596: B-Tree - Insert & Split
Mục tiêu: Hiểu cách B-Tree tự cân bằng khi thêm dữ liệu.
Lý thuyết: Khi 1 node đầy, nó split thành 2 node, đẩy 1 key lên node cha - quá trình có thể lan truyền tới tận root.
Thực hành: Mô phỏng thủ công insert 5 key liên tiếp vào 1 B-Tree bậc nhỏ (VD: bậc 3), vẽ lại cây sau mỗi lần insert gây split.
Ngày 597: B-Tree - Delete & Merge
Mục tiêu: Hiểu chiều ngược lại của split.
Lý thuyết: Khi xoá key làm node có quá ít key (dưới ngưỡng tối thiểu), node cần merge với node anh em hoặc mượn key từ node anh em.
Thực hành: Mô phỏng thủ công xoá 2 key từ cây đã xây ở Ngày 596, vẽ lại cây sau khi merge/rebalance.
Ngày 598: B+Tree - Khác biệt với B-Tree
Mục tiêu: Hiểu biến thể được hầu hết database dùng thực tế.
Lý thuyết: B+Tree chỉ lưu dữ liệu thực ở leaf node (internal node chỉ lưu key để định tuyến), khác B-Tree lưu dữ liệu ở mọi node - giúp internal node nhỏ gọn hơn, fit nhiều key hơn trong 1 page.
Thực hành: Vẽ sơ đồ so sánh B-Tree vs B+Tree cho cùng 1 tập dữ liệu, chỉ ra khác biệt ở vị trí lưu data.
Ngày 599: B+Tree - Leaf Node Linked List & Range Query
Mục tiêu: Hiểu lý do chính khiến B+Tree được chọn cho database.
Lý thuyết: Các leaf node của B+Tree được nối với nhau bằng linked list - range query (
WHERE age BETWEEN 20 AND 30) chỉ cần tìm điểm bắt đầu rồi duyệt tuần tự qua linked list, không cần tìm kiếm lại từ root cho mỗi giá trị.Thực hành: Vẽ sơ đồ minh hoạ range query chạy qua B+Tree leaf linked list, so sánh chi phí với việc phải tìm kiếm riêng lẻ từng giá trị trong khoảng.
Ngày 600: Lab - Implement B+Tree
Mục tiêu: Tự tay cài đặt cấu trúc dữ liệu quan trọng nhất của database.
Lý thuyết: Ôn lại toàn bộ Ngày 595-599.
Thực hành: Cài đặt
BPlusTree<K,V>bằng C++ vớiinsert()/search()/rangeQuery(start, end), test với vài nghìn key.
Ngày 601: LSM Tree - Giới thiệu
Mục tiêu: Học cấu trúc index thay thế hoàn toàn khác triết lý.
Lý thuyết: Log-Structured Merge Tree - tối ưu cho write-heavy workload bằng cách biến random write thành sequential write (ghi log thay vì update tại chỗ), đánh đổi bằng việc đọc phức tạp hơn.
Thực hành: Vẽ sơ đồ so sánh triết lý: B+Tree (update tại chỗ, tốt cho đọc) vs LSM Tree (append-only, tốt cho ghi).
Ngày 602: LSM Tree - Memtable & SSTable
Mục tiêu: Hiểu 2 thành phần chính của LSM Tree.
Lý thuyết: Memtable (cấu trúc trong memory, thường là balanced tree, nhận write mới) → khi đầy, flush thành SSTable (Sorted String Table, file bất biến trên disk, đã sắp xếp theo key).
Thực hành: Vẽ sơ đồ luồng: write mới → Memtable → Memtable đầy → flush thành SSTable mới trên disk.
Ngày 603: LSM Tree - Compaction
Mục tiêu: Hiểu cách LSM Tree dọn dẹp các SSTable tích luỹ theo thời gian.
Lý thuyết: Nhiều SSTable chồng chất theo thời gian (bao gồm cả version cũ/đã xoá của cùng 1 key) - compaction định kỳ merge nhiều SSTable thành 1, loại bỏ dữ liệu cũ/tombstone.
Thực hành: Vẽ sơ đồ minh hoạ compaction merge 3 SSTable (có key trùng nhau, version khác nhau) thành 1 SSTable sạch.
Ngày 604: So sánh B+Tree vs LSM Tree
Mục tiêu: Có tiêu chí chọn lựa cho hệ thống thực tế.
Lý thuyết: B+Tree phù hợp read-heavy (PostgreSQL, MySQL InnoDB); LSM Tree phù hợp write-heavy (RocksDB, Cassandra, LevelDB) - đánh đổi giữa read amplification và write amplification.
Thực hành: Lập bảng so sánh theo tiêu chí: tốc độ ghi, tốc độ đọc, độ phức tạp implementation, use case điển hình.
Ngày 605: Review Index
Mục tiêu: Tổng hợp nhóm Index.
Lý thuyết: Ôn lại toàn bộ: từ vấn đề Index giải quyết, tới 2 cấu trúc chính (B+Tree, LSM Tree) và trade-off giữa chúng.
Thực hành: Với database bạn dùng hàng ngày (VD: PostgreSQL), xác định nó dùng B+Tree hay LSM Tree cho index mặc định, giải thích tại sao phù hợp với use case phổ biến của nó.
Nhóm 4: Transaction (Ngày 606–619)
Ngày 606: ACID - Atomicity
Mục tiêu: Hiểu tính chất đầu tiên trong ACID.
Lý thuyết: Atomicity đảm bảo 1 transaction hoặc thực hiện toàn bộ, hoặc không thực hiện gì cả - không có trạng thái "nửa vời".
Thực hành: Viết ví dụ transaction chuyển tiền (trừ tài khoản A, cộng tài khoản B) minh hoạ vì sao thiếu Atomicity gây mất tiền nếu crash giữa 2 bước.
Ngày 607: ACID - Consistency
Mục tiêu: Hiểu tính chất thứ 2, dễ nhầm lẫn nhất.
Lý thuyết: Consistency đảm bảo transaction luôn đưa database từ 1 trạng thái hợp lệ (thoả mọi constraint) sang 1 trạng thái hợp lệ khác - liên hệ lại Invariant đã học ở Encapsulation (Giai đoạn 1) và Aggregate (Giai đoạn 7).
Thực hành: Xác định 1 constraint database (VD:
balance >= 0) và giải thích Consistency đảm bảo điều gì khi transaction vi phạm constraint đó.
Ngày 608: ACID - Isolation
Mục tiêu: Hiểu tính chất phức tạp nhất, sẽ đào sâu riêng ở nhóm tiếp theo.
Lý thuyết: Isolation đảm bảo nhiều transaction chạy đồng thời không ảnh hưởng lẫn nhau theo cách không mong muốn - mức độ "cô lập" có thể điều chỉnh (sẽ học ở Ngày 610-611).
Thực hành: Vẽ sơ đồ 2 transaction chạy đồng thời cùng đọc/sửa 1 row, minh hoạ vấn đề nếu không có Isolation.
Ngày 609: ACID - Durability
Mục tiêu: Liên hệ lại WAL đã học ở Nhóm 2.
Lý thuyết: Durability đảm bảo 1 khi transaction đã commit, dữ liệu tồn tại vĩnh viễn dù có crash ngay sau đó - chính là lý do WAL tồn tại.
Thực hành: Giải thích lại mối liên hệ trực tiếp giữa WAL (Ngày 589-590) và Durability.
Ngày 610: Isolation Level - Read Uncommitted & Read Committed
Mục tiêu: Học 2 mức độ isolation thấp nhất.
Lý thuyết: Read Uncommitted (đọc được cả dữ liệu chưa commit của transaction khác - dirty read), Read Committed (chỉ đọc dữ liệu đã commit, nhưng vẫn có thể đọc khác nhau giữa 2 lần đọc trong cùng transaction).
Thực hành: Vẽ sơ đồ minh hoạ dirty read xảy ra ở Read Uncommitted nhưng không xảy ra ở Read Committed.
Ngày 611: Isolation Level - Repeatable Read & Serializable
Mục tiêu: Học 2 mức độ isolation cao nhất.
Lý thuyết: Repeatable Read (đảm bảo đọc lại cùng row trong 1 transaction luôn ra cùng giá trị, nhưng vẫn có thể có phantom read), Serializable (mức cao nhất, tương đương chạy tuần tự từng transaction).
Thực hành: Lập bảng tổng hợp 4 isolation level theo chuẩn SQL, chỉ rõ mỗi level ngăn được hiện tượng nào (sẽ định nghĩa chi tiết ở Ngày 612).
Ngày 612: Các hiện tượng - Dirty Read, Non-repeatable Read, Phantom Read
Mục tiêu: Định nghĩa chính xác 3 hiện tượng hay bị nhầm lẫn.
Lý thuyết: Dirty Read (đọc dữ liệu chưa commit), Non-repeatable Read (đọc lại row cùng ID nhưng giá trị khác do transaction khác đã UPDATE và commit), Phantom Read (đọc lại cùng điều kiện WHERE nhưng số lượng row khác do transaction khác đã INSERT/DELETE).
Thực hành: Viết 3 ví dụ SQL minh hoạ rõ ràng cho từng hiện tượng, chạy thử trên 1 database thật với isolation level tương ứng.
Ngày 613: MVCC - Giới thiệu
Mục tiêu: Hiểu cơ chế hiện đại giải quyết Isolation mà không cần lock nặng nề.
Lý thuyết: Multi-Version Concurrency Control - thay vì lock để ngăn đọc/ghi xung đột, mỗi transaction thấy 1 "snapshot" riêng của dữ liệu tại thời điểm bắt đầu, cho phép đọc và ghi không chặn nhau.
Thực hành: Vẽ sơ đồ minh hoạ: transaction A đang sửa row X, transaction B vẫn đọc được version cũ của row X mà không cần đợi A commit.
Ngày 614: MVCC - Implementation (Version Chain)
Mục tiêu: Hiểu cách MVCC thực sự lưu trữ nhiều version.
Lý thuyết: Mỗi row có thêm metadata (transaction ID tạo ra nó, transaction ID xoá nó nếu có) - nhiều version của cùng 1 row logic tạo thành 1 chuỗi (version chain), transaction chọn version phù hợp với snapshot của nó.
Thực hành: Vẽ sơ đồ version chain cho 1 row bị UPDATE 3 lần bởi 3 transaction khác nhau, xác định transaction thứ 4 (bắt đầu sau lần UPDATE thứ 2) sẽ thấy version nào.
Ngày 615: Locking - Shared Lock vs Exclusive Lock
Mục tiêu: Hiểu cơ chế truyền thống, vẫn cần dùng song song với MVCC cho việc ghi.
Lý thuyết: Shared Lock (nhiều transaction có thể đọc cùng lúc), Exclusive Lock (chỉ 1 transaction được ghi, chặn mọi lock khác) - liên hệ lại
mutex(Shared/Exclusive tương tự Reader-Writer Lock đã gặp ở Giai đoạn 5).Thực hành: Lập bảng compatibility matrix: Shared-Shared (OK), Shared-Exclusive (chặn), Exclusive-Exclusive (chặn).
Ngày 616: 2-Phase Locking (2PL)
Mục tiêu: Hiểu giao thức đảm bảo Serializable bằng locking.
Lý thuyết: 2PL có 2 giai đoạn: Growing (chỉ được xin thêm lock, không được nhả), Shrinking (chỉ được nhả lock, không được xin thêm) - đảm bảo Serializable nhưng tăng nguy cơ deadlock.
Thực hành: Vẽ sơ đồ minh hoạ 1 transaction tuân thủ 2PL đúng cách (xin hết lock cần thiết trước, nhả hết sau khi commit).
Ngày 617: Deadlock trong Database
Mục tiêu: Liên hệ lại Deadlock đã học ở Giai đoạn 5, giờ ở ngữ cảnh database.
Lý thuyết: Deadlock giữa các transaction xảy ra tương tự deadlock giữa thread (Giai đoạn 5) - database dùng deadlock detection (wait-for graph) để phát hiện và chủ động abort 1 transaction (victim) để phá vỡ deadlock.
Thực hành: Vẽ wait-for graph cho 2 transaction đang deadlock (A đợi lock B giữ, B đợi lock A giữ), giải thích cách database phát hiện chu trình trong graph này.
Ngày 618: Lab - Implement MVCC đơn giản
Mục tiêu: Thực hành hoá cơ chế đã học.
Lý thuyết: Ôn lại version chain (Ngày 614).
Thực hành: Cài đặt 1 key-value store đơn giản với MVCC: mỗi key lưu list các version (kèm transaction ID), hỗ trợ
read(key, txnId)trả về version phù hợp với snapshot của transaction đó.
Ngày 619: Review Transaction
Mục tiêu: Tổng hợp toàn bộ nhóm Transaction - nhóm dài và quan trọng nhất giai đoạn này.
Lý thuyết: Tổng hợp: ACID → Isolation Level → Phenomena → MVCC → Locking → 2PL → Deadlock.
Thực hành: Viết bài tổng kết: giải thích cho 1 người không rành kỹ thuật hiểu vì sao "transaction" trong database phức tạp hơn nhiều so với chỉ là "1 khối lệnh SQL chạy cùng nhau".
Nhóm 5: Replication (Ngày 620–627)
Ngày 620: Replication - Giới thiệu
Mục tiêu: Hiểu vấn đề replication giải quyết.
Lý thuyết: Replication sao chép dữ liệu sang nhiều node để tăng khả năng chịu lỗi (node chết vẫn còn bản sao) và tăng khả năng đọc (phân tải đọc ra nhiều node).
Thực hành: Vẽ sơ đồ minh hoạ vấn đề: 1 database duy nhất không có replication, nếu node đó chết thì toàn bộ hệ thống dừng hoạt động.
Ngày 621: Leader-Follower Replication
Mục tiêu: Học mô hình replication phổ biến nhất.
Lý thuyết: 1 Leader (Primary) nhận mọi write, nhiều Follower (Replica) nhận bản sao dữ liệu từ Leader và phục vụ read - đơn giản, dễ đảm bảo consistency hơn multi-leader.
Thực hành: Vẽ sơ đồ Leader-Follower với 1 Leader và 2 Follower, minh hoạ luồng write đi từ client → Leader → replicate sang Follower.
Ngày 622: Synchronous vs Asynchronous Replication
Mục tiêu: Hiểu trade-off giữa độ bền dữ liệu và độ trễ.
Lý thuyết: Synchronous (Leader đợi Follower xác nhận đã nhận trước khi báo commit thành công - an toàn hơn nhưng chậm hơn) vs Asynchronous (Leader báo commit ngay, không đợi Follower - nhanh hơn nhưng có nguy cơ mất dữ liệu nếu Leader chết trước khi Follower kịp nhận).
Thực hành: Lập bảng so sánh 2 cách theo tiêu chí: độ trễ ghi, rủi ro mất dữ liệu khi Leader crash.
Ngày 623: Replication Lag
Mục tiêu: Hiểu vấn đề thực tế khi dùng Asynchronous Replication.
Lý thuyết: Replication Lag - khoảng thời gian Follower "theo sau" Leader - gây ra vấn đề đọc dữ liệu cũ nếu đọc từ Follower ngay sau khi ghi vào Leader (liên hệ Eventual Consistency đã học ở CQRS, Giai đoạn 8).
Thực hành: Viết ví dụ minh hoạ: user cập nhật profile (ghi vào Leader) rồi load lại trang ngay (đọc từ Follower có lag), thấy dữ liệu cũ - đề xuất giải pháp (đọc từ Leader cho chính user vừa ghi, "read-your-writes consistency").
Ngày 624: Failover
Mục tiêu: Hiểu cách hệ thống tự phục hồi khi Leader chết.
Lý thuyết: Failover - khi Leader chết, hệ thống cần bầu 1 Follower mới làm Leader (leader election) - cần cơ chế phát hiện Leader chết (heartbeat/timeout) và tránh split-brain (2 node cùng nghĩ mình là Leader).
Thực hành: Vẽ sơ đồ tuần tự quy trình failover: phát hiện Leader chết → bầu Follower mới → cập nhật client trỏ tới Leader mới.
Ngày 625: Multi-leader Replication
Mục tiêu: Học mô hình phức tạp hơn, dùng cho use case đặc thù.
Lý thuyết: Multi-leader - nhiều node đều có thể nhận write (VD: multi-datacenter, mỗi datacenter có 1 leader riêng) - phải xử lý conflict khi 2 leader cùng sửa 1 dữ liệu (conflict resolution: last-write-wins, merge, hoặc application-level).
Thực hành: Vẽ ví dụ conflict: 2 leader ở 2 vùng địa lý cùng sửa 1 record gần như đồng thời, đề xuất 1 chiến lược resolve conflict.
Ngày 626: Lab - Thiết kế Replication cho Mini KV Store
Mục tiêu: Chuẩn bị thiết kế cho project cuối giai đoạn.
Lý thuyết: Ôn lại Leader-Follower - mô hình phù hợp nhất để implement trong phạm vi thời gian hợp lý.
Thực hành: Thiết kế đầy đủ cơ chế replication cho Mini KV Store sắp xây: Leader nhận write, replicate WAL entry sang Follower qua network.
Ngày 627: Review Replication
Mục tiêu: Tổng hợp nhóm Replication.
Lý thuyết: Tổng hợp: Leader-Follower, Sync/Async, Replication Lag, Failover, Multi-leader.
Thực hành: Liên hệ lại Saga/Eventual Consistency (Giai đoạn 8) - viết nhận xét về mối liên hệ giữa Replication Lag và khái niệm Eventual Consistency đã học trước đó.
Nhóm 6: Project - Mini KV Store (Ngày 628–644)
Ngày 628: Thiết kế kiến trúc Mini KV Store
Mục tiêu: Lên thiết kế tổng thể trước khi code, tổng hợp toàn bộ giai đoạn.
Lý thuyết: Ôn lại toàn bộ: Storage Engine (Page/Buffer Pool/WAL), Index (B+Tree), Transaction (MVCC), Replication (Leader-Follower).
Thực hành: Vẽ sơ đồ kiến trúc đầy đủ Mini KV Store, xác định thứ tự implement hợp lý (thường: Storage trước, Index sau, Transaction sau cùng).
Ngày 629: Implement Page + Buffer Pool
Mục tiêu: Ghép lại thành phần đã xây từ Ngày 592 vào project chính thức.
Lý thuyết: Ôn lại
Page/BufferPoolđã cài đặt.Thực hành: Tích hợp
Page/BufferPoolvào codebase Mini KV Store, viết interfaceStorageManagerbọc quanh chúng.
Ngày 630: Implement WAL
Mục tiêu: Đảm bảo Durability cho Mini KV Store.
Lý thuyết: Ôn lại Ngày 589-591.
Thực hành: Cài đặt
WriteAheadLogvớiappend(entry)/replay(), mọi write phải qua WAL trước khi cập nhật Buffer Pool.
Ngày 631: Implement B+Tree Index
Mục tiêu: Tích hợp cấu trúc index vào project.
Lý thuyết: Ôn lại
BPlusTreeđã cài Ngày 600.Thực hành: Tích hợp B+Tree vào Mini KV Store, dùng nó làm cấu trúc chính lưu mapping key → vị trí data trong Page.
Ngày 632: Implement Get/Put/Delete API
Mục tiêu: Có API cơ bản hoạt động end-to-end.
Lý thuyết: Ôn lại toàn bộ luồng: Get (tra B+Tree → đọc Page qua Buffer Pool), Put (ghi WAL → cập nhật B+Tree → đánh dấu Page dirty).
Thực hành: Cài đặt đầy đủ
get(key)/put(key, value)/delete(key), viết test cơ bản verify hoạt động đúng.
Ngày 633: Implement Transaction - Atomicity qua WAL
Mục tiêu: Hỗ trợ transaction đa thao tác.
Lý thuyết: Ôn lại Ngày 606 - nhóm nhiều thao tác Put/Delete thành 1 transaction, WAL ghi rõ ranh giới BEGIN/COMMIT/ABORT.
Thực hành: Thêm API
beginTransaction()/commit()/abort(), đảm bảo nếu crash giữa transaction chưa commit, khi replay WAL sẽ bỏ qua toàn bộ thao tác của transaction đó (Atomicity).
Ngày 634: Implement MVCC
Mục tiêu: Hỗ trợ đọc/ghi đồng thời không chặn nhau.
Lý thuyết: Ôn lại version chain đã cài Ngày 618.
Thực hành: Tích hợp MVCC vào Mini KV Store: mỗi
put()tạo version mới thay vì ghi đè,get()trả về version phù hợp với snapshot của transaction gọi nó.
Ngày 635: Implement Isolation Level
Mục tiêu: Cho phép Mini KV Store hỗ trợ ít nhất 1 isolation level rõ ràng.
Lý thuyết: Ôn lại Read Committed (Ngày 610) - mức độ hợp lý để implement trong phạm vi project này.
Thực hành: Đảm bảo mỗi lần
get()trong 1 transaction chỉ thấy dữ liệu đã commit tại thời điểm đọc (Read Committed), viết test verify không có dirty read.
Ngày 636: Implement Range Query
Mục tiêu: Tận dụng lợi thế của B+Tree.
Lý thuyết: Ôn lại leaf node linked list (Ngày 599).
Thực hành: Cài đặt
rangeQuery(startKey, endKey)trả về toàn bộ key-value trong khoảng, tận dụng linked list ở leaf node.
Ngày 637: Implement Crash Recovery
Mục tiêu: Verify toàn bộ hệ thống chịu được crash.
Lý thuyết: Ôn lại quy trình replay WAL (Ngày 590).
Thực hành: Cài đặt logic khởi động: đọc WAL, replay mọi transaction đã commit, bỏ qua transaction chưa commit, đưa Buffer Pool về đúng trạng thái.
Ngày 638: Implement Checkpoint
Mục tiêu: Tối ưu thời gian recovery.
Lý thuyết: Ôn lại Ngày 591.
Thực hành: Cài đặt checkpoint định kỳ (flush toàn bộ dirty page + ghi checkpoint marker vào WAL), sửa logic recovery để chỉ replay từ checkpoint gần nhất.
Ngày 639: Implement Replication
Mục tiêu: Thêm khả năng chịu lỗi ở tầng node.
Lý thuyết: Ôn lại thiết kế Ngày 626.
Thực hành: Cài đặt Leader gửi WAL entry qua network cho 1 Follower đơn giản, Follower replay WAL entry nhận được để đồng bộ dữ liệu.
Ngày 640: Test - Storage Layer
Mục tiêu: Verify tầng thấp nhất hoạt động đúng trước khi test tầng cao hơn.
Lý thuyết: Ôn lại Testing Strategy (Giai đoạn 4).
Thực hành: Viết test cho
Page/BufferPool/WALđộc lập với B+Tree/Transaction.
Ngày 641: Test - Transaction/MVCC
Mục tiêu: Verify tính đúng đắn của concurrency control.
Lý thuyết: Ôn lại kỹ thuật test concurrency (Giai đoạn 5): chạy nhiều lần, dùng nhiều thread đồng thời.
Thực hành: Viết test với nhiều transaction chạy đồng thời (dùng thread), verify không có dirty read, verify Atomicity khi có transaction bị abort giữa chừng.
Ngày 642: Test - Crash Recovery
Mục tiêu: Verify hệ thống thực sự sống sót qua crash, không chỉ lý thuyết.
Lý thuyết: Ôn lại quy trình recovery đã cài Ngày 637-638.
Thực hành: Viết test giả lập crash: ghi 1 số transaction,
kill -9process giữa chừng (hoặc mô phỏng bằng cách không gọi flush cuối cùng), khởi động lại, verify dữ liệu đúng như mong đợi (transaction đã commit còn, transaction chưa commit mất).
Ngày 643: Benchmark Mini KV Store
Mục tiêu: Đánh giá hiệu năng thực tế so với công cụ production.
Lý thuyết: Ôn lại benchmark methodology đã dùng nhiều lần trong roadmap (đo throughput, latency).
Thực hành: Benchmark Mini KV Store (read/write throughput) so với SQLite hoặc RocksDB cho cùng workload, phân tích chênh lệch và nguyên nhân (thiếu tối ưu nào).
Ngày 644: Retrospective Giai đoạn 11
Mục tiêu: Tổng kết toàn bộ Giai đoạn Database Internals.
Lý thuyết: Nhìn lại hành trình từ Page/Buffer Pool đơn giản tới 1 Mini KV Store có Transaction/MVCC/Replication - đây chính là những gì PostgreSQL/MySQL/RocksDB thực sự làm ở quy mô production.
Thực hành: Viết báo cáo retrospective: liên hệ ngược lại với Spring Data/SQLAlchemy (Giai đoạn 10) - giờ đã hiểu "dưới gầm" của
@Transactional/Session thực sự làm gì.