#146 - Xử lý dữ liệu lỗi trong Streaming System của Gojek
Những bài viết hay
Handling Dead Letters in a Streaming System — blog.gojekengineering.com
Gojek đã thực thi hơn 3,5 tỷ giao dịch kể từ khi ra mắt ứng dụng vào năm 2015. Với số lượng đơn đặt hàng đáng kinh ngạc, đối tác thương mại, đối tác tài xế và người tiêu dùng trong hệ sinh thái, người ta có thể tưởng tượng lượng dữ liệu mà Gojek cần xử lý là rất dồi dào. Việc xử lý dữ liệu không tốt, đặc biệt với những dữ liệu không đúng, có thể gây ảnh hưởng đến các downstream services hoặc làm giảm hiệu năng của hệ thống.
Về việc xử lý dữ liệu phục vụ phân tích trong Gojek, các ứng dụng sẽ liên tục gửi messages đến các Kafka topics. Lượng dữ liệu này sẽ được xử lý và lưu trữ ở Google Bigquery bởi các consumer services mà Gojek gọi là Beast. Tuy nhiên, điều gì xảy ra nếu các messages được tạo ra không chính xác? Với vấn đề này, Gojek chia làm 3 loại:
Invalid data: schema của data đúng nhưng data không có giá trị
Invalid schema: schema của message không chính xác, ví dụ như thiếu, thừa 1 field nào đó, hoặc data type của 1 field không chính xác.
Partition key out of range: xảy ra khi bảng trong BigQuery được partition theo timestamp mà giá trị này trong consumed messages nằm ngoài khoảng cho phép. Điều này làm việc insert data vào bảng không thành công. Khi đó, lượng messages không được xử lý trong queue sẽ tăng lên liên tục, timeout xảy ra và Beast consumer service sẽ down.
Giải pháp cho vấn đề được đưa ra: lưu trữ Dead Letters (invalid messages) trong GCP:
Các messages bị lỗi chưa xử lý được lưu trữ trong một store riêng biệt và chúng có thể được sử dụng để đánh giá cho các ứng dụng upstream tạo ra chúng.
Tạo ra các metrics để xác định những ứng dụng liên tục tạo ra các messages bị lỗi
Alert đến các ứng dụng tạo ra messages lỗi
Vấn đề của Gojek là cần lưu những messages lỗi này ở đâu. Một giải pháp là mỗi kafka topic của một loại event sẽ có một kafka topic tương ứng lưu trữ các invalid messages. Tuy nhiên với lượng topics khổng lồ, việc này sẽ gây ra nhiều tốn kém về resources (RAM, CPU) khi phải scale up Kafka cluster. Cuối cùng, Gojek quyết định lưu trữ lượng data này vào Google Cloud Storage (GCS). Việc phân tích dữ liệu trong GCS là rất thuận tiện với nhiều tích hợp với các Big Data framework/engine như Spark, Hadoop hay Presto. Ngoài ra, chi phí lưu trữ trên GCS sẽ rẻ hơn nhiều so với việc scale Kafka cluster và lưu trữ trên các Kafka topic.
A Complete Guide to React Rendering Behavior — gist.github.com
Nếu bạn đang hoặc đã sử dụng ReactJS, bạn có từng nghĩ khi nào component được render, hay tại sao component được render lại. React xử lý việc render như thế nào? Việc chúng ta sử dụng Context và React-Redux sẽ ảnh hưởng như thế nào với render component? Mark Erikson - một kĩ sư trong Redux team đã chia sẻ bài viết này để giải đáp tất cả những câu hỏi liên quan tới rendering trong react.
3 CQRS Architectures that Every Software Architect Should Know — levelup.gitconnected.com
Bạn có thể nghe nhiều tới CRUD(Create - Read - Update - Delete) khi giao tiếp với Database. CRUD đáp ứng hầu hết các nhu cầu cơ bản của hệ thống. Tuy nhiên, với một số hệ thống mà cách đọc lên và ghi xuống không giống nhau, hay vì lý do performance mà chúng ta cần phải tối ưu, hay thậm chí chúng ta cần có nhiều cách hiển thị khác nhau cho cùng một data. Lúc này, CRUD không còn phù hợp nữa, ta cần một pattern khác thay thế. CQRS - Command Query Responsibility Segregation - có thể là pattern chúng ta cần.
Ý tưởng chính của CQRS là chi tách hai nhu cầu đọc (Read/Query) và ghi (Write/Command) thành hai module khác nhau. Mỗi module sẽ được tối ưu hoá để thực hiện nhiệm vụ của mình một cách hiệu quả nhất. Có ba loại kiến trúc CQRS chính mà bạn có thể áp dụng bao gồm:
- Single Database CQRS: cả hai module Command và Query cùng giao tiếp với một database. Đây là cách implement đơn giản nhất của CQRS, tuy nhiên có một số vấn đề như Query module phải phụ thuộc vào cách Command module ghi dữ liệu như thế nào dẫn đến khó tối ưu hoá cho việc đọc, vì dùng chung một database nên có thể ảnh hưởng tới performance.
- Two-database CQRS: với hướng tiếp cận này, chúng ta sẽ tách thành 2 database khác nhau, Command module sẽ ghi dữ liệu xuống "Write Database", Query module sẽ đọc dữ liệu từ "Read Database". Do mỗi module làm việc với database của nó, do đó chúng ta phải đẩy dữ liệu từ "Write Database" sang "Read Database". Với hướng tiếp cận này, chúng ta có thể giải quyết một số vấn được đề cập ở Single Database CQRS, nhưng cũng tăng thêm sự phức tạp cho hệ thống.
- Event-Sourcing CQRS: tương tự như Tow-Database CQRS, chúng ta cũng có 2 databases, điểm khác biệt cơ bản là command module thay vì lưu dưới dạng "final-state", data được lưu dưới dạng event. Để biết thêm thông tin chi tiết về Event-Sourcing bạn có thể xem thêm video này An Introduction to CQRS and Event Sourcing Patterns - Mathew McLoughlin. Dữ liệu được lưu ở dạng event, vì thế khi đẩy sang "Read Database" chúng ta phải tính toán lại để có được final state. Đây là hướng tiếp cận phức tạp nhất của CQRS, tuy nhiên nó mang lại các ưu điểm của Event-sourcing như không cần xây dựng audit trail, biết được trạng thái dữ liệu tại bất kỳ thời điểm nào(rất hữu ích cho quá trình debug). Ngoài ra bạn cũng có thể tối ưu hoá cách đọc theo nhiều cách khác nhau.
Trong một số trường hợp, việc phân tách giữa đọc và ghi mang lại nhiều ưu điểm, nhưng lưu ý rằng việc áp dụng CQRS sẽ tăng thêm độ phức tạp của hệ thống.
Comparing API Architectural Styles: SOAP vs REST vs GraphQL vs RPC
Để hai services hay lớn hơn là 2 hệ thống giao tiếp được với nhau chúng ta cần xây dựng một cầu nối mà thông qua đó hai bên có thể hiểu nhau. Theo thời gian, các cầu nối này trở thành các chuẩn giao thức khác nhau như RPC, SOAP, REST và mới nhất là GraphQL.
RPC (Remote Procedure Call): XML-RPC là version đầu tiên của RPC, tuy nhiên nó vấp phải các vấn về loại dữ liệu(data types) theo chuẩn XML, do đó JSON-RPC được sử dụng thay cho XML-RPC. gRPC là version mới nhất của RPC được phát triển bởi Google vào năm 2015.
Ưu điểm:
Tương tác đơn giản và dễ dàng
Thêm function mới dễ dàng
Hiệu suất cao
Nhược điểm:
Lệ thuộc chặt chẽ trong hệ thống
Khả năng khám phá thấp. Không có cách nào xem xét bên trong API.
Bùng nổ function. Thêm một tính năng quá dễ dàng do đó chúng ta thường có xu hướng thêm function mới thay vì chỉnh sửa function hiện có để đáp ứng một nhu cầu mới.
Các trường hợp sử dụng RPC:
Xây dựng command API
API nội bộ đòi hỏi hiệu năng cao
SOAP (Simple Objects Access Protocol): SOAP sử dụng XML format để gửi các gói tin giữa các hệ thống, được phát triển bởi Microsoft sau XML-RPC một năm.
Ưu điểm:
Không phụ thuộc ngôn ngữ và nền tảng
Phù hợp với nhiều giao thức truyền tải khác nhau
Tích hợp xử lý lỗi (Built-in error handling)
Tích hợp các giao thức WS-Security
Nhược điểm:
SOAP message chứa rất nhiều medata và chỉ hỗ trợ XML
Do kích thước các tập tin lớn, do đó tiêu tốn băng thông
Đòi hỏi kiến thức chuyên sâu của các giao thức liên quan và các quy tắc nghiêm ngặt
Tốn chi phí (effort) để thêm hoặc xoá các thuộc tính trong message
Trường hợp sử dụng SOAP: cấu trúc cứng nhắc cùng với khả năng bảo mật cao khiến SOAP trở thành lựa chọn phù hợp cho các dạng phần mềm đòi hỏi sự tuân thủ nghiêm ngặt "contract" giữa API provider và API consumer. Đó là lý do tại sao các tổ chức tài chính sử dụng SOAP.
REST(Representational state transfer): kiểu API phổ biến nhất hiện nay. REST cung cấp dữ liệu ở các định giản đơn giản, thường JSON hoặc XML.
Ưu điểm:
Tách rời Client và Server
Giao tiếp giữa client và server mô tả mọi thứ do đó không cần thêm bất cứ document nào để giao tiếp với REST API
Hỗ trợ caching ở tầng HTTP
Hỗ trợ nhiều định dạng khác nhau
Nhược điểm
Không có cấu trúc thống nhất. Mỗi một team, mỗi một tổ chức xây dựng REST theo một kiểu khác nhau.
REST trả về rất nhiều metadata để client có thể sử dụng tuỳ vào mục đích, trong đó có những metadata không cần thiết do đó gây lãng phí tài nguyên mạng
Thường cần nhiều request để lấy đủ thông tin cần thiết
Trường hợp sử dụng:
Management API: quản lý các thực thể (objects) trong hệ thống
Simple resource-driven apps: REST phù hợp với cách tiếp cận cho các ứng dụng dựa trên tài nguyên không cần sự linh hoạt trong query data
GraphQL: tương tự như REST nhưng thay vì trả về tất cả dữ liệu cho client mà không biết client cần gì, GraphQL cho phép client chỉ query những dữ liệu cần thiết.
Ưu điểm:
Typed schema: GraphQL công bố trước những gì một API có thể trả về, điều này giúp client có thể quyết định dữ liệu trả về bao gồm những gì.
Không có versioning. REST cung cấp nhiều version API khác nhau cho từng loại client khác nhau, GraphQL sử dụng một API duy nhất cho tất cả các client khác nhau
Tương tự SOAP, GraphQL cung cấp thông tin chi tiếp về các lỗi xảy ra
Xử lý quyền (permissions) linh hoạt. GraphQL cho phép thông tin nào có thể trả về, thông tin nào không thể tuỳ thuộc vào permission của client.
Nhược điểm:
Hiệu năng: có quá nhiều nested fields trong một request có thể ảnh hưởng tới hiệu năng của hệ thống
Không cung cấp HTTP cache giống như REST
Cần nhiều thời gian để tìm hiểu GraphQL
Trường hợp sử dụng:
Mobile API: trong trường hợp mobile network cũng là một vấn đề cần được quan tâm do đó GraphQL cung cấp khả năng query dữ liệu hiệu quả hơn.
Complex sýtems and microservices: GraphQL có thể che giấu sự phức tạp của hệ thống thông qua API bằng cách tổng hợp dữ liệu nhiều nơi.
Góc Distributed System
Càng ngày các hệ thống phân tán càng tiến hoá theo chiều hướng dễ dàng tiếp cận hơn với người vận hành thông qua các công cụ hỗ trợ đắc lực và dễ sử dụng; trong khi vẫn duy trì được những đặc tính ưu việt của nó, đó là một mạng lưới các servers hoạt động một cách ổn định (reliability), dễ mở rộng (scalability) và luôn sẵn sàng (availability). Một trong những keyword chúng ta thường xuyên nghe thấy hiện nay là Overlay Network. Đây là lớp mạng lõi của các hệ thống phân tán giúp duy trì kết nối trao đổi thông tin một cách thông suốt và linh hoạt giữa các server, và hẳn nó đã tồn tại ngay từ những ngày sơ khai của các mạng phân tán. Tuy nhiên khái niệm Overlay Network chỉ thực sự trở nên phổ biến và thân thiện khi trào lưu Software Defined Network (SDN) phát triển. Có thể chúng ta đã nghe nhiều về keywork Overlay Network, nhưng bạn có thực sự hiểu nó là gì ?Về cơ bản, Overlay Network mô tả cách vận hành hệ thống network thông qua các phần mềm dễ tiếp cận với người lập trình (và vận hành) mà ẩn đi (omit) tất cả những sự phức tạp của công nghệ mạng ở phía dưới. Để hiểu kĩ hơn về Overlay Network, góc Distributed System lần này mời bạn tìm hiểu loạt bài viết dưới đây nhé.
Góc Database
Nếu bạn đã từng tìm hiểu về các thiết kế của các hệ thống database, hẳn bạn sẽ biết việc thiết kế các phương thức truy xuất dữ liệu (access methods) sẽ phụ thuộc nhiều vào phần cứng và yêu cầu tải (workload requirement).
Để đưa ra một cơ sở lý luận cho việc thiết kế các phương thức truy xuất dữ liệu ngày càng hiệu quả hơn trong tương lai, Manos Athanassoulis và nhóm tác giả đến từ Havard, IBM Research, EPFL và Facebook đã đưa ra một mô hình phân tích về tradeoff trong quá trình thiết kế các phương thức truy xuất dữ liệu.
Trước tiên, chúng ta hãy cùng làm quen một vài khái niệm:
Khái niệm overhead. Trong bài báo này các bạn sẽ nghe đề cập nhiều đến khái niệm overhead. Khái niệm này có thể hiểu một cách nôm na là sự lãng phí, trong ngữ cảnh của bài báo (và có thể nhiều ngữ cảnh khác) khái niệm này ám chỉ những chi phí phát sinh bên lề khi bạn thực thi một tác vụ nào đó.
Read overhead (RO) là chỉ số ám chỉ lượng dữ liệu dư thừa mà bạn phải đọc chỉ để lấy ra thông tin cần thiết. Ví dụ như bạn có một file có 500 dòng chứa thông tin học sinh của một khoa. Bạn muốn lấy thông tin của học sinh A ở dòng thứ 31. Tuy nhiên do hệ thống chỉ cho phép bạn đọc dữ liệu theo batch mỗi lần 30 dòng nên bạn phải tải hết dữ liệu từ dòng 31 đến dòng 60 lên bộ nhớ chỉ để lấy thông tin về dòng thứ 31. Trong ví dụ này, 29 dòng dữ liệu từ 32 đến 60 được xem là “read overhead”.
Update overhead (UO) là chỉ số ám chỉ lượng dữ liệu phải được cập nhật một cách không chủ đích chỉ để các dòng dữ liệu mà bạn muốn thay đổi. Tương tự ví dụ trên, bạn có thể hình dung nếu ta chỉ muốn cập nhật dữ liệu của dòng 31, tuy nhiên do giới hạn phần cứng buộc ta phải ghi 30 dòng một lúc thì 29 dòng dữ liệu được ghi kèm theo (mặc dù không có thay đổi gì) được xem là “update overhead”.
Memory overhead (MO) là chỉ số ám chỉ phần không gian bộ nhớ phải tiêu tốn chỉ để lưu trữ các dữ liệu dư thừa đi kèm phần dữ liệu chính muốn xử lý. Trong ví dụ trên, phần bộ nhớ dùng để lưu trữ 29 dòng dữ liệu kèm theo được xem là “memory overhead”.
Giống như động cơ vĩnh cửu vẫn đang là niềm mơ ước của các nhà khoa học vật lý, bài toán làm sao giảm thiểu hết mức ba loại chỉ số RO, UO, MO cũng được xem là thử thách mà các nhà khoa học máy tính đang không ngừng tìm kiếm giải pháp. Nhưng liệu chúng ta có thể tạo ra một giải pháp đồng thời đạt được cả ba không?
Nếu như định lý CAP (đã đề cập trong newsletter #137) trong hệ dữ liệu phân tán đã đưa ra giới hạn rằng trong một giải pháp thì chỉ có thể chọn hai trong ba yếu tố C-A-P mà không thể nào đạt được cả ba thì trong bài báo này các tác giả cũng đưa ra Phỏng đoán RUM tiên đoán rằng một phương thức truy cập chỉ có thể tối ưu được hai trong ba yếu tố RO, UO, MO nêu trên.
Mời các bạn cùng tham khảo bài báo được xuất bản vào năm 2016 trong khuôn khổ hội nghị Extending Database Technology (EDBT) để hiểu thêm về các phân tích của nhóm tác giả. Link bài báo.
Code & Tools
This week sponsor
Established in 2012, Chotot.com is the leading online classified website in Vietnam with more than 1,2 billion monthly page views, 10 million monthly users, 3,6 million transactions in 2019. With the motto “Muốn Là Có” (“A Way to Your Wants”), Chotot.com provides an effective online marketplace for Vietnamese to buy and sell various types of products easily. For more information, please visit www.chotot.com.
Góc tuyển dụng
ChoTot create product using a variety of open source and cloud native technologies, to name some: Kubernetes, Kafka, Elastic Search, PostgreSQL, MongoDB, TimescaleDB. We utilize Google Cloud Platform, such as Airflow, Argo, Data Catalog, Google AI Platform, Google BigQuery, Google Analytics, Superset for our infrastructure needs while running our heavy workloads on our own on-premises infrastructure. Visit careers.chotot.com to learn more about our vacancies and company culture.
Quotes
A language that doesn't affect the way you think about programming is not worth knowing.
- Alan J. Perlis