SAGA Pattern trong kiến trúc ngân hàng lõi (Core Bank Architecture)

Bài viết đến từ anh Trần Minh Thiện – Giám đốc Kiến trúc Giải pháp

Enterprise Architect team @Techcombank

1. Tổng quan 

Trong các hệ thống phân tán (distributed systems), các giao dịch thương mại (business transactions) qua nhiều tầng dịch vụ (multiple-layer services) cần thiết có một cơ chế để đảm bảo dữ liệu phải nhất quán xuyên suốt. Vì vậy, khái niệm SAGA đại diện cho một mô hình hoạt động thương mại tổng quát (như khi ta đặt một  chuyến du lịch) gồm nhiều yêu cầu chuyên sâu (low-level requests) mà mỗi yêu cầu này sẽ thực hiện cập nhật dữ liệu trong từng service đơn lẻ. Mỗi yêu cầu cũng  sẽ chứa một yêu cầu khôi phục (compensating request) chỉ được thực hiện khi yêu cầu ban đầu bị lỗi.

core bank architecture

2. Ngữ cảnh 

Giao dịch (transactions) là thành phần chủ yếu trong mọi ứng dụng doanh nghiệp (enterprise application). Không có giao dịch sẽ không thể duy trì tính nhất quán cho dữ liệu (data consistency). Tuy nhiên, trong một mô hình Microservices, chúng ta sẽ không có một nguồn thống nhất vì các trạng thái được phân bổ trên khắp  các service riêng với dữ liệu lưu trữ của riêng chúng. Trong các hệ thống phân tán, các giao dịch thương mại qua nhiều tầng dịch vụ yêu cầu một cơ chế để đảm bảo tính nhất quán dữ liệu qua các dịch vụ. 

SAGA là một mô hình có thể giúp quản lý sự sai sót mà trong đó mỗi yêu cầu có một yêu cầu khôi phục dùng để phục hồi trạng thái của một yêu cầu đã hoàn thành trước đó. SAGA đại diện cho một quy trình giao dịch thương mại đơn lẻ.

3. Ý nghĩa 

Giao dịch phân tán (distributed transactions) dựa trên hai giai đoạn commit không phải là một tùy chọn. Chúng ta cần áp dụng mô hình cơ sở dữ liệu theo dịch vụ (database-per-service).

4. Giải pháp 

Mô hình SAGA thực hiện các giao dịch nằm trải khắp các database vật lý bằng cách chia chúng thành các giao dịch nhỏ hơn, cùng các giao dịch khôi phục hoạt động trên từng database đơn lẻ. Mỗi giao dịch được bao đóng trong một service. Nếu một giao dịch cục bộ (local transaction) bị lỗi vì vi phạm một quy tắc giao dịch  (business rule), thì SAGA sẽ thực thi một chuỗi các giao dịch khôi phục để khôi phục lại các thay đổi đã được thực hiện bởi các giao dịch cục bộ trước đó. Mỗi hành động trong SAGA đều có một hành động khôi phục để phục hồi lại giúp đảm bảo tính nhất quán (consistency) và đúng đắn (correctness) trong toàn bộ mô hình  microservices. 

Một mô hình SAGA sẽ đảm bảo một trong hai kết quả sau đây: 

  • Hoặc tất cả các yêu cầu (requests) trong SAGA được hoàn thành thành công, 
  • Hoặc một tập hợp con các yêu cầu (subset of requests) và các yêu cầu khôi phục của chúng được thực hiện.

Để mô hình SAGA hoạt động, cả yêu cầu và yêu cầu khôi phục phải tuân thủ một số đặc điểm sau: 

  • Yêu cầu và yêu cầu khôi phục không thay đổi giá trị, vì cùng một message có thể được gửi nhiều lần. Dù message không thay đổi (idempotent message) này được gửi bao nhiêu lần, kết quả cuối cùng phải giống nhau. 
  • Yêu cầu khôi phục phải tuân theo tính hoán đổi, vì message có thể đến theo bất kỳ thứ tự nào. Trong ngữ cảnh của mô hình SAGA, có thể xảy ra trường  hợp yêu cầu khôi phục đến trước yêu cầu chính của nó. Ví dụ, nếu việc Đặt phòng khách sạn hoàn thành sau khi Hủy phòng khách sạn, chúng ta vẫn sẽ có  một đặt phòng khách sạn đã bị hủy (không tạo lại đặt phòng!). 
  • Yêu cầu chính có thể bị hủy, điều này kích hoạt một yêu cầu khôi phục. Yêu cầu khôi phục KHÔNG THỂ bị hủy, chúng phải thực hiện cho đến khi hoàn  thành. Các giao dịch khôi phục được thực hiện cho đến khi đạt được trạng thái thành công (success state). 

