Khi máy khởi động thành công nhưng phản hồi biến mất: Bài toán nhất quán trong hệ thống backend cấp phát máy ảo

07 tháng 10, 2026·20 phút đọc

Một lệnh tạo máy ảo Linux có thể chạy thành công nhưng phản hồi lại không bao giờ đến được client, khiến người dùng tưởng rằng yêu cầu chưa từng được gửi. Bài viết phân tích cách Mainbrella thiết kế backend để xử lý những khoảng trống thông tin này thông qua cơ chế đặt chỗ bền vững, khóa idempotency, số hiệu reservation và các ranh giới nhất quán giữa account, runtime và nhà cung cấp hạ tầng.

Khi máy khởi động thành công nhưng phản hồi biến mất: Bài toán nhất quán trong hệ thống backend cấp phát máy ảo

Bạn yêu cầu một máy chủ tạo ra một máy ảo Linux. Máy khởi động thành công. Nhưng phản hồi lại biến mất. Từ phía client, tình huống này trông chẳng khác gì một yêu cầu chưa từng đến được máy chủ. Thử lại là hợp lý. Nhưng khởi động thêm một máy thứ hai sẽ khiến chi phí khắc phục cao hơn cả sự cố ban đầu.

Đây là một cách tiếp cận thú vị để hiểu về backend của Mainbrella. Chỉ cần theo dõi một máy ảo đủ xa, ta sẽ thấy cùng một vấn đề lặp đi lặp lại: một lệnh có thể chạy xong mà người gọi không hề thấy kết quả; một yêu cầu riêng tư có thể tồn tại lâu hơn cả tư cách thành viên mạng; một thao tác chụp đĩa có thể thành công nhưng lại không để lại cho ta "tay nắm" cần thiết để khôi phục nó. Mỗi ranh giới đều cần một quyết định về việc thao tác thử lại được phép làm gì.

Minh họa luồng xử lý admission trong backendMinh họa luồng xử lý admission trong backend

Hãy theo dõi một tác vụ minh họa chạy analysis.py, đọc dữ liệu từ một container khác và ghi ra metrics.csv. Máy của nó chiếm slot c17. Tên tệp và slot chỉ là ví dụ; các cơ chế bên dưới đều đến từ backend mã nguồn mở. Ta sẽ bắt đầu trước khi Linux khởi động, vì đó là nơi quyền sở hữu và ngân sách phải được quyết định.

Một nơi duy nhất quyết định ai được slot cuối cùng

Hai yêu cầu tạo máy có thể đến cùng lúc khi tài khoản chỉ còn một slot trống. Nếu chỉ đọc một biến đếm, khởi động máy rồi cập nhật lại biến đếm, cả hai yêu cầu đều có cơ hội thắng. Đến khi ta nhận ra vấn đề, phần tốn kém nhất đã xảy ra xong.

Mainbrella cấp cho mỗi tài khoản một Cloudflare Durable Object – một coordinator bền vững có bộ nhớ riêng. API công khai xác thực người dùng và quyền lợi đã trả tiền, sau đó địa chỉ hóa coordinator đó thành account:. Mỗi slot máy có một Durable Object riêng. Với c17, tên của nó là user::slot:17. Slot đầu tiên vẫn giữ tên cũ user: và ID công khai nhỏ gọn; ID đó không dùng để chọn kích thước máy.

Coordinator tài khoản chịu trách nhiệm tuần tự hóa quá trình admission. Một yêu cầu phải đặt trước slot, số lần khởi động trong tháng và hạn mức tính toán trước khi được phép yêu cầu runtime khởi động máy. Do đó, yêu cầu tiếp theo sẽ thấy dung lượng đã bị chiếm ngay cả khi máy đầu tiên vẫn đang khởi động. Bộ điều khiển tài khoản triển khai hàng đợi một cách tường minh bằng promise tail; một lệnh await bên trong quyết định admission không cho phép quyết định tiếp theo chen ngang.

Giữ khóa này cho đến khi mọi máy sẵn sàng sẽ vô tình tuần tự hóa toàn bộ thời gian khởi động. Vì vậy hệ thống giải phóng khóa sau khi đặt chỗ bền vững (durable reservation) và thực hiện provision bên ngoài khóa. Một tài khoản ra quyết định theo thứ tự; nhưng các runtime của nó có thể khởi động song song.

