Skip to content

Pub/Sub Storage Model và Message Storage Policies

Why this matters in production

Khi làm việc với các hệ thống phân tán quy mô lớn, tính bền vững của dữ liệu (durability) và khả năng sẵn sàng (availability) thường là những yếu tố được đưa lên hàng đầu. Nhiều kỹ sư khi sử dụng Google Cloud Pub/Sub thường có một giả định sai lầm rằng: "Vì Pub/Sub là một dịch vụ toàn cầu (global service), mọi message tôi gửi lên sẽ tự động được sao chép và đồng bộ hóa trên toàn thế giới để sẵn sàng chống lỗi vùng (regional failover)."

Thực tế hoàn toàn khác. Mặc dù các thành phần giao tiếp đầu cuối (Publish/Subscribe control planes) của Pub/Sub hoạt động trên phạm vi toàn cầu, dữ liệu thông điệp (message payload) vật lý chỉ được ghi và lưu trữ tại một vùng duy nhất (single region) tại một thời điểm.

Nếu vùng lưu trữ đó gặp sự cố hoàn toàn (full regional outage), toàn bộ các thông điệp chưa được xử lý (backlog) nằm tại vùng đó sẽ bị cô lập và không thể truy cập được cho đến khi vùng đó được khôi phục. Việc không hiểu rõ mô hình lưu trữ này sẽ dẫn tới:

  • Thiết kế hệ thống không có khả năng chống lỗi vùng thực sự, gây gián đoạn dịch vụ khi có thảm họa thiên tai hoặc sự cố diện rộng tại trung tâm dữ liệu.
  • Vi phạm các chính sách tuân thủ dữ liệu nghiêm ngặt (như GDPR ở Châu Âu hoặc HIPAA ở Mỹ) do để Pub/Sub tự do lưu trữ dữ liệu tại các vùng không được phép.
  • Không thể tối ưu hóa độ trễ (latency) của quá trình Publish/Subscribe do định tuyến mạng chéo vùng không cần thiết.

Internal Model: Cơ Chế Lưu Trữ Vật Lý Của Pub/Sub

Để xây dựng một mental model chính xác về cách Pub/Sub lưu trữ dữ liệu, trước hết ta phải bóc tách kiến trúc phân tầng bên dưới của dịch vụ này. Pub/Sub được xây dựng trên ba thành phần chính: Publishers/Subscribers (Client), Google Front Ends (GFE), và hệ thống điều phối nội bộ gồm Forwarders (Publishers)Routers (Subscribers) kết hợp với Storage Nodes.

mermaid
graph TD
    Client[Publisher Client] -->|1. Route request| GFE[Google Front End]
    GFE -->|2. Forward| Forwarder[Pub/Sub Forwarder]
    Forwarder -->|3. Route & Replicate| StorageNode[Storage Nodes - Region A]
    subgraph Region A [Region A - Selected Storage]
        StorageNode -->|Sync Write| Zone1[(Zone 1 Colossus)]
        StorageNode -->|Sync Write| Zone2[(Zone 2 Colossus)]
        StorageNode -.->|Best-effort| Zone3[(Zone 3 Colossus)]
    end
    Router[Pub/Sub Router] -->|Read Message| StorageNode
    Router -->|Deliver| SubscriberClient[Subscriber Client]

1. Luồng Ghi Dữ Liệu (Publish Path) và Cơ Chế Chọn Region