Các yêu cầu trong SAGA cần được gửi lại (retried) hoặc khởi tạo lại (re-issued) (ví dụ, lỗi network gây ra gián đoạn yêu cầu) và do đó, hệ thống xử lý của các yêu cầu  này (cả API hoặc Event) cần đảm bảo rằng các yêu cầu dịch vụ (service calls) sẽ không thay đổi. Trong trường hợp API call (POST / PATCH), nếu một yêu cầu giống với yêu cầu đã xảy ra trước đó, thì cần có một phản hồi (response) giống tương tự.

Điều này yêu cầu các dịch vụ chia sẻ APIs có thể: 

  • Xác định rằng một yêu cầu là yêu cầu lặp lại trước đó (repeat request). 
  • Có khả năng phản hồi đúng theo các phản hồi chính xác đã thực hiện trước đó (dữ liệu chuyển tải – data payload).
Khách hàng (Client) phải …  Nhà cung cấp (Service provider) phải …
  • Thiết lập thời gian đóng (timeout), thử lại (retry), khôi phục (compensation) 
  • Đưa ra thông tin khôi phục (compensation)
  • Thiết lập cấu hình không thay đổi (idempotency)
Các loại hình giao dịch (transactions) có thể xảy ra trong một mô hình SAGA:
Giao dịch có khả năng khôi phục (Compensatable transaction) Các giao dịch có khả năng trả lại trạng thái trước đó bằng cách sử dụng một giao dịch khôi phục.
Giao dịch then chốt (Pivot Transaction) Là điểm quyết định tiếp tục hoặc không tiếp tục (go/no-go) trong một mô hình SAGA. Nếu giao dịch then chốt hoàn tất, mô hình SAGA sẽ tiếp tục chạy cho đến khi hoàn thành.
Giao dịch có thể thử lại (Retriable Transaction) Là các giao dịch theo sau giao dịch then chốt và được đảm bảo thành công.

Mô hình SAGA có các chế độ khôi phục sau đây: 

  • Backward: nếu các giao dịch nào thất bại, chúng ta cần quay lại và hủy bỏ bất kỳ giao dịch thành công nào trước đó. 
  • Forward: chúng ta tiếp tục làm lại (retry) các giao dịch cho đến khi chúng thành công. Mô hình này hàm ý rằng một giao dịch rồi cuối cùng cũng sẽ thành  công dù có thể đòi hỏi sự can thiệp của chính chúng ta. 

SAGA có nghĩa là ACD (Atomicity – Tính Nguyên Tử, Consistency – Tính Nhất quán, Durable – Tính Bền vững), có nghĩa là chúng thiếu Tính Cô Lập (Isolation). Khi sử dụng SAGA cho các giao dịch phân tán, thiếu tính cô lập có thể gây ra ba bất lợi sau: 

  • Các cập nhật bị mất (lost updates) – Một SAGA ghi đè lên mà không cần đọc các thay đổi được thực hiện bởi SAGA khác. 
  • Các truy cập bẩn (dirty reads) – Một giao dịch hoặc SAGA đọc các cập nhật được thực hiện bởi một SAGA khác vốn chưa hoàn thành các cập nhật đó. Các truy cập không rõ ràng / không lặp lại (fuzzy/non-repeatable reads) – Hai bước khác nhau của một SAGA đọc cùng dữ liệu và ghi nhận kết quả khác  nhau do SAGA khác đã thực hiện các cập nhật trước đó. 

Cùng xem xét những giải pháp khả thi sau đối với việc xử lý các hiện tượng bất thường có thể xảy ra do thiếu tính cô lập trong kiến trúc đa tầng database của Microservices. 

  • Khóa ngữ nghĩa (Semantic lock) – Một khóa ở tầng ứng dụng (application-level lock). Điều này có thể được đạt được bằng cách thêm một chỉ dẫn cho biết bản ghi đang được cập nhật, có thể thêm *_PENDING vào trạng thái của bản ghi. 
  • Góc nhìn bi quan (Pessimistic view) – Sắp xếp lại các bước của một SAGA để giảm thiểu rủi ro thương mại. 