Quyết định chia sẻ ngắn gọn đứng trước, công việc độc lập dài hơi đứng sau. Thời gian ở đây chỉ mang tính sơ đồ: các thanh chồng lấn minh họa tính đồng thời, không phải đo độ trễ khởi động thực tế.

Có một bài kiểm thử đặc biệt hữu ích cho sự phân biệt này. Nó gửi sáu yêu cầu đồng thời vào một tài khoản Builder có năm slot, và giữ mọi bước kiểm tra sẵn sàng của runtime sau một cổng chặn. Năm máy đạt trạng thái khởi động trước khi cổng mở. Yêu cầu thứ sáu nhận về lỗi xung đột, và tài khoản ghi nhận đúng năm lần khởi động. Bài test kiểm tra cả hai nửa của thiết kế: admission độc quyền và provision song song.

Quyền sở hữu được thiết lập trước khi quá trình phối hợp này bắt đầu. Adapter xác thực chấp nhận API key hoặc session đăng nhập. Một thông tin Bearer không hợp lệ rõ ràng sẽ thất bại ngay cả khi đi kèm cookie trình duyệt hợp lệ. Bộ tạo yêu cầu nội bộ tự cung cấp danh tính tài khoản và quyền lợi. Để người gọi tự chọn header x-mainbrella-user sẽ phá hủy chính ranh giới tài khoản mà ta vừa xây dựng.

Biên nhận tồn tại trước cả khi máy ra đời

Với tác vụ analysis.py, client cung cấp một Idempotency-Key kèm theo POST /containers. Khóa đó mang ý nghĩa "nỗ lực cụ thể này để tạo một máy". Tài khoản ghi lại slot và reservation thuộc về nó, cùng với dấu vân tay (fingerprint) của cấu hình được yêu cầu. Thay đổi image, kích thước hoặc bất kỳ lựa chọn nào được ghi vân tay trong khi tái sử dụng cùng khóa sẽ tạo ra xung đột.

Thao tác ghi quan trọng thì rất nhỏ. Đây là phần cốt lõi của container-account-core.js, đã lược bỏ phần kiểm tra xung quanh:

const reservationId = ++state.nextReservationId;
state.reservations[slot] = reservationId;
const creation = {
  id: crypto.randomUUID(), slot, reservationId, fingerprint,
  expiresAt: this.now() + CREATION_RETENTION_MS,
};
await this.ctx.storage.put({
  [KEY]: state,
  [CREATION_PREFIX + idempotencyKey]: creation,
});

Thao tác ghi nhiều khóa đó cam kết cả reservation bị tính phí lẫn biên nhận tạo máy một cách nguyên tử. Ta không muốn có biên nhận cho một slot chưa từng được đặt trước, cũng không muốn một slot đã bị tính phí mà thao tác thử lại có khóa lại không tìm thấy. Chỉ sau đó bộ điều khiển mới phát lệnh khởi động.

Biên nhận được lưu bền vững trước khi máy khởi độngBiên nhận được lưu bền vững trước khi máy khởi động

Giờ hãy giả sử phản hồi bị mất. Một thao tác thử lại khớp khóa sẽ tìm thấy biên nhận trước khi cố gắng admission mới. Trong lúc reservation đang chờ xử lý, nó có thể trả về trạng thái starting. Reservation đang chờ có cửa sổ đối soát chín mươi giây; sau đó, coordinator hỏi thẳng runtime xem thực tế đang tồn tại gì. Nếu tìm thấy máy đang chạy, nó trả về máy đó mà không tính thêm một lần khởi động nữa. Thao tác thử lại không bao giờ phát lại reservation mơ hồ lần thứ hai.

Biên nhận tồn tại trong hai mươi bốn giờ. Nếu máy đã dừng hoặc slot đã bị tái sử dụng, cùng khóa được lưu giữ đó sẽ trả về creation_no_longer_running. Nó không âm thầm khởi động một máy thay thế. Client muốn có máy thay thế phải thực hiện nỗ lực tạo mới với một khóa mới. Đây cũng là lý do client nên lưu khóa và yêu cầu trước khi gửi chúng: cơ chế chống trùng lặp phía máy chủ chẳng giúp ích gì nếu client quên mất danh tính mà nó cần để hỏi lại.

Vậy điều gì xác lập trạng thái sẵn sàng? Bộ điều khiển runtime khởi động image đã chọn với sleep infinity làm entrypoint, sau đó thực thi uname -a. Lệnh phải thoát thành công trong vòng sáu mươi giây. Điều này xác nhận rằng guest có thể thực thi một lệnh. Nhưng tác vụ phân tích Python của ta vẫn cần các phụ thuộc và bước kiểm tra riêng; một kernel biết trả lời không thể chứng nhận một phép tính tài chính.

