Đọc dữ liệu theo thời gian thực bằng luồng thay đổi

Tính năng Luồng thay đổi cho Firestore có khả năng tương thích với MongoDB cho phép các ứng dụng truy cập vào các thay đổi theo thời gian thực (chèn, cập nhật và xoá) được thực hiện đối với một tập hợp hoặc toàn bộ cơ sở dữ liệu. Một luồng thay đổi sẽ sắp xếp các bản cập nhật theo thời gian sửa đổi.

Bạn có thể truy cập vào Luồng thay đổi thông qua các API tương thích với MongoDB và Trình điều khiển MongoDB truyền thống. Phương thức triển khai Change Streams của Firestore có khả năng tương thích với MongoDB có thể xử lý mọi thông lượng ghi và đọc thông qua một phương thức triển khai duy nhất về phân vùng tự động trên các hoạt động ghi và đọc song song. Điều này cho phép bạn xây dựng khối lượng công việc có thông lượng cao. Bạn cũng có thể cải thiện cơ sở hạ tầng di chuyển và đồng bộ hoá dữ liệu giữa Cloud Firestore và các giải pháp lưu trữ khác.

Ngoài khả năng tương thích với trình điều khiển MongoDB, bạn có thể sử dụng Cloud Firestore để đọc Luồng thay đổi song song. Điều này cho phép bạn xây dựng khối lượng công việc đọc song song, có công suất cao. Mỗi luồng đại diện cho một phân vùng kết quả được phân phối hợp lý.

Luồng thay đổi hỗ trợ các tính năng sau:

  • Luồng thay đổi có thể định cấu hình với phạm vi cơ sở dữ liệu hoặc tập hợp.
  • Thời gian lưu giữ cho một luồng thay đổi được chỉ định khi tạo. Thời gian lưu giữ mặc định là 7 ngày và thời gian lưu giữ tối thiểu là 1 ngày. Thời gian lưu giữ phải là bội số của 1 ngày, tối đa là 7 ngày. Bạn không thể thay đổi thời gian lưu giữ sau khi tạo. Để thay đổi khoảng thời gian lưu giữ, bạn phải bỏ và tạo lại luồng thay đổi.
  • delete, insert, update và drop thay đổi các sự kiện có thể quan sát được bằng cách sử dụng db.collection.watch() và db.watch().
  • updateDescription.updatedFields chứa các điểm khác biệt của bản cập nhật.
  • Tất cả lựa chọn fullDocument và fullDocumentBeforeChange.
    • Đang tra cứu toàn bộ tài liệu để tìm thông tin cập nhật.
    • Hình ảnh trước của tài liệu trước khi tài liệu đó được thay thế, cập nhật hoặc xoá.
    • Hình ảnh sau của tài liệu sau khi được thay thế hoặc cập nhật.
    • Hình ảnh trước và sau khi thay đổi dữ liệu cũ hơn một giờ yêu cầu bật tính năng khôi phục tại thời điểm cụ thể (PITR).
  • Tất cả các lựa chọn tiếp tục, bao gồm cả resumeAfter và startAfter.
  • Khi sử dụng watch() để quan sát các thay đổi, bạn có thể liên kết các giai đoạn tổng hợp như $addFields, $match, $project, $replaceRoot, $replaceWith, $set và $unset.

Định cấu hình luồng thay đổi

Để tạo, loại bỏ hoặc xem các Luồng thay đổi hiện có cho một cơ sở dữ liệu, hãy sử dụng Google Cloud Console.

Vai trò và quyền

Để tạo, xoá và liệt kê Luồng thay đổi, một tài khoản chính cần có các quyền datastore.schemas.create, datastore.schemas.delete và datastore.schemas.list trong dịch vụ Quản lý danh tính và quyền truy cập (IAM).

Ví dụ: vai trò Quản trị viên chỉ mục kho dữ liệu (roles/datastore.indexAdmin) cấp các quyền này.