Đọc lại giá trị (Reread value) – Ngăn chặn việc ghi bẩn (dirty write) bằng cách đọc lại dữ liệu để xác minh rằng nó không thay đổi trước khi ghi đè lên nó. Tệp nhiều phiên bản (Version file) – Ghi nhận các cập nhật cho một bản ghi để có thể sắp xếp lại chúng. 

Theo giá trị (By value) – Sử dụng rủi ro hệ thống của mỗi yêu cầu để chủ động chọn cơ chế đồng bộ (concurrency mechanism).

Giao dịch khôi phục: 

  • Logic khôi phục không dễ tổng quát hoá với giao dịch khôi phục là ứng dụng đặc thù và phụ thuộc vào việc nó có đủ thông tin để có thể không làm các tác động xảy ra trước đó của bất kỳ thao tác nào mà nó đã hoàn thành.
  • Hạ tầng xử lý các bước trong thao tác ban đầu và giao dịch khôi phục phải được đảm bảo linh hoạt. Nó không được mất thông tin cần thiết để khôi  phục cho một bước gặp sự cố.
  • Giao dịch khôi phục không nhất thiết phải trả dữ liệu trong hệ thống về trạng thái ban đầu của nó. Thay vào đó, nó khôi phục cho công việc đã được thực hiện bởi các bước đã hoàn thành thành công trước đó khi thao tác gặp sự cố. Quá trình khôi phục trả hệ thống về trạng thái như ban đầu. Ví dụ, một hóa  đơn thẻ tín dụng có thể có một khoản phí được theo sau bởi một khoản bồi hoàn nhưng kết quả ngữ nghĩa là cùng một số dư như trước khi SAGA bắt đầu. 
  • Thứ tự của các bước trong giao dịch khôi phục không nhất thiết phải hoàn toàn ngược lại với các bước trong hoạt động ban đầu. 

Có hai cách phổ biến để phối hợp các mô hình SAGA: 

  • Orchestration – theo đó một bộ phận điều phối (orchestrator) sẽ thông báo cho những cấu phần tham gia (microservices) những hành động cần thực hiện. 
  • Choreography – theo đó mỗi microservice xuất ra các sự kiện theo từng miền (domain events) nhằm kích hoạt hành động trong các microservice khác. 

Orchestration 

Một chiến lược quản lý mô hình SAGA trong đó phần điều phối tập trung (centralized controller) thông báo cho các thành phần của SAGA về các hoạt động  (operations) cần thực hiện. 

Trong mô hình SAGA Orchestration, một bộ phận điều phối có trách nhiệm tập trung các quyết định của SAGA và sắp xếp các logic thương mại (business logic). Mỗi  giao dịch/yêu cầu trong SAGA được duy trì bởi SAGA Orchestrator. SAGA Orchestrator chịu trách nhiệm thực hiện từng giao dịch/yêu cầu phụ (sub requests /transactions) và trong trường hợp giao dịch/yêu cầu phụ nào đó thất bại, nó có thể thực hiện giao dịch/yêu cầu khôi phục tương ứng. SAGA Orchestrator phải có  khả năng tiếp tục thực hiện từ điểm dừng của nó trong SAGA trước đó bất kỳ gián đoạn nào 

SAGA Orchestrator chịu trách nhiệm cho việc: 

  • Lưu trữ và biên dịch trạng thái máy (machine state) của SAGA. 
  • Quản lý một bản ghi log SAGA bền vững. 
  • Thực hiện một chuỗi yêu cầu thông qua tương tác với các dịch vụ khác. 
  • Quản lý việc thử lại (retries) các giao dịch phụ (sub transactions) khi cần. 
  • Xử lý phục hồi lỗi (failure recovery) bằng cách thực hiện các yêu cầu khôi phục. 

core bank architecture