Thông điệp của hôm qua có thể đến máy của ngày mai

Giả sử lệnh dispatch cho lần khởi động đầu tiên bị trễ. Trong lúc đó, tài khoản hủy reservation, giải phóng c17 và gán slot đó cho một máy thay thế. Lệnh dispatch cũ cuối cùng vẫn đến. Đích của nó vẫn là cùng một đối tượng runtime. Việc tra cứu slot theo tên không thể cho biết liệu thông điệp này có quyền khởi động thứ gì hay không.

Mỗi lần khởi động được chấp nhận nhận một số hiệu reservation tăng dần. Tại runtime, hệ thống lưu bền vững hai mốc nước cao (high-water mark): lần khởi động mới nhất được chấp nhận và lần hủy mới nhất. Một lệnh khởi động có số hiệu bằng hoặc thấp hơn một trong hai mốc này sẽ bị từ chối. Thao tác hủy ghi "hàng rào" (fence) của nó ngay cả khi không có guest nào đang chạy để tiêu hủy, nhờ đó một thông điệp đến muộn không thể hồi sinh công việc đã bị hủy.

Hàng rào hủy ngăn thông điệp cũ hồi sinh công việc đã hủyHàng rào hủy ngăn thông điệp cũ hồi sinh công việc đã hủy

Chiều ngược lại cũng quan trọng không kém. Một lệnh dọn dẹp trễ cho reservation 41 không được phép dừng máy của reservation 42. Đường dẫn DELETE của runtime tiến mốc hủy nhưng để guest yên khi lệnh hủy cũ hơn lần khởi động được chấp nhận. Quay lại phía tài khoản, việc hoàn tất một lần khởi động vẫn phải khớp với reservation slot hiện tại trước khi nó có thể xóa trạng thái đang chờ. Một phản hồi thành công cũ cũng không có quyền gì đối với một máy thay thế.

Số hiệu reservation bảo vệ các thông điệp vòng đời nội bộ. Các lệnh công khai và yêu cầu tệp nhận diện một thế hệ đang chạy bằng { id, createdAt }. ID slot có thể tái sử dụng; thế hệ thì không. Dù được biểu diễn như một dấu thời gian, createdAt thực chất được tính bằng max(now, previousCreatedAt + 1). Việc tạo lại máy trong cùng một mili-giây, hay đồng hồ hệ thống chạy ngược, vẫn cho guest mới một danh tính khác biệt.

Hãy giữ cả hai giá trị từ container đang chạy được trả về. Một yêu cầu dọn dẹp chỉ ghi c17 không thể diễn đạt ý bạn muốn nói đến vòng đời nào. Các bài kiểm thử vòng đời cố tình dừng và tái sử dụng một slot trước khi phát lệnh khởi động cũ, kể cả khi không dịch chuyển đồng hồ. Đây là phép kiểm tra có sức gợi mở hơn hẳn một lần chạy hello-world thành công khác.

Hạn cứng là một phần của admission

Máy của ta cũng cần quyền tiếp tục tiêu thụ tài nguyên tính toán. Một biến đếm theo tháng chỉ được kiểm tra lúc khởi động sẽ cho phép nhiều máy đồng thời tiêu dùng cùng một hạn mức còn lại. Vì vậy hệ thống đặt trước lượng runtime mà chúng có thể dùng trước khi bất kỳ máy nào khởi động.

Các kích thước máy có trọng số trong plan-policy.js: Lite dùng một đơn vị tính toán, Medium dùng mười, và XL dùng hai mươi tám. Tài khoản đặt trước theo đơn vị mili-giây tính toán. Hợp đồng thuê của nó kết thúc tại mốc sớm nhất trong bốn ranh giới:

hard deadline = min(
  start time + plan session limit,
  paid access expiration,
  next UTC month boundary,
  start time + remaining unit-ms / machine weight
)

Ví dụ bằng số: đặt trước một máy Medium trong một giờ cam kết mười đơn vị-giờ tính toán. Nếu xác nhận dừng sau năm phút, lượng tiêu thụ là 10 × 5 / 60, tức khoảng 0,833 đơn vị-giờ; phần đặt trước chưa dùng được giải phóng. Một lệnh dừng thất bại hoặc runtime không đọc được sẽ giữ nguyên reservation của nó. Coi "không liên lạc được" là "chắc chắn đã rảnh" sẽ cho phép tài khoản tiêu hạn mức đó hai lần. Một lần khởi động được chấp nhận nhưng thất bại vẫn tiêu tốn lượt khởi động trong tháng; việc quyết toán runtime là một phép tính riêng.

