TL; DR: Nếu nhân Linux mất ghi I / O được đệm , có cách nào để ứng dụng tìm ra không?
Tôi biết bạn phải fsync()
tập tin (và thư mục mẹ của nó) cho độ bền . Câu hỏi đặt ra là nếu kernel mất bộ đệm bẩn đang chờ ghi do lỗi I / O, làm thế nào để ứng dụng có thể phát hiện ra điều này và phục hồi hoặc hủy bỏ?
Hãy nghĩ rằng các ứng dụng cơ sở dữ liệu, vv, trong đó thứ tự ghi và viết độ bền có thể rất quan trọng.
Mất viết? Làm sao?
Trong một số trường hợp, lớp khối của hạt nhân Linux có thể bị mất các yêu cầu I / O được đệm đã được gửi thành công write()
, pwrite()
v.v., với một lỗi như:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(Xem end_buffer_write_sync(...)
và end_buffer_async_write(...)
trongfs/buffer.c
).
Trên các hạt nhân mới hơn, thay vào đó, lỗi sẽ chứa "ghi trang không đồng bộ bị mất" , như:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Vì ứng dụng write()
sẽ quay trở lại mà không có lỗi, nên dường như không có cách nào để báo cáo lỗi về ứng dụng.
Phát hiện chúng?
Tôi không quen thuộc với các nguồn kernel, nhưng tôi nghĩ rằng nó đặt AS_EIO
trên bộ đệm không được ghi ra nếu nó thực hiện ghi async:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
nhưng nó không rõ ràng với tôi nếu hoặc làm thế nào ứng dụng có thể tìm hiểu về điều này khi nó fsync()
là tệp để xác nhận nó trên đĩa.
Dường như wait_on_page_writeback_range(...)
trongmm/filemap.c
sức bởi do_sync_mapping_range(...)
trongfs/sync.c
đó là rẽ gọi bằng sys_sync_file_range(...)
. Nó trả về -EIO
nếu một hoặc nhiều bộ đệm không thể được viết.
Nếu, như tôi đoán, điều này lan truyền đến fsync()
kết quả, sau đó nếu ứng dụng hoảng loạn và giải cứu nếu nó bị lỗi I / O fsync()
và biết cách thực hiện lại công việc của nó khi được khởi động lại, điều đó có đủ an toàn không?
Có lẽ không có cách nào để ứng dụng biết được byte nào trong tệp tương ứng với các trang bị mất để nó có thể viết lại chúng nếu biết, nhưng nếu ứng dụng lặp lại tất cả công việc đang chờ xử lý của nó kể từ lần thành công cuối cùng fsync()
của tệp và điều đó viết lại bất kỳ bộ đệm kernel bẩn nào tương ứng với ghi bị mất đối với tệp, sẽ xóa mọi cờ lỗi I / O trên các trang bị mất và cho phép tiếp theo fsync()
hoàn thành - phải không?
Sau đó, có trường hợp nào khác, vô hại, nơi fsync()
có thể trở lại -EIO
nơi bảo lãnh và làm lại công việc sẽ quá quyết liệt?
Tại sao?
Tất nhiên lỗi như vậy không nên xảy ra. Trong trường hợp này, lỗi phát sinh từ sự tương tác đáng tiếc giữa dm-multipath
mặc định của trình điều khiển và mã cảm giác được SAN sử dụng để báo cáo lỗi không phân bổ dung lượng lưu trữ được cung cấp mỏng. Nhưng đây không phải là trường hợp duy nhất có thể xảy ra - tôi cũng đã thấy các báo cáo về nó từ LVM được cung cấp mỏng chẳng hạn, như được sử dụng bởi libvirt, Docker, v.v. Một ứng dụng quan trọng như cơ sở dữ liệu nên cố gắng đối phó với các lỗi như vậy, thay vì mù quáng tiếp tục như thể tất cả đều ổn.
Nếu kernel nghĩ rằng không sao để mất ghi mà không chết với kernel, thì các ứng dụng phải tìm cách đối phó.
Tác động thực tế là tôi đã tìm thấy một trường hợp trong đó một vấn đề đa luồng với SAN gây ra mất ghi đã hạ cánh gây ra tham nhũng cơ sở dữ liệu vì DBMS không biết rằng bài viết của nó đã thất bại. Không vui.