Một phương pháp khả dụng để theo dõi trạng thái với mô hình Orchestrated SAGA là ghi lại tất cả các giao dịch/yêu cầu đã chạy và phản hồi trong cùng một bản ghi SAGA. Mỗi giao dịch/yêu cầu trong SAGA được duy trì bởi SAGA Orchestrator. SAGA Orchestrator chịu trách nhiệm thực hiện từng giao dịch/yêu cầu phụ và  trong trường hợp giao dịch/yêu cầu phụ (Ti) nào đó thất bại, nó có thể thực hiện giao dịch/yêu cầu bù tương ứng (Ci). Tham khảo bên dưới:

core bank architecture

Trong ví dụ sau giả sử giao dịch thứ (n + 1) của một SAGA bị thất bại, kết quả của n giao dịch trước đó phải được hoàn trả lại như cũ (undoes). Theo khái niệm này,  mỗi bước trong các bước đó, Ti, có một giao dịch khôi phục tương ứng, Ci, hoàn trả lại (undoes) các kết quả của Ti. Để hoàn trả lại (undoes) kết quả của n những bước đầu tiên đó, SAGA phải thực hiện từng bước Ci theo thứ tự ngược lại. Chuỗi các bước là T1 … Tn, Cn … C1. Trong ví dụ này, Tn+1 thất bại, điều này yêu cầu hoàn trả lại (undoes) các bước T1 … Tn 

core bank architecture

Ưu điểm: 

  • Dễ bảo trì vì quy trình thương mại được mô hình hóa trong một dịch vụ tập trung (centralized service). 
  • Bộ máy xử lý trung tâm theo dõi tất cả các phiên quy trình – ví dụ: trạng thái. 
  • Các giao dịch rollback được quản lý thông qua một bộ máy xử lý quy trình tập trung. 
  • Tránh sự phụ thuộc chu kỳ (cyclic dependencies) giữa các dịch vụ. 
  • Độ phức tạp của giao dịch vẫn giữ được sự tuyến tính nếu có thêm các bước mới.  

Nhược điểm: 

  • Ràng buộc thời gian khi yêu cầu một kết nối từ điểm này đến điểm khác giữa các dịch vụ (sự đồng bộ – synchronous). 
  • Có nguy cơ tập trung quá nhiều logic trong orchestrator và cuối cùng tạo ra một kiến trúc với smart orchestrator chỉ đạo các dumb services phải làm gì. Có thể làm tăng độ trễ của hệ thống.
  • Có thể trở thành một nút thắt hiệu năng (performance bottleneck) khi tất cả các tương tác đi qua dịch vụ đó. 

Choreography 

Một chiến lược quản lý mô hình SAGA mà trong đó các thành phần trao đổi sự kiện (events) mà không có điểm kiểm soát tập trung. 

Trong mô hình SAGA choreography, không có sự phối hợp tập trung, mỗi dịch vụ tạo ra và lắng nghe các sự kiện của dịch vụ khác và quyết định xem có nên thực  hiện một hành động hay không. Giao dịch phân tán (distributed transactions) được thực hiện bằng các giao dịch cục bộ không đồng bộ trên mỗi microservice liên quan tham gia. Giao dịch phân tán kết thúc khi dịch vụ cuối cùng thực hiện giao dịch cục bộ của mình và không tạo ra bất kỳ sự kiện nào hoặc sự kiện được tạo ra không liên quan đến bất kỳ thành phần nào của SAGA. 

Nếu bất kỳ microservice nào không hoàn thành giao dịch cục bộ (local transaction) của mình, các microservice khác sẽ thực hiện các giao dịch khôi phục để trả lại (rollback) các thay đổi đã được cam kết (commit) trước đó. 

Ví dụ Event Choreography 

core bank architecture

  • Order Service lưu một đơn hàng mới, đặt trạng thái là đang chờ và công bố một event có tên là ORDER_CREATED_EVENT.
  • Payment Service lắng nghe từ event ORDER_CREATED_EVENT, thu tiền từ khách hàng và tạo ra event BILLED_ORDER_EVENT. Stock Service nghe từ event BILLED_ORDER_EVENT, cập nhật kho hàng, chuẩn bị các sản phẩm được mua trong đơn hàng và tạo ra  event ORDER_PREPARED_EVENT.
  • Delivery Service nghe từ event ORDER_PREPARED_EVENT và sau đó lấy và giao sản phẩm. Cuối cùng, nó tạo một event ORDER_DELIVERED_EVENT. Cuối cùng, Order Service nghe từ event ORDER_DELIVERED_EVENT và đặt trạng thái của đơn hàng là đã hoàn thành. 
  • Trong trường hợp trên, nếu cần theo dõi trạng thái của đơn hàng, Order Service có thể đơn giản lắng nghe tất cả các sự kiện và cập nhật trạng thái của  mình. 