Runtime lưu bền vững hạn cứng này cùng một hạn rảnh (idle deadline). Hoạt động thực có thể đẩy hạn rảnh ra xa, nhưng bị chặn trần bởi hạn cứng. Việc polling trạng thái thì không. Một alarm của Durable Object thực thi việc hết hạn ngay cả khi client đã biến mất. Thay đổi gói dịch vụ có thể rút ngắn vòng đời hiện có, nhưng không thể kéo dài hạn cứng ban đầu của nó.

Việc dừng công việc phải luôn khả thi ngay cả khi tra cứu thanh toán không sẵn sàng. Đường dẫn DELETE công khai xác thực quyền sở hữu mà không cần phân giải thanh toán mới; coordinator dùng quyền lợi đã lưu của mình để dọn dẹp khi nó còn hiệu lực. Tương tự, một quan sát chưa thanh toán mới hơn sẽ đánh bại một quan sát đã thanh toán cũ hơn. Nếu không, một bước kiểm tra bị trễ có thể tái cấp quyền cho một máy mà ta đã thu hồi.

Người xem bị ngắt kết nối không nên sở hữu tiến trình

Với một thế hệ đang chạy trong tay, ta có thể khởi động analysis.py. Một lệnh foreground ngắn có mức trần sáu mươi giây. Với công việc mà ta muốn tìm lại kết quả sau khi ngắt kết nối, ta dùng một managed execution – tiến trình được quản lý. Định danh của nó thuộc về một thế hệ container cụ thể, và việc tạo nó đòi hỏi một khóa idempotency riêng.

// machine là giá trị { id, createdAt } chính xác được trả về cho guest đang chạy.
// Lưu executionKey và yêu cầu này trước khi gửi.
const query = new URLSearchParams(machine);
const response = await fetch(`${apiOrigin}/containers/executions?${query}`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
    'Idempotency-Key': executionKey,
  },
  body: JSON.stringify({
    argv: ['python3', '/workspace/analysis.py'],
    timeoutMs: 120_000,
  }),
});
if (!response.ok) throw new Error(`Execution HTTP ${response.status}`);
const execution = await response.json(); // Lưu execution.id.

Dạng argv cung cấp các đối số nguyên văn thay vì lắp ghép văn bản shell. Trong executions.js, bản ghi execution được lưu trước khi tiến trình khởi chạy. Một thao tác thử lại khớp khóa sẽ tìm thấy danh tính được lưu giữ đó. Một yêu cầu tạo bị hủy giữa chừng hay một luồng sự kiện bị ngắt không tự nhiên có quyền giết tiến trình được quản lý; chỉ lệnh hủy tường minh mới có.

Các sự kiện đầu ra có số thứ tự tăng dần, được ghi cam kết cùng với con trỏ cập nhật của bản ghi. Client kết nối lại với endpoint sự kiện bằng số thứ tự đã xử lý cuối cùng làm con trỏ. Nó đọc phần hậu tố đã lưu thay vì phụ thuộc vào việc một socket cụ thể đã thấy mọi byte. Bản thân luồng bị giới hạn trong ba mươi giây, nên việc kết nối lại là hoạt động bình thường. Tiến trình có thể chạy tối đa mười lăm phút, tiếp tục bị giới hạn bởi timeout được yêu cầu và hợp đồng thuê cứng còn lại của container.

Những bản ghi này là tài nguyên hữu hạn. Mỗi runtime lưu tối đa ba mươi hai bản ghi execution, với hạn lưu giữ một giờ tính từ lúc job được nhận. Các job được quản lý chia sẻ một nhóm bốn thao tác đồng thời với lệnh foreground và truyền tệp. Đầu ra bị giới hạn ở một MiB và một số lượng sự kiện hữu hạn. Chạm giới hạn đầu ra sẽ kết thúc job và đánh dấu nó là bị cắt ngắn (truncated); do đó việc tìm thấy vài dòng stdout trông hợp lý là chưa đủ. Ta phải kiểm tra trạng thái kết thúc, mã thoát, timeout và cờ cắt ngắn trước khi tin vào kết quả.

