Cái Đêm Máy Tính Của Tôi Tự Khởi Động Lại
Một đêm nọ, giữa lúc đang chạy một tác vụ dài, cả cái máy tính của tôi tự tắt rồi bật trở lại. Không phải chương trình — mà là cả cỗ máy. Một khoảnh khắc, một tiến trình đang chạy; khoảnh khắc kế tiếp, tôi đang nhìn vào một màn hình đăng nhập mới tinh, như thể ai đó vừa rút phích cắm điện ra rồi cắm lại.
Tôi đã bỏ ra nhiều ngày để cẩn thận với cái tài nguyên duy nhất mà tôi chắc chắn là quan trọng. Sự cố lại đến từ cái tài nguyên mà tôi chưa bao giờ nghĩ đến việc phải để mắt tới.
Sự cố không có thông điệp nào
Phần đáng bất an nhất là có quá ít thứ để đọc. Không có stack trace, không có ngoại lệ, không có dòng log nào đánh dấu sự kết thúc. Chỉ có một cú khởi động lại đột ngột và, vùi sâu trong chính những bản ghi của hệ điều hành, là một thứ tương đương kỹ thuật số của cái nhún vai: hệ thống đã khởi động lại mà không tắt một cách sạch sẽ.
Một lỗi bình thường để lại một cái xác. Lỗi này để lại một căn phòng trống. Và một lỗi không có thông điệp tự bản thân nó đã là một loại manh mối — nó thường có nghĩa là sự cố đã xảy ra ở đâu đó bên dưới chương trình của bạn, trong một tầng không dừng lại để giải thích trước khi nó kéo mọi thứ đổ sập theo.
Tôi đã hoạch định nhầm bộ nhớ
Tôi đang chạy một mẻ công việc rất lớn trên GPU, và tôi đã định cỡ mẻ đó một cách cẩn thận — dựa trên bộ nhớ của GPU. Tôi biết card có bao nhiêu VRAM, tôi biết đại khái mỗi đơn vị công việc sẽ tốn bao nhiêu, và tôi đã làm phép tính để giữ an toàn dưới mức trần. Theo như tôi nghĩ, bộ nhớ là một bài toán đã được giải.
Nó đã được giải cho nhầm bộ nhớ. VRAM của card vẫn ổn suốt cả thời gian đó; nếu tôi để mắt vào đồng hồ đo của nó vào đúng khoảnh khắc xảy ra sự cố, nó hẳn đã trông hoàn toàn bình thản. Mức trần mà tôi thực sự đụng phải là RAM hệ thống của cỗ máy chủ — bộ nhớ thông thường mà phần còn lại của máy tính chạy trên đó — và tôi đã hoàn toàn không hoạch định cho nó, bởi vì trong mô hình tư duy của tôi, đơn giản là nó không phải là ràng buộc.
RAM của máy chủ đã đi đâu
Gửi một mẻ công việc khổng lồ tới GPU không phải là miễn phí ở phía máy chủ. Trước khi GPU có thể chạy bất cứ thứ gì, công việc phải được chuẩn bị, thiết lập, và bàn giao qua — và sự chuẩn bị đó nằm trong RAM hệ thống. Mỗi mảnh của khâu thiết lập đó, tự bản thân nó, đều khiêm tốn. Nhân lên trên một mẻ đủ lớn để bão hòa GPU, những mảnh khiêm tốn ấy chồng chất thành một thứ khổng lồ.
RAM hệ thống đầy. Hệ điều hành làm cái việc nó vẫn làm khi bộ nhớ cạn kiệt — nó tràn ra đĩa, rồi cũng thiếu luôn cả cái đó. Áp lực bộ nhớ nghiêm trọng không phải lúc nào cũng tự thông báo bằng một ngoại lệ có thể bắt được. Nó có thể có nghĩa là một lần cấp phát thất bại, một tiến trình bị giết, hoặc cả một cỗ máy trở nên mất ổn định. Máy của tôi đã mất ổn định đủ mạnh để khởi động lại.
Vì sao tôi không bao giờ thấy nó sắp đến
Điều bực bội khi nhìn lại là tôi đã giám sát cẩn thận — chỉ là tôi đang giám sát nhầm đồng hồ đo. Mắt tôi dán vào GPU, cái tài nguyên mà tôi đã tối ưu, đã lo lắng về nó và cảm thấy mình thông minh. Suốt cả thời gian đó, áp lực đang dồn lên ở một nơi mà tôi chẳng có lý do gì để nhìn vào, bởi vì tôi đã quyết định sẵn rằng đó không phải là nơi vấn đề sẽ nằm.
Đó là một khuôn mẫu mà giờ đây tôi đã thấy quá nhiều lần để có thể gọi nó là sự trùng hợp: bạn đo đạc cái ràng buộc mà bạn đang nghĩ tới, và bạn trở nên mù lòa trước cái mà bạn không nghĩ tới. Lỗi không hề lẩn trốn. Nó ngồi ngay trong một điểm mù do chính bạn tạo ra, bằng cách tự tin về việc giới hạn nằm ở đâu.
Cách khắc phục: hãy chờ đợi bức tường, và bật ra khỏi nó
Sửa chữa không phải là một phép tính tốt hơn. Nó là việc từ bỏ ý tưởng rằng tôi có thể tính toán trước kích cỡ an toàn ngay từ đầu. Thay vì dự đoán chính xác cỗ máy có thể sống sót với một mẻ lớn cỡ nào, tôi để cho cỗ máy nói cho tôi biết: thử chạy mẻ đó, và nếu nó đụng phải một lỗi bộ nhớ có thể phục hồi, hãy coi đó là một tín hiệu để lùi lại — giảm khối lượng công việc một cách thận trọng và thử lại, co nhỏ dần cho đến khi nó vừa.
Đó là một vòng lặp nhỏ bé và khiêm tốn, và nó vững vàng hơn nhiều so với phép tính cẩn thận của tôi từng có, chính bởi vì nó không phụ thuộc vào việc tôi đã hiểu được mọi chi phí ẩn từ trước. Tôi không còn cần phải biết chính xác bức tường nằm ở đâu; đối với các lỗi bộ nhớ có thể phục hồi, tiến trình có thể tự mò mẫm tới một kích cỡ khả thi trên bất kỳ phần cứng nào nó chạy. Sự cạn kiệt ở mức máy chủ như cái đêm hôm đó là trường hợp khó hơn — nó vẫn cần các giới hạn và sự giám sát từ bên ngoài, chứ không chỉ một vòng lặp thử lại.
Các giả định về tài nguyên im lặng cho đến khi chúng không còn im lặng nữa
Bài học mà tôi giữ lại chẳng liên quan gì nhiều đến GPU một cách cụ thể. Đó là các giả định về tài nguyên vốn vô hình cho đến đúng cái khoảnh khắc chúng trở nên thảm khốc. Bạn có thể hoàn toàn đúng về cái giới hạn mà bạn đang hình dung và vẫn bị hạ gục bởi cái giới hạn mà bạn không hình dung — và sự cố, khi nó đến, thường ập tới mà không có lời giải thích nào, bởi vì cái tầng bị vỡ nằm bên dưới cái tầng nơi các thông điệp lỗi của bạn cư trú.
Vậy nên tôi đã ngừng cố gắng chính xác về tài nguyên và bắt đầu cố gắng phòng thủ trước chúng. Sự chính xác giả định rằng bạn biết hết mọi chi phí. Sự phòng thủ giả định rằng bạn đã bỏ sót ít nhất một chi phí. Trong một hệ thống được thiết kế để chạy hàng giờ, không có người trông, trên bộ nhớ mà bạn không kiểm soát hoàn toàn, giả định thứ hai là giả định an toàn duy nhất.
Một chương trình bị sập để lại cho bạn một thông điệp. Một cỗ máy khởi động lại để lại cho bạn một sự im lặng. Tôi đã dần coi sự im lặng đó là một trong những điều thành thật hơn cả mà phần cứng của tôi từng nói với tôi: bạn đã hoạch định nhầm thứ rồi.
— Không tín hiệu, không lợi nhuận, không phải lời khuyên đầu tư.