Distributed teams là gì

Distributed transaction là gì?

Tuan
Follow
May 25 · 8 min read

Thời của kiến trúc microservices đã làm thay đổi cách thiết kế các hệ thống. Chúng ta đi từ kiến trúc monolithic sang microservices nhằm mục đích tăng tốc độ phát triển, linh hoạt, hiệu năng cao hơn, scale dễ hơn... Tuy nhiên điều đó cũng mở ra những vấn đề công nghệ mới như service discovery, balancing, retry, sidecar... Một vấn đề được quan tâm nhiều đó là làm thế nào để mang các transaction sang distributed transaction tức là một giao dịch được thực hiện trên nhiều microservices khác nhau, chỉ thành công khi toàn bộ các services thực hiện thành công; và nếu thất bại thì sẽ roll-back lại như thế nào?

Trong bài viết này tôi sẽ trình bày về các thiết kế distributed transaction, cách triển khai idempotent, compensation transaction và phân tích những ưu nhược điểm của chúng. Những thiết kế này hoàn toàn có thể mang vào áp dụng cho các hệ thống phân tán như giao dịch thanh toán, chứng khoán...

Một số khái niệm

Database transaction là đơn vị công việc được thực hiện trong một hệ quản trị cơ sở dữ liệu hoặc các hệ tương tự, mỗi giao dịch được xử lý nhất quán và tin cậy mà không phụ thuộc vào các giao dịch khác. Một hệ CSDL lý tưởng phải đảm bảo toàn bộ tính chất ACID cho mỗi giao dịch. Chi tiết ACID bạn có thể xem ở đây.

Distributed transaction Giao dịch phân tán là một giao dịch cơ sở dữ liệu trong đó hai hoặc nhiều hơn máy chủ mạng tham gia vào. Thông thường các máy chủ cung cấp các tài nguyên giao dịch, trong khi người quản lý giao dichjchiuj trách nhiệm tạo và quản lý một giao dịch toàn cục bao trùm toàn bộ các hoạt động với các tài nguyên đó.

Idempotent là một đặc điểm của một operation cho phép có thể thực hiện operation đó nhiều lần mà không làm thay đổi kết quả. Ý tưởng này thường được dùng trong thiết kế các dịch vụ webservice hoặc event stream. Ví dụ với Restful service, một operation request là idempotent nếu sau vô số lần gọi, nó vẫn trả về kết quả như nhau. Ví dụ request gọi tới GET là idempotent vì nó không thay đổi giá trị nhưng request gọi đến POST,, DELETE, PUT, PATCH thì tùy trường hợp có thể idempotemt hoặc không.

Compensation transaction là một transaction cho việc đảo ngược lại những thao tác đã thực hiện. Ví dụ một distributed transaction thực hiện một loạt các bước, nếu một bước trong số đó bị lỗi, thì không thể thực hiện rollback lại database như hệ thống monolith, mà sẽ thực hiện một compensation transaction để quay ngược lại các thay đổi mà các service đã thực hiện.

Các thiết kế distributed transaction

Trong kiến trúc microservices, một hệ thống monolith được phân rã thành các services tách biệt. Có nghĩa local transaction trong hệ thống monolith bây giờ trở thành distributed transaction giữa multiple services.

Vậy giải pháp nào cho vấn đề thực hiện distributed transaction? Giả sử chúng ta có hệ thống cho phép người dùng đặt hàng, nếu một người dùng đặt một lệnh mua hàng thì sẽ lần lượt thực hiện cập nhật vào service Customer và service Order.

Trong hệ thống cơ sở dữ liệu, tính Atomicity nghĩa là một giao dịch hoặc là thực hiện hoàn thành đầy đủ các bước hoặc không có bước nào hoàn thành. Hệ thống microservice không có một global transaction coordinator mặc định. Nếu CreateOrder bị lỗi thì làm thế nào có thể rollback lại những thay đổi đã thực hiện ở CustomerMicroservice.

Nếu một đối tượng được ghi bởi một transaction và ở cùng thời điểm đó [trước khi transaction kết thúc], đối tượng cũng được đọc bởi một request khác, thì dữ liệu trả về sẽ là dữ liệu cũ hay dữ liệu được cập nhật.? Trong trường hợp trên, khi UpdateCustomerFund thành công nhưng nó vẫn đợi response từ CreateOrder, khi đó các requests truy xuất danh fund của customer này sẽ trả về kết quả đã được cập nhật hay không?

Giải pháp

Các vấn đề trên thực sự quan trong trong hệ thống microservices. Nếu không thì không có cách để biết được transaction đã thực hiện thành công hay chưa. Hai pattern thường được sử dụng gồm

  • 2pc [two-phase commit]
  • Saga

2-phase commit

2PC thường được sử dụng trong các hệ CSDL. Trong một số trường hợp, chúng ta có thể sử dụng 2pc cho microservices. Nhưng cần chú ý, không phải tất cả các trường hợp đều phù hợp với 2pc và thực tế thì 2pc bị xem là không thực tế trong kiến trúc microservices.

Vậy thế nào là 2pc?

Đúng như tên gọi, 2pc bao gồm 2 phases: một prepare phase và một commit phase. Trong prepare phase, tất cả các microservices cần tham gia sẽ được yêu cầu chuẩn bị cho việc thay đổi dữ liệu cần thực hiện atomic. Khi tất cả các miroservices tham gia đã chuẩn bị xong thì phase commit sẽ yêu cầu các service thực hiện thay đổi.