Khi một Publisher gửi một message tới Global Endpoint (pubsub.googleapis.com), luồng xử lý diễn ra như sau:

  1. Định tuyến Biên (Edge Routing): Request đi qua mạng trục của Google (Google Global Network Backbone) và đi vào điểm PoP (Point of Presence) gần nhất. Google Front End (GFE) sẽ nhận request này và chuyển hướng đến một Pub/Sub Forwarder nằm trong một vùng khả dụng gần nhất có độ trễ mạng thấp nhất.
  2. Xác Định Vùng Lưu Trữ (Storage Region Selection): Forwarder sau khi nhận message sẽ kiểm tra Message Storage Policy của Topic để xác định xem những region nào được phép lưu trữ dữ liệu.
    • Nếu không có chính sách đặc biệt nào được định nghĩa, Pub/Sub sẽ chọn region gần nhất với Publisher (tương ứng với region nơi Forwarder đang chạy) để tối ưu hóa độ trễ ghi dữ liệu (write latency).
    • Nếu chính sách giới hạn vùng lưu trữ được áp dụng, Forwarder sẽ chọn region gần nhất nằm trong danh sách các region được phép (allowed regions).
  3. Lưu Trữ Đồng Bộ Hóa Đa Zone (Synchronous Zonal Replication): Đây là bước cốt lõi để đảm bảo độ bền vững của dữ liệu. Một khi region lưu trữ đã được chọn (ví dụ: asia-east1), Pub/Sub sẽ ghi thông điệp vào hệ thống lưu trữ phân tán Colossus (hệ thống file thế hệ mới của Google).
    • Message sẽ được ghi đồng bộ (synchronously) vào ít nhất hai zone khác nhau trong region đó trước khi trả về kết quả thành công (HTTP 200 OK hoặc Publish OK) cho client.
    • Pub/Sub cũng sẽ cố gắng ghi thêm vào một zone thứ ba theo cơ chế nỗ lực tối đa (best-effort asynchronous/opportunistic write).
    • Điều này đảm bảo rằng ngay cả khi một zone bị sập hoàn toàn (ví dụ: do mất điện vật lý), dữ liệu vẫn an toàn và không bị mất mát nhờ zone còn lại đã ghi nhận thành công.

2. Message Storage Policies (MSP) Hoạt Động Như Thế Nào Bên Trong?

Message Storage Policy là một thuộc tính cấu hình ở tầng Topic. Nó cho phép bạn khai báo danh sách các GCP regions được phép lưu trữ dữ liệu.

Khi Forwarder nhận được message, nó sẽ thực hiện thuật toán định tuyến lưu trữ:

[Publisher] ──(Publish Request)──> [Forwarder (Region X)]

                         Is Region X allowed by Topic's MSP?
                                  /            \
                               Yes              No
                               /                  \
   [Ghi trực tiếp vào Region X]                    [Chuyển tiếp đến Region Y gần nhất thuộc MSP]
   (Độ trễ tối thiểu)                              (Độ trễ tăng do chéo vùng)

Nếu một Publisher nằm ở vùng europe-west1 xuất bản tin nhắn lên một Topic có Message Storage Policy chỉ cho phép vùng us-central1us-east1:

  1. Message sẽ đi vào GFE tại châu Âu và chuyển đến Forwarder tại europe-west1.
  2. Forwarder phát hiện rằng europe-west1 không có trong danh sách được phép lưu trữ.
  3. Forwarder sẽ chuyển tiếp (in-transit forwarding) message này qua mạng backbone của Google đến các Storage Nodes tại vùng gần nhất được phép, trong trường hợp này là us-east1.
  4. Message được ghi đồng bộ trên các zone của us-east1.
  5. Sau khi ghi thành công, tín hiệu ACK ghi dữ liệu sẽ truyền ngược lại châu Âu và phản hồi về cho Publisher. Lúc này, độ trễ ghi sẽ tăng lên đáng kể do khoảng cách địa lý chéo đại dương (cross-Atlantic network round-trip).

3. Ràng Buộc Dữ Liệu Di Chuyển (In-Transit Encryption and Routing)