Tạo luồng thay đổi

Trước khi có thể mở con trỏ luồng thay đổi tương ứng, bạn phải tạo một luồng thay đổi. Không hỗ trợ tính năng tự động bật luồng thay đổi khi tạo tập hợp hoặc cơ sở dữ liệu.

Để tạo một luồng thay đổi, hãy sử dụng Google Cloud Console.

  1. Trong Google Cloud Console, hãy chuyển đến trang Databases (Cơ sở dữ liệu).

    Chuyển đến phần Cơ sở dữ liệu

  2. Trong danh sách, hãy chọn một cơ sở dữ liệu Firestore có khả năng tương thích với MongoDB. Bảng điều khiển Firestore Studio sẽ mở ra.
  3. Trong bảng điều khiển Explorer (Trình khám phá), hãy tìm nút Change streams (Luồng thay đổi), nhấp vào More actions (Thao tác khác) rồi chọn Create change stream (Tạo luồng thay đổi).
  4. Nhập tên, phạm vi và khoảng thời gian lưu giữ riêng biệt cho luồng thay đổi, sau đó nhấp vào Lưu

Xem luồng thay đổi

Bạn có thể xem thông tin chi tiết về Luồng thay đổi trong Google Cloud Console.

  1. Trong Google Cloud Console, hãy chuyển đến trang Databases (Cơ sở dữ liệu).

    Chuyển đến phần Cơ sở dữ liệu

  2. Trong danh sách, hãy chọn một cơ sở dữ liệu Firestore có khả năng tương thích với MongoDB. Bảng điều khiển Firestore Studio sẽ mở ra.
  3. Trong bảng điều khiển Explorer (Trình khám phá), hãy tìm nút Change streams (Thay đổi luồng).
  4. Để mở hoặc đóng nút, hãy nhấp vào biểu tượng Bật/tắt nút.

Xoá luồng thay đổi

Để xoá một luồng thay đổi, hãy sử dụng Google Cloud Console.

  1. Trong Google Cloud Console, hãy chuyển đến trang Databases (Cơ sở dữ liệu).

    Chuyển đến phần Cơ sở dữ liệu

  2. Trong danh sách, hãy chọn một cơ sở dữ liệu Firestore có khả năng tương thích với MongoDB. Bảng điều khiển Firestore Studio sẽ mở ra.
  3. Trong bảng điều khiển Explorer (Trình khám phá), hãy tìm nút Change streams (Thay đổi luồng).
  4. Để mở hoặc đóng nút, hãy nhấp vào biểu tượng Bật/tắt nút.
  5. Trong Explorer (Trình khám phá), hãy tìm luồng thay đổi mà bạn muốn xoá.
  6. Nhấp vào biểu tượng Thao tác khác rồi chọn Xoá luồng thay đổi.
  7. Trong hộp thoại, hãy nhập tên luồng thay đổi để xác nhận việc xoá, sau đó nhấp vào Xoá.

Mở hoặc tiếp tục con trỏ luồng thay đổi

Các ví dụ sau đây minh hoạ cách tạo, tiếp tục và định cấu hình con trỏ luồng thay đổi.

Trước khi tạo con trỏ luồng thay đổi, bạn phải tạo một luồng thay đổi cho cơ sở dữ liệu hoặc tập hợp một cách rõ ràng.

Tạo con trỏ luồng thay đổi

Để tạo con trỏ luồng thay đổi mới, hãy sử dụng phương thức watch trong trình điều khiển MongoDB. Để theo dõi tất cả thay đổi trên một cơ sở dữ liệu, hãy tạo một luồng thay đổi trong phạm vi cơ sở dữ liệu và gọi phương thức watch trên đối tượng db.

let cursor = db.watch()

Để tạo một con trỏ trong phạm vi của một tập hợp, trước tiên, bạn phải tạo một luồng thay đổi cho tập hợp đó. Sau đó, hãy gọi phương thức watch trên tập hợp tương ứng.