Hoạt động phục hồi event choreography trong giao dịch phân tán  

  1. Stock Service tạo sự kiện PRODUCT_OUT_OF_STOCK_EVENT.
  2. Cả Order Service và Payment Service lắng nghe sự kiện trên: 
  • Payment Service hoàn tiền cho khách hàng. 
  • Order Service đặt trạng thái đơn hàng là thất bại. 

Áp dụng mô hình choreography đồng nghĩa với việc một dịch vụ không giao tiếp trực tiếp với dịch vụ khác để hướng dẫn một hành động. Thay vào đó, mỗi dịch vụ  quan sát môi trường của nó và thực hiện hành động dựa trên các sự kiện một cách độc lập. 

Ưu điểm: 

  • Hỗ trợ giao dịch phân tán kéo dài vì mỗi microservice chỉ tập trung vào giao dịch nguyên tử cục bộ (local atomic) của nó và các microservice khác không bị  chặn nếu một microservice chạy trong thời gian dài. 
  • Loại bỏ nhu cầu có một orchestrator thông minh. 
  • Tăng tính khả dụng của dịch vụ luôn chạy 

Nhược điểm: 

  • Việc giám sát và tra lỗi trở nên khó khăn hơn, đặc biệt khi có nhiều microservice tham gia. 
  • Sự thay đổi trong quy trình thương mại có thể dẫn đến sự thay đổi trong nhiều dịch vụ khác nhau. 
  • Chỉ hỗ trợ một quy trình thương mại duy nhất – điều này có tác động đáng kể nếu ví dụ như bạn chỉ muốn gửi hóa đơn tới khách hàng doanh nghiệp sau  khi đơn hàng được gửi đi. 
  • Yêu cầu phát triển các giao dịch khôi phục để undo các thay đổi đã thực hiện trước đó trong SAGA. 
  • Có thể trở nên khó khăn để duy trì nếu hệ thống phức tạp và có nhiều microservice tham gia. Cũng khó để theo dõi xem dịch vụ nào lắng nghe sự kiện của  dịch vụ nào và có thể tạo ra sự phụ thuộc vòng lặp giữa các dịch vụ nếu các dịch vụ phải theo dõi các sự kiện của nhau. 

Choreography với Observer 

Một nhược điểm của phương pháp choreography là các dịch vụ có thể kết thúc với kiến thức ngầm về quy trình cấp cao được tích hợp vào dịch vụ. Trong ví dụ trên,  Order Service và Payment Service phải nhận thức về PRODUCT_OUT_OF_STOCK_EVENT và thực hiện giao dịch khôi phục khi nhìn thấy sự kiện này. Phương pháp  này gây ra các lo ngại về cấp độ quy trình trong việc triển khai của các dịch vụ riêng lẻ. 

Độ phức tạp của phương pháp này có thể nhanh chóng trở nên quá tải đối với các trường hợp sử dụng thực tế. Bằng cách tạo ra một Observer vào việc triển khai,  chúng ta có thể thiết kế một giải pháp chia sẻ một số lợi ích của cả hai phương pháp này. Trong phương pháp này, một phương thức quan sát bên ngoài lắng nghe  các sự kiện từ tất cả các dịch vụ. Bất kỳ logic kinh doanh liên quan đến hoàn thiện quy trình hoặc bồi hoàn được thực hiện trong Observer, để nó hiểu được một  giao dịch hoàn chỉnh và không có lỗi trông như thế nào và nó có thể kích hoạt một phản ứng trong trường hợp gặp sự cố như: 

  • Gửi thông báo sự kiện cho một người để cảnh báo về một vấn đề cần được khắc phục, hoặc 
  • Khởi tạo các giao dịch khôi phục để rollback các tác động của quy trình. 

Chúng ta có thể tái thiết lập ví dụ trên vào biểu đồ sau:

core bank architecture