Một điểm đặc biệt trong cơ chế hoạt động của Pub/Sub là sự phân biệt giữa dữ liệu tĩnh (data-at-rest)dữ liệu di chuyển (data-in-transit):

  • Theo mặc định, dữ liệu đi qua mạng Google luôn được mã hóa ở tầng vận chuyển (transit encryption) bằng TLS và ALTS (Application Layer Transport Security).
  • Nếu thuộc tính enforceInTransit trong Message Storage Policy được thiết lập là true, Pub/Sub cam kết rằng toàn bộ luồng dữ liệu (data flow) từ lúc đi vào hệ thống cho đến khi được lưu trữ tĩnh sẽ chỉ đi qua các ranh giới mạng nằm trong các region được phép. Nếu client cố gắng gửi request từ một region bị cấm, request đó có thể bị từ chối thẳng thừng để đảm bảo tính tuân thủ pháp lý cao nhất.

Constraints, Trade-offs & Failure Modes

Mặc dù kiến trúc lưu trữ của Pub/Sub rất mạnh mẽ, nó vẫn có những giới hạn vật lý và các kịch bản lỗi mà các kiến trúc sư hệ thống cần phải biết:

1. Không Có Sao Chép Dữ Liệu Chéo Vùng Tự Động (No Automatic Cross-Region Storage Replication)

Pub/Sub không sao chép dữ liệu thông điệp tĩnh chéo vùng. Nếu bạn xuất bản một message và nó được lưu tại us-central1, bản sao của message đó chỉ tồn tại trong các zone của us-central1.

  • Lý do thiết kế: Việc sao chép đồng bộ chéo vùng (cross-region synchronous replication) sẽ đẩy độ trễ ghi dữ liệu lên mức không thể chấp nhận được đối với một hệ thống tin nhắn thời gian thực (real-time messaging system), đồng thời nhân đôi chi phí tài nguyên lưu trữ và băng thông mạng chéo vùng (egress cost).
  • Hệ quả khi xảy ra lỗi vùng (Regional Outage): Nếu toàn bộ vùng us-central1 bị mất kết nối mạng hoặc sập nguồn hoàn toàn:
    • Tất cả các message đã được lưu trữ thành công tại us-central1 nhưng chưa được Subscriber xác nhận (ACK) sẽ bị kẹt (locked).
    • Các Subscriber nằm ở các vùng khác (ví dụ: europe-west3 hoặc asia-east1) kết nối vào Subscription đó sẽ không thể đọc được các message đang kẹt này.
    • Khi vùng us-central1 khôi phục hoạt động, các message này mới khả dụng trở lại để tiêu thụ. Dữ liệu không bị mất (nhờ cơ chế lưu trữ bền vững đa zone trên Colossus), nhưng độ khả dụng (availability) bị gián đoạn.

2. Đánh Đổi Giữa Tuân Thủ Dữ Liệu (Compliance) Và Hiệu Năng (Latency)

Việc áp đặt Message Storage Policy chặt chẽ (chỉ lưu trữ tại một vài quốc gia cụ thể để phục vụ luật cư trú dữ liệu) tạo ra một sự đánh đổi lớn về mặt hiệu năng hệ thống:

  • Nếu Publisher, Pub/Sub Topic, và Subscriber không đồng vị trí địa lý (co-located) trong cùng một region, độ trễ mạng sẽ tăng lên tuyến tính theo khoảng cách địa lý.
  • Bạn phải chấp nhận trả thêm chi phí mạng (network egress pricing) khi dữ liệu được chuyển tiếp chéo vùng giữa các hạ tầng Google Cloud.

Production Patterns & Architecture Designs

Để giải quyết triệt để vấn đề chịu lỗi vùng trong production khi sử dụng Pub/Sub, các kỹ sư thường áp dụng một trong hai mô hình kiến trúc dưới đây.

1. Mô Hình Active-Active Multi-Region Publishing

