#219 - Triết lí Unix trong Apache Kafka và Samza
Trong số này, chúng ta cùng tìm hiểu về:
Hệ thống control plan storage Delos của Facebook
Triết lí Unix trong Apache Kafka và Samza
Các design patterns cho microservices observability
PACELC, một phiên bản mở rộng từ CAP theorem trong đó cân nhắc đến nhiều yếu tố hơn
Bài toán Merge k Sorted Lists và lời giải bài House Robber III
Những bài viết hay
Delos: Simple, flexible control plane storage
Để các hệ thống ở control plan như scheduler hay resource broker hoạt động hiệu quả, bền vững và nhanh chóng thì các hệ thống này cần phải sử dụng một hệ thống storage với đầy đủ các tính chất trên. Vào năm 2019, các đội ngũ kỹ sư ở Facebook đã công bố Delos, một hệ thống storage được thiết kế riêng cho các hệ thống ở tầng control plan. Trước khi Delos ra đời thì họ đã sử dụng Zookeeper cho phần control plan storage. Tuy nhiên, với việc Zookeeper chỉ đáp ứng được một số APIs nhất định, đi theo hướng monolithic và các chỉ số về hiệu suất và độ bền vững nhất định, các đội ngũ kỹ sư ở Facebook nhận ra rằng họ khó có thể đáp ứng được mọi yêu cầu từ các hệ thống ở control plane.
Do đó, Delos được sinh ra để giúp việc mở rộng hệ thống control plane storage một cách dễ dàng hơn bằng việc tách riêng các thành phần như là APIs, ordering, log, … Do việc tách riêng các thành phần này ra, họ có thể dễ dàng thay thế các bộ phận với nhiều công nghệ khác nhau để đáp ứng được các yêu cầu ở nhiều hệ thống control plane khác nhau. Để hiểu rõ hơn về cách đội ngũ kỹ sư ở Facebook đã thiết kế và áp dụng Delos như thế nào, các bạn có thể tham khảo qua bài viết sau.
(by curioustien)
Apache Kafka, Samza, and the Unix Philosophy of Distributed Data
Phát triển phần mềm thời hiện đại vẫn còn rất nhiều điều để học từ những năm 1970, một trong số đó là triết lí Unix. Chúng ta có thể làm được vô vàn khả năng chỉ với vài sự kết hợp của các lệnh awk, sed, grep, sort, uniq và xargs trong hệ điều hành Unix. Điều này không hề là ngẫu nhiên, đó là kết quả trực tiếp từ triết lí thiết kế của Unix:
Làm cho mỗi chương trình làm tốt một việc. Để thực hiện một công việc mới, hãy làm chương trình mới, thay vì làm phức tạp các chương trình cũ bằng cách thêm các “tính năng” mới.
Đầu ra của mọi chương trình trở thành đầu vào cho một chương trình khác không được biết trước
Những nguyên tắc này là nền tảng cho việc kết hợp (compose) các chương trình lại với nhau để thực hiện một công việc phức tạp, thông qua pipe ( "|"). Ý tưởng chính ở đây là một chương trình không biết hoặc không quan tâm đầu vào của nó đến từ đâu hoặc đầu ra của nó sẽ đến đâu: nó có thể là một file hoặc một chương trình khác thuộc hệ điều hành hoàn toàn khác.
Trong thực tế, Kafka thực chất có rất nhiều điểm tương đồng với pipe trong Unix, liên kết đầu ra của một tiến trình với đầu vào của một tiến trình khác. Và Samza giống như một thư viện giúp bạn đọc từ đầu vào và viết vào đầu ra. Sử dụng Kafka và Samza, bạn có thể lập trình với phong cách xử lí luồng (stream-processing) giống với phong cách Unix truyền thống, gồm các tool nhỏ và có khả năng kết hợp (composable)
Các nguyên tắc thiết kế giống Unix của Kafka cho phép xây dựng các hệ thống phức tạp ở quy mô lớn. Trong một tổ chức lớn, Kafka giúp mỗi team có thể độc lập phát triển các công việc xử lí luồng (steam-processing job), consume từ một luồng và publish sang luồng mới. Các luồng dữ liệu trong Kafka đóng vai trò là kênh giao tiếp giữa các hệ thống của các team khác nhau. Mỗi team tập trung giúp hệ thống của họ làm tốt một việc duy nhất.
Giống như các tool trong Unix có thể được kết hợp với nhau để hoàn thành một nhiệm vụ cụ thể, các hệ thống phân tán này được kết hợp với nhau, điều hành toàn bộ hoạt động của cả tổ chức lớn. Nhờ tính loose-coupling, các thành phần riêng lẻ có thể được phát triển và deploy hoàn toàn độc lập. Nhờ khả năng chịu lỗi và buffering của Kafka, khi có lỗi xảy ra ở một phần của hệ thống, nó sẽ được khoanh vùng và tách biệt. Các tổ chức này giúp mỗi team có thể phát triển nhanh chóng mà không sợ ảnh hưởng hay gián đoạn các team khác.
Trong bài viết, tác giả có miêu tả chi tiết triết lí Unix, sự khác biệt trong thiết kế database so với Unix, so sánh và nêu những điểm lợi của Kafka so với Unix pipe, và nhiều ví dụ thực tế. Mời các bạn cùng đọc.
(by nghialuu)
Microservices Observability Design Patterns
Khi một dịch vụ được triển khai, chúng ta sẽ muốn biết dịch vụ đó đang hoạt động như thế nào, bao nhiêu request mỗi giây, mức độ sử dụng tài nguyên v.v... Ngoài ra chúng ta cũng muốn nhận được những thông báo nếu có sự cố xảy ra như một service instance bị lỗi, hay ổ cứng hết dung lượng v.v.. Một cách lý tưởng nhất là nhận được thông báo trước khi nó gây tác động tới người dùng.
Do đó chúng ta cần triển khai một số pattern để giúp quản lý các service cũng như khắc phục sự cố một cách dễ dàng hơn. Một số pattern phổ biến hiện nay như: Log aggregation, Health check API, Distributed tracing, v.v...
Mời các bạn đọc bài viết sau để tìm hiểu kỹ hơn về các pattern này.
(by lpv)
Góc Data
Consistency Tradeoffs in Modern Distributed Database System Design
Trong các kỳ newsletter trước mục Góc Database đã giới thiệu cho các bạn về CAP theorem. Mặc dù CAP theorem đang được vận dụng khá rộng rãi trong các dòng distributed database hiện đại, nhiều người vẫn cảm thấy bản thân lý thuyết này vẫn có một vài khuyết điểm như:
Dễ gây ngộ nhận rằng CA là một phương án có thể lựa chọn trong 3 phương án CA, CP, AP (chính tác giả cũng thừa nhận điều này ở một bài blog viết hơn 10 năm sau khi công bố paper)
Tập trung quá nhiều vào consistency/availability tradeoff mà không hề cân nhắc đến "latency", trong khi latency là một yếu tố quan trọng cần cân nhắc khi lựa chọn database hiện đại.
Đến với góc Database kỳ này, mời các bạn cùng tham khảo bài báo của tác giả Daniel J. Abadi xuất bản năm 2012 chia sẻ về một PACELC, một phiên bản mở rộng từ CAP theorem trong đó cân nhắc đến nhiều yếu tố hơn.
(by n^4)
Góc Lập Trình
Đề ra tuần này: Merge k Sorted Lists
Lời giải đề bài tuần trước: House Robber III
Ta có thể diễn giải đề bài toán một cách đơn giản hơn như sau, cho một cây nhị phân, tìm tập hợp các nút trong cây có tổng giá trị lớn nhất, với điều kiện không có 2 nút liên tiếp kề nhau, trả về tổng giá trị đó.
Ta có thể nhận thấy đây là một bài toán mang tính chất cấu trúc con tối ưu (optimal substructure), chính vì vậy khả năng cao có thể giải bằng quy hoạch động.
Để giải bài toán quy hoạch động, hướng suy nghĩ thường như sau:
Tìm một lời giải đơn giản cho bài toán bằng đệ quy.
Xác định các bước lặp trong lời giải đệ quy, ghi nhớ giá trị tại bước đệ quy đó bằng “memoization” (Nếu bạn chưa nghe đến thì nó là memoization chứ không phải memorization. Memoization là một thuật ngữ dùng trong ngành khoa học máy tính, tham khảo wiki).
Quay trở lại bài toán, với mỗi nút trong cây, ta có 2 lựa chọn:
Chọn nút hiện tại, và tiếp tục xét các nút con tiếp theo không liền kề nó.
Bỏ qua nút hiện tại, và xét 2 nút con của nó.
Giải thuật đệ quy đơn giản như sau: https://pastebin.com/HK91QfU4
Để ý rằng, các lời gọi trong trường hợp 1 sẽ bị lặp bởi các lời gọi tiếp theo ở trường hợp 2. Vì vậy nếu ta thực hiện lưu giá trị trả về của hàm traverse tại mỗi nút ở lần gọi đệ quy đầu tiên, ta sẽ tối ưu được các lệnh gọi đệ quy bị lặp lại.
Lời giải cuối cùng cho bài toán như sau: https://pastebin.com/CJxYW4ND
Vì mỗi nút mạng chỉ sẽ được xét nhiều nhất 1 lần, nên độ phức tạp thời gian là O(N)
(by ndaadn)
History
Happy birthday to Edsger Dijkstra!
Dijkstra có lẽ làm một cái tên không còn xa lạ với bất kỳ ai trong chúng ta. Ông sinh ngày 11 tháng 5 năm 1930 tại Rotterdam, mất ngày 6 tháng 8 năm 2002.
Ông được nhận giải thưởng Turing năm 1972 cho các đóng góp có tính chất nền tảng trong lĩnh vực ngôn ngữ lập trình. Ông cũng đã được nhận giải thưởng của ACM dành cho paper xuất sắc trong lĩnh vực phân tán. Sau khi ông qua đời, giải thưởng thường niên này đã được đổi tên thành giải thưởng ACM Edsger W. Dijkstra.
Các bạn sinh viên hẳn đều đã từng code thuật toán Dijkstra cho bài toán tìm đường đi ngắn nhất. Thuật toán này chỉ được thiết kế trong vòng 20 phút. Đó là vào một buổi sáng khi ông đi mua sắm ở Amsterdam cùng với vị hôn thê của mình. Khi mệt mỏi họ đã ngồi xuống uống cafe và tại đây ông đã tự hỏi liệu có thể làm được bài toán này không. Một trong những lý do khiến thuật toán này đẹp đó là nó được thiết kế mà không cần bút và giấy. Sau này ông học được rằng, một trong những lợi thế của việc thiết kế mà không cần bút và giấy đó là bạn gần như buộc phải tránh tất cả những điều phức tạp có thể tránh được.
Các bạn có thể đọc kỹ hơn bài phỏng vấn ông vào năm 2001 tại đây.
(by lpv)
Code & Tools
Apache Ratis - Một thư viện Java cho Raft protocol
GoogleTest - Một thư viện testing và mocking cho C++ của Google
Quotes
There are very different programming styles. I tend to see them as Mozart versus Beethoven. When Mozart started to write, the composition was finished. He wrote the manuscript and it was 'aus einem Guss' (from one cast). In beautiful handwriting, too. Beethoven was a doubter and a struggler who started writing before he finished the composition and then glued corrections onto the page. In one place he did this nine times. When they peeled them, the last version proved identical to the first one.
— Dijkstra
Bạn đánh giá nội dung số newsletter này thế nào?
(1 = Rất tệ / 5 = Rất tốt)
(Việc đánh giá của các bạn là rất quan trọng, sẽ giúp chúng tôi liên tục cải thiện nội dung newsletter tốt hơn)