let cursor = db.my_collection.watch()

Giờ đây, khi đã tạo con trỏ luồng thay đổi, bạn có thể bắt đầu truyền trực tuyến. Ví dụ: nếu bạn chèn một tài liệu và gọi tryNext trên con trỏ, bạn sẽ thấy thay đổi xuất hiện trên luồng thay đổi.

let doc = db.my_collection.insertOne({value: "hello world"})
console.log(cursor.tryNext())

Nếu cập nhật và xoá tài liệu, thì bạn sẽ thấy những thay đổi đó xuất hiện trên luồng thay đổi:

db.my_collection.updateOne({"_id": doc.insertedId}, {$set: {value: "hello world!"}})
db.my_collection.deleteOne({"_id": doc.insertedId}})

// Prints the update event
console.log(cursor.tryNext())

// Prints the delete event
console.log(cursor.tryNext())

Tiếp tục một luồng thay đổi

Để tiếp tục một luồng thay đổi, hãy sử dụng các tuỳ chọn resumeAfter hoặc startAfter. Để xác định vị trí trong nhật ký thay đổi cần tiếp tục từ resumeAfter và startAfter, hãy sử dụng mã thông báo tiếp tục.

// Create a cursor and add one event to the change stream.
let cursor = db.my_collection.watch();
db.my_collection.insertOne({value: "hello world"});
let event = cursor.tryNext();

// Get the resume token from the event.
let resumeToken = event._id;

// Add a new event to the change stream.
db.my_collection.insertOne({value: "foobar"});

// Create a new cursor by using the resume token as a starting point.
let newCursor = db.my_collection.watch({resumeAfter: resumeToken})

// Log the change event containing the "foobar" value.
console.log(newCursor.tryNext())

Cách sử dụng startAfter:

// Start after the resume token.
let startAfterCursor = db.my_collection.watch({startAfter: resumeToken})

Thêm hình ảnh trước và sau trong các thao tác cập nhật và xoá

Nếu cần, bạn có thể thêm hình ảnh trước và sau của tài liệu trong các sự kiện thay đổi về việc cập nhật và xoá. Hình ảnh chỉ có thể truy cập được trong khoảng thời gian khôi phục về một thời điểm (PITR). Để đọc hình ảnh tài liệu cũ hơn một giờ, bạn phải bật PITR.

Luồng thay đổi tận dụng khoảng thời gian PITR để cung cấp chế độ xem tài liệu trước và sau sự kiện thay đổi nhất định. Theo mặc định, các sự kiện cập nhật chứa một trường updateDescription là delta của các trường được sửa đổi bằng thao tác cập nhật.

Để đưa hình ảnh trước và sau vào một sự kiện thay đổi, bạn phải chỉ định các tuỳ chọn fullDocumentBeforeChange và fullDocument trong truy vấn luồng thay đổi.

let cursor = db.my_collection.watch({
  "fullDocument": "required",
  "fullDocumentBeforeChange": "required"
})

Nếu truy vấn cố gắng đọc một tài liệu nằm ngoài khoảng thời gian lưu giữ PITR hoặc nếu PITR không được bật, thì giá trị required sẽ gửi thông báo lỗi phía máy chủ.

Thay vì gửi lỗi, bạn có thể sử dụng giá trị whenAvailable để trả về giá trị null nếu hình ảnh không còn nữa.

let cursor = db.my_collection.watch({
  "fullDocument": "whenAvailable",
  "fullDocumentBeforeChange": "whenAvailable"
})

Đưa hình ảnh hiện tại vào bản cập nhật

Theo mặc định, các sự kiện cập nhật chứa một trường updateDescription là delta của các trường được sửa đổi bằng thao tác cập nhật. Để tra cứu phiên bản mới nhất của toàn bộ tài liệu, hãy sử dụng giá trị updateLookup trong tuỳ chọn fullDocument.

