#186 - Ứng dụng của RocksDB vào hệ thống Manhattan của Twitter
Grokking Newsletter là newsletter hàng tuần của Grokking cho các bạn software engineers người Việt. Tuần này: so sánh các engine K8S, sử dụng RocksDB, lịch sử các web browsers, công cụ xây dựng database document, và nhiều thông tin hay khác.
Những bài viết hay
Comparison of the Kubernetes Engines
Ngày nay, có rất nhiều công ty sử dụng Kubernetes (k8s) để quản lý các containers trong môi trường micro-services. Có rất nhiều các lựa chọn để provision và quản lý các k8s cluster. Vì vậy, cần có sự nghiên cứu và chuẩn bị cẩn thận để có thể đưa ra lựa chọn phù hợp nhất.
Khi cài đặt k8s, chúng ta sẽ cần giải quyết các khía cạnh sau:
Provisioning Servers
TLS Management
Configuration management (k8s upgrade)
Scaling
Chúng ta có các tools được cung cấp bởi các cloud provider như: EKS, AKS, GKE, Kops. Trong môi trường cloud, có hai cách để thiết lập orchestration cho cụm k8s: quản lý bởi Control Plane layer cung cấp bởi Cloud Provider, hoặc bởi team Infra:
AWS EKS tương thích với các ứng dụng chạy trên bất kỳ nền kiến trúc Kubernetes vanilla nào, cung cấp một control plane cho mỗi tenant trên mỗi cluster. Control Planes không được chia sẻ giữa các cụm.
GKE cho phép thiết lập các ứng dụng và cung cấp các tính năng của k8s cluster nhanh chóng hơn các provider khác. GKE cung cấp các clusters với tính năng High Availability (HA) thông qua các regional clusters (master và workers được replicated).
AKS hỗ trợ bất kỳ kiến trúc Kubernetes vanilla nào, cũng hỗ trợ Apache Mesos và Docker Swarm. Các nhà phát triển Windows thường thích AKS hơn. AKS không hỗ trợ cung cấp HA cluster.
Với các cụm k8s được quản lý bởi team infra, Kops là một tool phổ biến giúp quản lý các k8s components như systemd services hay static pods. Chúng ta có thể sử dụng Kops để tạo khung cơ sở hạ tầng của mình nhằm quản lý các cluster theo as-code principles.
Ngoài ra, với môi trường On-Premise, chúng ta cũng có các lựa chọn như: Kubespray, Kubeadm RKE, K3S.
Adopting RocksDB within Manhattan
Manhattan là một hệ thống cơ sở dữ liệu key-value store được xây dựng ở nội bộ Twitter để hỗ trợ việc chứa dữ liệu realtime cho các chức năng như là Tweets, Users, và Direct Messages. Lúc mới xây dựng Manhattan, các đội ngũ kỹ sư ở Twitter đã thiết kế bộ phận storage engine của Manhattan với tiêu chí là có thể thay thế nhiều loại storage engine một cách dễ dàng. Do đó, hiện tại Manhattan có thể sử dụng được với 2 storage engines khác nhau: SSTable và MhBtree (một inhouse B-tree cho Manhattan).
Do các ứng dụng dùng Manhattan thường có lưu lượng đọc nhiều nên đa số các clusters ở Twitter sử dụng MhBtree. Mặc dù việc dùng B-tree tốt cho việc đọc nhưng vẫn có một vài hạn chế như là: việc nén dữ liệu không tốt (poor compression), việc chạy backups không hiệu quả do các hạn chế về copy-on-write trong cách triển khai của Manhattan, …
Từ những hạn chế trên, các đội ngũ kỹ sư ở Twitter đã bắt đầu thử nghiệm với RocksDB như là một storage engine mới của Manhattan từ năm 2017 do RocksDB có những benchmarks khá là ấn tượng lúc bấy giờ. Tuy nhiên, cũng như các hệ thống khác, việc tích hợp RocksDB với Manhattan không hề dễ dàng như mong đợi. Các đội ngũ kỹ sư ở Twitter phải tốn hết hơn 1 năm để tích hợp thành công RocksDB với Manhattan bằng việc trải qua nhiều thử thách trong implementations như là: việc debug các vấn đề về hiệu suất giữa cầu nối Manhattan với RocksDB qua JNI interface; sự khác biệt về encoding cho keys và values giữa RocksDB và Manhattan; … Ở bài viết sau đây, kỹ sư Vijay Teja Gottipati, một senior software engineer ở Twitter, sẽ giải thích cụ thể hơn cách họ đã tích hợp RocksDB với Manhattan như thế nào để các hệ thống ở Twitter có thể cải thiện hiệu suất tốt hơn trên nền tảng RocksDB.
Top 8 Benefits of Functional Programming
Functional Programming (lập trình hàm) là một phong cách lập trình mà application được xây dựng bằng việc áp dụng và kết hợp (compose) functions với nhau. Functional Programming đang dần hấp dẫn cộng đồng lập trình viên những năm gần đây bởi những lợi ích mà nó mang lại.
Functional programming giúp chúng ta chứng minh được những dòng code được viết ra chính xác bằng toán học. Function thường được định nghĩa như những biểu thức (expressions) chỉ phụ thuộc vào input và output, chứ không chỉ là những câu lệnh (statements) để thay đổi state của application.
Trong bài viết này, tác giả viết về một số định nghĩa về functional programming như: pure functions, immutability cũng như nêu lên 8 lợi ích mà chúng ta có thể thu được khi sử dụng style này khi coding hoặc chuyển hẳn sang một số ngôn ngữ thuần functional programming như Haskell, Scala, Closure.
Sau đây là 8 lợi ích đó:
Pure functions thì tốt hơn impure functions. Pure functions là functions có những đặc điểm sau: nó luôn trả về cùng một kết quả khi nhận cùng đối số đầu vào; nó không tạo ra side effects khi evaluation. Với những điều kiện đó pure functions luôn đơn giản và dễ hiểu hơn so với impure functions.
Pure functions dễ test hơn. Pure functions không bị phụ thuộc vào state bên ngoài, nên chỉ cần tập test input và output. Kết quả của tests luôn được xác định và ổn định.
Functional programming dẫn tới ít bug hơn. Với việc hạn chế mutable state, functional programming tránh được rất nhiều bug liên quan đến shared state, đặc biệt là khi làm việc với parallel/concurrency.
Functional code thường phân hoạch được trạng thái của ứng dụng, làm cho nó dễ hiểu hơn. Bằng việc sử dụng Elm Archiecture làm ví dụ tác giả giải thích tác dụng của functional programming trong việc module hoá code và lợi ích của nó.
Trong Functional Programming, khai báo của hàm đáng được tin cậy hơn. Trái ngược với các style programming khác, việc khai báo hàm trong functional programming luôn đáng tin cậy. Vì đầu ra của hàm luôn phụ thuộc vào đối số đầu vào mà không phụ thuộc vào bất kỳ state bên ngoài.
An toàn hơn khi làm việc với concurrency. Pure functions thì luôn luôn thread-safe. Do đó chúng ta có thể tránh được rất nhiều bug liên quan đến concurrency như: race-condition, death-lock.
Đơn giản hơn với đệ quy. Hàm đệ quy là hàm sử dụng chính bản thân nó trong quá trình thực thi. Việc sử dụng hàm đệ quy thì đơn giản hơn so với các kiểu vòng lặp như for loop bởi vì vòng lặp for yêu cầu mutable variable, kiểm tra điều kiện khởi tạo, kết thúc.
Biến bất biến (immutable variables) dẫn tới ít side-effects hơn. Với việc sử dụng immutable variable, chúng ta có thể xác định giá trị của mỗi biến ở bất kỳ thời gian nào, và tự tin rằng giá trị đó sẽ không bao giờ thay đổi. Do đó, chúng ta không phải theo dõi giá trị của biến khi đọc code, điều này dẫn tới code được viết ra sẽ đơn giản và dễ hiểu hơn.
Góc Distributed System
Tìm hiểu về Hybrid Logical Clock
https://martinfowler.com/articles/patterns-of-distributed-systems/hybrid-clock.html
Vấn đề đồng bộ thời gian trong các hệ thống phân tán thường được xử lý thông qua hai phương án: sử dụng đồng hồ vật lý đắt tiền (vd: atomic clock) giúp hạn chế tối đa tính sai khác thời gian giữa các server (clock skew), có thể thấy ví dụ trong Google Spanner với hệ thống TrueTime; hoặc sử dụng logical clock (vd: lamport clock, vector clock) để thể hiện thứ tự xảy ra của các events (1 số nguyên hoặc 1 vector) trong hệ thống với điều kiện: event a xảy ra trước event b thì C(a) < C(b) với C là đại lượng biểu diễn logical clock. Nếu không có sự hỗ trợ của đồng hồ vật lý đắt tiền, các hệ thống sử dụng Logical clock sẽ gặp phải 1 hạn chế là: client không biết được thời gian date-time cụ thể khi một event xảy ra.
Hybrid Logical Clock là giải pháp bổ sung cho vấn đề này. HLC cũng cung cấp đơn vị thời gian tăng dần như logical clock, nhưng có mối quan hệ mật thiết với thời gian vật lý (actual date-time). HLC được sử dụng rộng rãi trong các hệ thống distributed database, ví dụ có MongoDB, CockroachDB, YugabyteDB.
HLC thường được implement là một đối tượng có hai giá trị:
system time: thời gian vật lý trên local server.
counter (hoặc tick): số nguyên, tăng dần.
Một số thao tác cơ bản với HLC:
Khởi tạo: Khi khởi tạo giá trị cho 1 đối tượng HLC, counter thường được set là -1.
So sánh: khi so sánh this.HLC với other.HLC, thứ tự so sánh sẽ là: 1) so sánh system time trước, 2) nếu system time bằng nhau, so sánh counter.
cập nhật HLC: bao gồm cập nhật giá trị system time, và cập nhật counter (tăng lên). Khi HLC được reset thì counter lại về -1.
Cũng như Logical clock, mỗi server trong hệ thống sẽ khởi tạo và duy trì 1 đối tượng HLC của nó, gắn HLC vào event (đánh timestamp) gửi đi và cập nhật HLC khi nhận events từ server khác. Cũng như Vector clock, HLC có thể được đánh version và thường được lưu vào backend K-V store. Cách implement thì tuỳ vào từng software cụ thể.
HLC có thể được convert sang thời gian thực date-time, bạn tham khảo theo hướng dẫn trong paper gốc [1].
HLC được dùng trong các hệ thống distributed database như mongo, cockroachdb để xác định timestamp cho transaction khi nó được commit. Cách thiết kế và implement được giới thiệu trong bài viết.
Tài liệu tham khảo:
[1] Paper gốc về HLC: https://cse.buffalo.edu/tech-reports/2014-04.pdf
Góc Engineering Manager
Một trong những trách nhiệm mà Engineering Manager phải đối mặt đó là phỏng vấn tuyển dụng. Có nhiều kỹ thuật hoặc cách đặt câu hỏi có thể được, trong mục Góc EM tuần này, mời các bạn cùng tham khảo phương pháp phỏng vấn STAR.
Phương pháp này chú trọng việc đặt câu hỏi phỏng vấn thông qua khảo sát các tình huống cụ thể bằng cấu trúc:
Situation: yêu cầu ứng viên mô tả một tình huống cụ thể mà ứng viên phải đối mặt trong quá khứ. Hoặc người phỏng vấn cũng có thể nêu ra một tình huống giả định trong đó người phỏng vấn muốn khảo sát phương án xử lý của ứng viên. Đối với engineer, các tình huống này có thể là: một sự cố xảy ra lúc nửa đêm, hệ thống đột nhiên trả về toàn lỗi, database CPU tăng quá cao một cách bất chợt, sếp yêu cầu thay đổi kiến trúc trong một khoảng thời gian ngắn.
Task: Mô tả về mục tiêu đi kèm với tình huống đó. Ví dụ như khi tình huống đó xảy ra thì ứng viên phải ngay lập tức kiểm soát sự cố, hoặc phải đưa ra bản thiết kế mới, ...
Action: Yêu cầu ứng viên mô tả những hành động cụ thể mà chính ứng viên đã làm (hoặc sẽ làm nếu đây là một tình huống giả định)
Results: Yêu cầu ứng viên mô tả kết quả của những hành động của mình. Cũng như những kinh nghiệm rút ra.
Phương pháp này không chỉ dùng để đánh giá hành vi (behavior) mà còn có thể dùng để đánh giá kinh nghiệm xử lý sự cố, kinh nghiệm điều chỉnh kiến trúc, ... đặc biệt hữu dụng để phỏng vấn vai trò senior, lead trở lên.
Góc Lập Trình
Đề ra tuần này:
Bạn được cho trước một mảng k
linked-lists lists
, mỗi linked-list đều đã được sắp xếp theo thứ tự tăng dần.
Nhiệm vụ của bạn là merge tất cả các linked-list đó lại vào một linked-list duy nhất.
Lời giải tuần trước:
Bài toán yêu cầu tìm hoán vị thứ k trong chuỗi hoán vị của tập hợp [1, 2, 3, ..., n]
, nên cách đầu tiên ta có thể thử là sinh toàn bộ hoán vị của tập hợp và chọn hoán vị thứ k tương ứng. Tập hợp [1, 2, 3, ..., n]
sẽ có n!
(n giai thừa), vì vậy ta cũng có Time Complextiy và Space Complexity tương ứng là O(n!)
Để tối ưu cho giải thuật trên, ta có phân tích như sau:
Nếu ta cố định số thứ 1 ta có thể sinh được (n-1)!
hoán vị, nếu ta tiếp tục cố định số thử 2 ta có (n-2)
hoán vị. Ví dụ: với tập hợp [1, 2, 3]
, số hoán vị là 6
, nếu ta cố định 1
là số thứ nhất, ta sinh được 2 hoán vị là: 123
và 132
.
Như vậy, ta có thể xác định được vị trí của hoán vị cần tìm trong chuỗi hoán vị. Thuật toán được thực hiện như sau đây.
Có thể bạn chưa biết
Chúng ta sẽ cùng điểm lại một vài sự kiện lịch sử liên quan tới Internet và các trình duyệt.
Vào tháng 1 năm 1993, Marc Andreesen (người sáng lập a16z) đã viết Mosaic, một trong số các trình duyệt web đầu tiên đóng vai trò quan trọng trong việc phổ biến WWW. Tuy nhiên bản quyền lại thuôc về Trung tâm Ứng dụng siêu máy tính quốc gia (NCSA) tại Đại học Illinois tại Urbana, vì thời điểm này, anh ta đang thực tập tại đây!
Sau đó, vào tháng 4 năm 1994, Marc đã bắt đầu Netscape, viết những dòng code đầu tiên, và rất nhanh chóng phát triển công ty lên tới hơn 1000 nhân viên. Năm 1995, từ quý 1 tới quý 4, doanh thu của công ty đã tăng từ 5 triệu $ lên 40 triệu $, và lên tới 100 triệu $ vào năm 1996.
Chứng kiến sự trỗi dậy của Netscape, Bill Gates đã viết vào cuốn sổ tay của mình dòng chữ "The Internet Wave Tidal", và đẩy Internet lên vị trí ưu tiên hàng đầu của Microsoft, khi đó là tháng 5 năm 1995.
Chỉ 3 tháng sau đó, Microsoft đã tung ra Internet Explorer, tích hợp miễn phí vào hệ điều hành Windows 95. Điều này đã dẫn tới việc Netscape mất dần thị trường vào tay Internet Explorer, và sau đó đã phải bán cho AOL.
Thực tế dự án IE đã bắt đầu từ 1994, và vào cuối năm 1994, Microsoft đã thương lượng được với Spyglass trong việc nắm bản quyền source code của Spyglass Mosaic, phiên bản thương mại của NCSA Mosaic (được viết bởi... Marc Andreesen). Và IE đã được phát triển trên chính nền tảng Spyglass Mosaic!
Nói cách khác, Marc Andreesen đã phát triển Mosaic, trình duyệt web đầu tiên phổ biến trên internet, Netscape trình duyệt thương mại thành công đầu tiên, và sau đó IE - trình duyệt được xây dựng trên nền tảng Mosaic - đã khai tử Netscape!
Nguồn: LinkedIn
Các bạn có thể nghe thêm podcast sau để nghe Marc Andreesen chia sẻ câu chuyện về Netscape.
Code & Tools
Database documentation tự động với dbdocs.io
[Nội dung tài trợ] dbdocs.io là một công cụ giúp các bạn Dev có thể tự sinh ra nguyên trang document về cấu trúc database của mình chỉ bằng vài dòng lệnh command-line. Nó giúp quá trình planning và design database trong team dev dễ dàng hơn.
Hiện team dbdocs (ở TPHCM) đang tìm thêm 2 bạn fullstack/backend engineers để tham gia phát triển sản phẩm dev tool này. Bạn nào quan tâm có thể xem thêm tại: https://careers.holistics.io/job-openings/open-source-full-stack-engineer
(Team dbdocs cũng là team xây dựng công cụ dbdiagram.io phổ biến mà nhiều bạn dev đang dùng.)
Quotes
A distributed system is one in which the failure of a computer you didn’t even know existed can render your own computer unusable.
— Leslie Lamport