Kiểu thực hiện của mô hình SAGA này mang lại một số lợi ích của cả phương pháp choreography và orchestration: 

  • Quy trình chính (sunny-day flow) được thiết kế theo kiểu choreography mà không yêu cầu sự ràng buộc chặt chẽ và các dịch vụ phải hiểu vị trí của chúng  trong quy trình làm việc tổng thể. 
  • Observer hoạt động như một điểm giám sát để hiểu trạng thái hiện tại của một SAGA. Logic kinh doanh cấp độ quy trình được chứa trong thành phần này  và vì nó đang quan sát, có ít khả năng nó trở thành một “god object”. 
  • Việc khôi phục (compensation) có thể được kích hoạt bởi observer thông qua orchestration hoặc choreography (kích hoạt một “sự kiện rollback”) hoặc có  thể được chuyển giao cho con người khi các trường hợp ngoại lệ này hiếm gặp và không đáng tự động hóa. 
  • Nó cung cấp một sự cân đối giữa độ phức tạp và số lượng khôi phục (compensation) cần thiết nếu xảy ra sự cố.

5. Những điểm lưu ý khi lựa chọn 

  • Hãy suy nghĩ một cách cẩn thận xem trường hợp sử dụng của bạn có đòi hỏi một kết quả hoàn toàn thành công hoặc thất bại hay không. Hầu hết các quy trình kinh doanh trong thực tế thường được xử lý tốt hơn thông qua một số phần tái thử hoặc phục hồi. 
  • Logic phục hồi có thể phức tạp. Hãy xem xét phục hồi theo con người khi chi phí hoặc tần suất thấp. 
  • Chỉ tự động hóa phục hồi khi chi phí xứng đáng và bạn không thể loại bỏ nguyên nhân gốc của sự cố quy trình.

6. Kết luận 

Ưu tiên sử dụng sự kết hợp giữa choreography với observer hơn là chỉ sử dụng thuần choreography vì nó kết hợp những ưu điểm tốt nhất của orchestration và choreography.

Cân nhắc sử dụng orchestration trong các trường hợp: 

  • Khi thứ tự các bước xử lý thay đổi thường xuyên hoặc cho phép nhiều luồng quy trình kinh doanh khác nhau. 
  • Trong phạm vi của dịch vụ (service boundaries). 
  • Khi tích hợp với các hệ thống của bên thứ ba. Tránh sử dụng thuần choreography vì nó có xu hướng xây dựng kiến thức về quy trình vào từng dịch vụ cụ thể, từ  đó hạn chế khả năng thay đổi và kết hợp.

 


Thuộc dự án Inside GemTechnology do TopDev hợp tác cùng Techcombank triển khai, chuỗi nội dung thuần “Tech” độc quyền được chia sẻ bởi đội ngũ chuyên gia Công nghệ & Dữ liệu tại Techcombank sẽ được cập nhật liên tục tại chuyên mục Tech Blog | Techcombank Careers x TopDev. Cùng theo dõi & gặp gỡ các chuyên gia bạn nhé!

 

Các cơ hội việc làm tại Techcombank


Bài viết liên quan

Nguyên tắc thiết kế về Component Cohesion trong kiến trúc phần mềm (Principles of Component Cohesion in Software Architectures)

Bài viết đến từ anh Nguyễn Ngọc Hải - Quản lý cao cấp Kiến trúc Giải pháp Enterprise Architect team @Techcombank Tổng quan Trong quy trình phát triển phần mềm, các nguyên tắc về thiết kế component là không thể thiếu để có một hệ thống được tổ chức quy củ, dễ mở rộng và dễ quản lý. Components trong bài dùng để chỉ đến các thành phần nhỏ nhất, thuộc về một hệ thống, có thể triển khai được một cách tương đối độc lập với các component khác, có thể là micro-service, hoặc library, package, v.v.. Các nguyên tắc thiết kế dưới đây giúp hướng dẫn developer và kiến trúc sư đặt class nào vào component nào (component cohesion), và những component đó liên quan gì đến nhau (component coupling). Việc có các nguyên tắc thiết kế đặc biệt quan trở nên quan trọng khi các hệ thống phần mềm phát triển ngày một lớn và phức tạp. Lúc này chỉ riêng việc quyết định đặ [...]