Đây là mô hình thiết kế tối ưu nhất để đạt được tính sẵn sàng cực cao (High Availability), có khả năng sống sót qua sự cố sập hoàn toàn một region của Google Cloud.

                    [Client Traffic]
                    /              \
         (Publish Region A)    (Publish Region B)
                  /                  \
        [Topic Region A]       [Topic Region B]
               |                      |
     [Subscription Region A] [Subscription Region B]
                  \                  /
         [Idempotent Consumer ở cả 2 Regions]
  • Cấu hình: Tạo hai Pub/Sub Topics độc lập ở hai vùng khác nhau (ví dụ: topic-us-east1 tại vùng us-east1topic-us-central1 tại vùng us-central1).
  • Phía Publisher: Client sẽ gửi tin nhắn đến cả hai topics. Bạn có thể sử dụng giải pháp xuất bản song song (publish to both) hoặc cơ chế dự phòng phía client (client-side failover): nếu ghi vào topic-us-east1 bị lỗi timeout hoặc trả về lỗi 5xx, tự động chuyển hướng ghi sang topic-us-central1.
  • Phía Subscriber: Consumer chạy song song ở cả hai vùng và lắng nghe từ cả hai Subscriptions tương ứng.
  • Ràng buộc quan trọng: Hệ thống tiêu thụ phía sau (downstream database/service) phải được thiết kế có khả năng chống trùng lặp (deduplication) và xử lý idempotent, vì tin nhắn có thể bị gửi lặp lại ở cả hai vùng trong quá trình chuyển đổi dự phòng.

2. Cấu Hình Khai Báo Message Storage Policy Bằng Terraform

Để đảm bảo các quy định về an toàn thông tin và định vị dữ liệu, bạn nên cấu hình cứng Message Storage Policy ngay từ bước định nghĩa hạ tầng bằng code (IaC). Dưới đây là snippet Terraform mẫu để cấu hình một Topic chỉ lưu trữ dữ liệu tại khu vực Châu Âu (europe-west1europe-west3):

hcl
resource "google_pubsub_topic" "europe_only_topic" {
  name    = "production-eu-events"
  project = "my-production-project"

  # Cấu hình Message Storage Policy
  message_storage_policy {
    allowed_persistence_regions = [
      "europe-west1",
      "europe-west3"
    ]
  }

  labels = {
    environment = "production"
    compliance  = "gdpr"
  }
}

Nếu bạn muốn cấu hình chính sách này ở cấp độ tổ chức để áp dụng chung cho tất cả các dự án con, bạn có thể sử dụng Organization Policy với constraint constraints/gcp.resourceLocations để giới hạn các vùng mà tài nguyên lưu trữ (bao gồm cả Pub/Sub) được phép khởi tạo.


Common Mistakes & Anti-Patterns

1. Phụ Thuộc Hoàn Toàn Vào Global Endpoint Cho Các Hệ Thống Yêu Cầu Chịu Lỗi Vùng Tuyệt Đối

Mô tả: Sử dụng duy nhất endpoint pubsub.googleapis.com và một topic duy nhất mà không có cơ chế dự phòng phía client. Khi vùng chứa dữ liệu vật lý của topic đó bị sập, toàn bộ ứng dụng bị tê liệt. Cách khắc phục: Triển khai kiến trúc Active-Active Multi-Region hoặc xây dựng cơ chế chuyển đổi dự phòng phía client sang một backup topic nằm ở region khác.

2. Bỏ Qua Cấu Hình Timeout Ghi Dữ Liệu Khi Sử Dụng Message Storage Policy

Mô tả: Khi cấu hình Message Storage Policy giới hạn vùng lưu trữ ở xa Publisher (ví dụ: Publisher chạy tại Tokyo nhưng chỉ được ghi dữ liệu tại Mỹ), độ trễ ghi tăng từ ~10ms lên ~150ms. Nếu client library cấu hình timeout quá thấp (ví dụ: default write timeout 100ms), Publisher sẽ liên tục gặp lỗi timeout giả lập mặc dù hệ thống bên dưới vẫn ghi nhận thành công sau đó. Cách khắc phục: Đo lường và điều chỉnh giá trị PublishTimeout của client library phù hợp với khoảng cách địa lý quy định trong Message Storage Policy.


References