Tính năng này không yêu cầu PITR và thực hiện tra cứu tài liệu.

let cursor = db.my_collection.watch({
  "fullDocument": "updateLookup",
})

Đọc song song

Để tăng công suất, bạn có thể sử dụng tuỳ chọn firestoreWorkerConfig để phân tách một truy vấn luồng thay đổi trên nhiều worker. Mỗi worker chịu trách nhiệm phân phát các thay đổi cho một nhóm tài liệu riêng biệt. Bạn phải tạo một con trỏ song song thông qua truy vấn runCommand hoặc aggregate.

Ví dụ: bạn có thể phân phối một luồng thay đổi cho 3 worker như sau:

let cursor1 = db.my_collection.aggregate([{
    "$changeStream": {
        "firestoreWorkerConfig": {numWorkers: 3, workerId: 0 }}
  }]);

let cursor2 = db.my_collection.aggregate([{
    "$changeStream": {
        "firestoreWorkerConfig": {numWorkers: 3, workerId: 1 }}
  }]);

let cursor3 = db.my_collection.aggregate([{
    "$changeStream": {
        "firestoreWorkerConfig": {numWorkers: 3, workerId: 2 }}
  }]);

Thay đổi sự kiện phát trực tiếp và bản sao lưu

Cả cấu hình luồng thay đổi và dữ liệu luồng thay đổi đều không có sẵn trong các thao tác khôi phục bản sao lưu. Nếu khôi phục cơ sở dữ liệu bằng Luồng thay đổi, bạn phải tạo lại các luồng thay đổi đó trong cơ sở dữ liệu đích để mở con trỏ đến cơ sở dữ liệu đó.

Thanh toán

Sự khác biệt về hành vi

Phần sau đây mô tả sự khác biệt về Luồng thay đổi giữa Firestore có khả năng tương thích với MongoDB và MongoDB.

updateDescription

updateDescription là một tài liệu trong sự kiện update mô tả các trường đã được cập nhật hoặc xoá bằng thao tác cập nhật. Trong Cloud Firestore, những điểm khác biệt đáng chú ý là:

  • Trong updateDescription, các trường truncatedArrays và disambiguatedPaths không được điền sẵn.
  • updateDescription.updatedFields đại diện cho sự khác biệt chuẩn giữa hình ảnh trước và sau của một tài liệu trước và sau khi áp dụng đột biến.

Hãy xem xét trạng thái ban đầu sau đây của một tài liệu:

db.my_collection.insertOne({
  _id: 1,
  root: {
    array: [{a: 1}, {b: 2}, {c: 3}]
  }
})

Trường hợp 1: chỉ thay đổi phần tử đầu tiên của mảng.

Trong trường hợp này, hành vi của Cloud Firestore khớp với MongoDB.

db.my_collection.updateOne(
  {_id: 1},
  {'$set': {"root.array.0.a": 100}}
)

{
  updatedFields: {"root.array.0.a": 100},
  removedFields: []
}

Trường hợp 2: ghi đè bằng toàn bộ mảng

Trong trường hợp này, thao tác chỉ cập nhật trường mảng đầu tiên nhưng ghi đè toàn bộ mảng.

Sự khác biệt về bản cập nhật Cloud Firestore không phân biệt giữa hai trường hợp này và trả về cùng một updateDescription.updatedFields cho cả hai:

db.my_collection.updateOne(
  {_id: 1},
  {'$set': {"root.array": [{a: 100}, {b: 2}, {c: 3}]}}
)

// In other implementations, updatedFields reflects the mutation itself
{
  updatedFields: {
    "root.array": [{a: 100}, {b: 2}, {c: 3}]
  },
  removedFields: []
}

// Firestore updatedFields is the diff between the before and after versions of the document
{
  updatedFields: {"root.array.0.a": 100},
  removedFields: []
}

Bước tiếp theo