Đây là ranh giới khó chịu: bản ghi bền vững không làm cho "tay nắm" tiến trình trở nên bền vững. Khi một đối tượng runtime khởi động lại, quá trình phục hồi đánh dấu các execution chưa hoàn tất là bị gián đoạn. Nếu chúng thuộc thế hệ guest hiện tại của nó, nó tiêu hủy guest đó thay vì để công việc không được theo dõi tiếp tục chạy. Nó không bao giờ âm thầm chạy lại lệnh. Với tác vụ phân tích của ta, điều này nghĩa là một gián đoạn có thể hủy bỏ một tệp CSV chưa lưu; với một lệnh gửi hóa đơn, việc tự động phát lại có thể gây ra hậu quả tồi tệ hơn. Phục hồi sau khởi động lại được thiết kế cố ý gây gián đoạn hơn việc kết nối lại một người xem.

Yêu cầu dữ liệu đến với một danh tính mà guest không thể tự chọn

Giả sử analysis.py đọc một shard từ http://data.internal/shards/west. Ta đăng ký thế hệ của nó làm thành viên của một mạng dịch vụ riêng tư. Trong cùng mạng đó, một thế hệ khác sở hữu tên dịch vụ data và cổng 8080. Nguồn có thể chỉ là thành viên chỉ-gọi, không có cổng lắng nghe.

Yêu cầu từ guest không nêu tên tài khoản lẫn mạng. private-services-runtime.js cài đặt một bộ chặn HTTP ra ngoài cho *.internal. Bộ chuyển tiếp của nó loại bỏ các header danh tính dành riêng và cung cấp giá trị tài khoản, slot và thế hệ đáng tin cậy từ các thuộc tính được cấu hình của entrypoint runtime. Gửi một header sở hữu bịa đặt từ Python không chọn được một khách hàng khác.

Sổ đăng ký dịch vụ riêng của tài khoản tìm mạng chứa đúng thế hệ nguồn đó, rồi phân giải data bên trong nó. Một tài khoản khác – hoặc một mạng khác trong cùng tài khoản này – có thể tái sử dụng tên. Đích đến kiểm tra lại thế hệ đang chạy và đăng ký của nó dưới khóa vòng đời trước khi mở cổng ứng dụng đã đăng ký. Phân giải tên chỉ là bước kiểm tra quyền đầu tiên.

Tại sao phải kiểm tra lại? Dịch vụ data của ta có thể bị tách ra hoặc thay thế trong lúc nó đang trả lời. Đích đến đệm phản hồi trong giới hạn cho phép rồi kiểm tra lại đăng ký của mình. Tài khoản kiểm tra lại tính sống của nguồn và cả hai tư cách thành viên trước khi giải phóng phản hồi. Một yêu cầu được nhận dưới tư cách thành viên của hôm qua không được phép chuyển byte dưới sự sắp đặt của hôm nay.

Những bước kiểm tra đó không thể hoàn tác một tác dụng phụ ở tầng ứng dụng đã xảy ra. Nếu yêu cầu đã thay đổi dịch vụ dữ liệu trước khi phản hồi bị từ chối, ứng dụng vẫn cần một cách để đối soát thay đổi đó. Đọc shard bất biến thì dễ xử lý; một hàng đợi tác vụ hay dịch vụ thanh toán sẽ cần danh tính thao tác riêng.

Tính năng này là định tuyến HTTP riêng tư có giới hạn: thân yêu cầu và phản hồi tối đa một MiB, timeout mười giây, không hỗ trợ nâng cấp WebSocket hay kết nối TCP tùy ý. Một client PostgreSQL sẽ không tự nhiên trở nên nhận biết dịch vụ riêng chỉ vì hostname của nó kết thúc bằng .internal. Việc kích hoạt triển khai và năng lực được quảng cáo qua GET /capabilities cũng phải hiện diện trước khi ta xây dựng một quy trình dựa trên nó.

Tệp CSV cần một cuộc sống ngoài đầu ra của lệnh

Tác vụ của ta ghi /workspace/metrics.csv. Ta lấy nó về bằng một yêu cầu tệp gắn với thế hệ trong khi guest vẫn đang chạy. Endpoint tệp truyền byte thô, với giới hạn một MiB, thay vì giải mã dữ liệu nhị phân thành văn bản hay giấu một bản xuất trong luồng stdout đã bị cắt ngắn.