Thông thường sẽ cần có sự tham gia của một global coordinator để đảm bảo vòng đời của transaction, và coordinator sẽ cần gọi tới các microservices trong phase prepare và commit.

Trong ví dụ trên, kh người dùng gửi một request Order, Coordinator ẽ tạo một global transaction với tất cả các thông tin cần thiết. Nó sẽ báo CustomerMicroservice chuẩn bị cho việc cập nhật tiền của một customer với transaction vừa được tạo. CustomerMicroservice sẽ kiểm tra, ví dụ customer đó có đủ tiền để thực hiện không. Khi CustomerMicroservice đồng ý thực hiện, nó sẽ khóa đối tượng customer để không bị thay đổi và báo Coordinator là đã chuẩn bị xong. Tương tự cũng diễn ra với OrderMicroservice. Khi Coordinator xác nhận các microservices đã sẵn sàng thay đổi, nó sẽ yêu cầu chúng thực hiện thay đổi. Sau khi thực hiện xong các đối tượng sẽ được mở khóa.

Nếu ở bất cứ điểm nào có một microservices faile ở bước chuẩn bị, Coordinator sẽ bỏ transaction và bắt đầu rollback. Ví dụ

Như hình bên trên, khi CustomerMicroservice failed không prepare phase được thì OrderMicroservice đã báo đã prepare xong. Coordinator sẽ yêu cầu bỏ abort trên OrderMicroservice và Order microservice sẽ rollback các thay đổi đã thực hiện, unlock các objects.

Lợi ích của việc sử dụng 2pc

2pc là một giao thức mạnh về tính nhất quán - consitency. Đầu tiên, các bước prepare và commit đảm bảo transaction là atomic. Transaction sẽ kết thúc khi tất cả các microservices báo thành công hoặc tất cả các microservices không thực hiện thay đổi nào. Thứ hai, 2pc cho phép read-write isolation. Có nghĩa là các thay đổi trên dữ liệu là không thấy được cho tới khi Cooridnator commit.

Nhược điểm của 2pc

Trong khi 2pc có thể giải quyết được vấn đề thì nó thường không phù hợp với hệ thống microservices vì 2pc là synchronous [blocking]. Giao thức này cần khóa các đối tượng sẽ bị thay đổi trước khi transaction hoàn thành. Trong ví dụ trên nếu một customer đặt một order thì thông tin field "fun" sẽ bị khóa trên đối tượng đó. Điều này dẫn đến đối tượng không thể thực hiện thêm các orders mới.

Điều này là không tốt, trong các hệ CSDL, các transactions thường nhanh, khoảng 50ms. Tuy nhiên, các microservices sử dụng RPC hoặc HTTP call có delay cao. Việc lock chính là bottleneck của hệ thống. Ngoài ra có khả năng hai transaction thực hiện lock lẫn nhau dẫn tới deadlock.

SAGA

Saga pattern là một pattern được sử dụng rộng rãi trong distributed transaction. Saga pattern thực hiện bất đồng bộ asynchronous và reactive. Trong Saga, một distributed transaction thực hiện các asynchronous local transaction trên tất cả các related microservices. Các microservices giao tiếp với nhau thông qua một event bus.

Trong hình bên trên, OrderMicroservice nhận request tạo order. Đầu tiên nó sẽ bắt đầu một local transaction tạo order và emit một event OrderCreated. CustomerMicroservice lắng nghe sự kiện này và cập nhật customer fund khi nhận được event. Nếu CustomerMicroservice thực hiện thành công, một event CustomerFundUpdated sẽ được emit và kết thúc transaction.

Nếu bất kỳ một microservices nào bị lỗi trong local transaction của nó, các microservices khác sẽ thực hiện một compensation transaction để rollback các thay đổi.

Trong ví dụ trên, UpdateCustomerFund thất bại nó sẽ báo một event CustomerFundUpdateFailed. OrderMicroservice lắng nghe event này và bắt đầu thực hiện compensation trasaction để thực hiện xử lý lại các thay đổi.

Ưu điểm của Saga

Một ưu điểm lớn của Saga đó là hỗ trợ long-lived transactions. Bởi vì mỗi microservice tập trung vào thực hiện local transaction, các microserivce khác không bị block nếu microservice thực hiện trong thời gian dài. Điều này cho phép các transactions mới tiếp tục được thực hiện. Ngoài ra, bởi vì các local transactions thực hiện song song, không còn việc các object bị lock.

Nhược điểm của Saga.

Saga pattern khó khi cần thực hiện debug, đặc biệt khi có nhiều services tham gia. Ngoài ra vì event bus sẽ trở nên khó trong việc maintain nếu hệ thống phức tạp. Ngoài ra một nhược điểm nữa là nó không thực hiện được read-isolation. Ví dụ customer sẽ thấy order đã được tạo nhưng một giây sau đó order bị xóa vì compensation transaction.

Process manager

Để xử lý các vấn đề phức tạp với Saga pattern, thông thường có thêm một process manager đóng vai trò như một orchestrator như bên 2PC. Process manager này có trách nhiệm lắng nghe các event và kích hoạt các endpoints.

Tổng kết

Saga pattern là cách yêu thích hơn để giải quyết distributed transaction trong kiến trúc microservices. Tuy nhiên nó cũng đưa ra một số vấn đề như làm thế nào để atomic thay đổi dữ liệu hay emit event. Sử dụng Saga pattern yêu cầu thay đổi cách nghĩ về việc phát triển và kiểm thử. Nó sẽ là một hử thác với các teams mới chuyển sang microservices hoặc không quen thuộc với pattern này.

Video liên quan

Chủ Đề