Đường dẫn ghi trong files.js thể hiện một lựa chọn thứ tự hữu ích khác. Nó ghi một tệp tạm trong thư mục cha hiện có của đích đến, rồi đổi tên nó đè lên mục tiêu. Người đọc không nên nhìn thấy một tệp thông thường mới tải lên dở dang. Đường dẫn được truyền như một đối số vị trí, không được nội suy vào mã shell. Thao tác ghi từ chối đích là liên kết tượng trưng (symlink); thao tác đọc có thể đi theo liên kết bên trong guest thuộc sở hữu.

Những bảo đảm này hẹp hơn một giao dịch bao trùm toàn bộ tác vụ phân tích. Một lần truyền tệp thành công không chứng minh job đã dùng đúng đầu vào hay hoàn tất mọi dòng. Ứng dụng nên kiểm tra lược đồ và nguồn gốc của artifact, và sao chép đầu ra hữu ích ra ngoài máy tạm trước khi nó bị tiêu hủy. Nếu muốn quay lại chính môi trường đó, ta cần một workspace được lưu.

Một snapshot có thể tồn tại mà vẫn không thể khôi phục

Lưu lại nghe như một thao tác duy nhất: chụp đĩa, ghi nhớ kết quả, dừng máy. Thực tế nó băng qua ba chủ thể nắm trạng thái – tài khoản, runtime và nhà cung cấp – và không chủ thể nào có thể cam kết nguyên tử thay cho hai chủ thể còn lại.

Trong workspaces.js, tài khoản đặt trước dung lượng lưu và lưu bền vững thao tác trước khi yêu cầu chụp. Tại runtime, một biên nhận chứa danh tính lần lưu được lưu bền vững trước khi gọi snapshotContainer(). Sau khi thao tác chụp trả về, runtime lưu "tay nắm" của nhà cung cấp vào biên nhận đó. Tài khoản sau đó ghi cam kết "tay nắm" vào bản ghi workspace của mình. Chỉ sau lần cam kết đó thì stop: true mới được phép tiêu hủy nguồn.

Nếu phản hồi giữa runtime và tài khoản bị mất sau khi runtime đã lưu "tay nắm", một thao tác thử lại có thể đọc biên nhận. Ta phục hồi đúng lần chụp cũ mà không phải chụp thêm lần nữa. Nhưng nếu runtime bị gián đoạn sau khi nhà cung cấp đã chấp nhận thao tác chụp và trước khi "tay nắm" của nó được lưu, biên nhận chỉ còn chứa một ý định. Ta biết mình đã thử; ta không biết đối tượng nào của nhà cung cấp cần khôi phục.

Vị trí của thông tin bị mất quyết định cách phục hồi. Chỉ có đĩa phía nhà cung cấp là chưa đủ: ta cần "tay nắm" của nó ở phía bên này của ranh giới bền vững.

Runtime từ chối chụp lại thao tác chưa được giải quyết đó và trả về workspace_save_unavailable. Đường dẫn lưu để nguyên nguồn, tùy thuộc hợp đồng thuê thông thường của nó. Chọn một khóa mới chỉ để lỗi biến mất sẽ là một nỗ lực chụp mới, chứ không phải phục hồi lần chụp cũ. Sự phân biệt đó chính là giới hạn của lời hứa idempotency.

Hạn ngạch lưu cũng tính đến chi phí của sự mơ hồ. Admission đặt trước toàn bộ dung lượng đĩa theo kích thước nguồn trước khi chụp. Xóa một workspace giải phóng hạn ngạch workspace đã lưu đang tồn tại, nhưng không hoàn lại ngân sách chụp trong lịch sử: nhà cung cấp có thể đã thực hiện xong công việc. Một lệnh gọi thất bại hay mơ hồ không thể trở thành cách rẻ tiền để lặp lại việc chụp vô hạn.

Khôi phục một workspace sẵn sàng đi qua quy trình admission container thông thường và tiêu tốn một lượt khởi động mới. Guest được khôi phục nhận một thế hệ mới và phải dùng kích thước đã lưu cùng chính sách internet. Digest của image vẫn phải khớp; một image không tương thích hay việc khôi phục phía nhà cung cấp thất bại sẽ tạo ra lỗi thay vì âm thầm thay thế bằng một máy trống.

Trạng thái được lưu là một hệ thống tệp. RAM, tiến trình đang chạy, bản xem trước và tư cách thành viên dịch vụ riêng đều không quay trở lại cùng nó. Tác vụ Python của ta cần lưu tiến độ vào tệp nếu muốn tiếp tục, và một dịch vụ được khôi phục phải tự khởi động tiến trình và đ

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