TL; DR: Jeśli jądro Linuksa utraci buforowany zapis we / wy , czy jest jakiś sposób, aby aplikacja się dowiedziała?
Wiem, że potrzebujesz fsync()
pliku (i jego katalogu nadrzędnego) dla trwałości . Pytanie brzmi: jeśli jądro traci brudne bufory oczekujące na zapis z powodu błędu we / wy, w jaki sposób aplikacja może to wykryć i odzyskać lub przerwać?
Pomyśl o aplikacjach baz danych itp., Gdzie kolejność i trwałość zapisu mogą być kluczowe.
Zagubione zapisy? W jaki sposób?
Warstwa blok Czy Linux Kernel jest w pewnych okolicznościach stracić buforowane żądań I / O, które zostały wprowadzone z powodzeniem write()
, pwrite()
itp, z błędem jak:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(Zobacz end_buffer_write_sync(...)
i end_buffer_async_write(...)
wfs/buffer.c
środku).
W nowszych jądrach błąd będzie zawierał „utracony asynchroniczny zapis strony” , na przykład:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Ponieważ aplikacja write()
została już zwrócona bez błędów, wydaje się, że nie ma możliwości zgłoszenia błędu z powrotem do aplikacji.
Wykrywanie ich?
Nie jestem zaznajomiony ze źródłami jądra, ale myślę , że ustawia AS_EIO
bufor, który nie został zapisany, jeśli wykonuje zapis asynchroniczny:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
ale nie jest dla mnie jasne, czy i w jaki sposób aplikacja może się o tym dowiedzieć, kiedy później fsync()
będzie plik, aby potwierdzić, że znajduje się na dysku.
Wygląda na to, że wait_on_page_writeback_range(...)
wmm/filemap.c
potędze, do_sync_mapping_range(...)
wfs/sync.c
którym jest sprawdzany sys_sync_file_range(...)
. Zwraca, -EIO
jeśli nie można zapisać jednego lub więcej buforów.
Jeśli, jak się domyślam, rozprzestrzeni się to do fsync()
wyniku, to jeśli aplikacja panikuje i wyskakuje, jeśli otrzyma błąd we / wy fsync()
i wie, jak ponownie wykonać swoją pracę po ponownym uruchomieniu, to powinno być wystarczającym zabezpieczeniem?
Prawdopodobnie nie ma sposobu, aby aplikacja wiedziała, które przesunięcia bajtów w pliku odpowiadają utraconym stronom, aby mogła je przepisać, jeśli wie jak, ale jeśli aplikacja powtórzy całą swoją oczekującą pracę od ostatniego udanego fsync()
pliku, a to przepisuje wszelkie brudne bufory jądra odpowiadające utraconym zapisom w pliku, które powinny wyczyścić wszelkie flagi błędów we / wy na utraconych stronach i pozwolić na zakończenie następnej fsync()
- prawda?
Czy są zatem inne, nieszkodliwe okoliczności, do których fsync()
mogą powrócić, w -EIO
których ratowanie i ponawianie pracy byłoby zbyt drastyczne?
Czemu?
Oczywiście takie błędy nie powinny się zdarzyć. W tym przypadku błąd wynikał z niefortunnej interakcji między dm-multipath
ustawieniami domyślnymi sterownika a kodem rozpoznawczym używanym przez sieć SAN do zgłaszania niepowodzenia alokacji alokacji elastycznej. Ale to nie jedyna okoliczność, w której mogą się zdarzyć - widziałem również raporty o tym, na przykład z cienkiego aprowizowanego LVM, używanego przez libvirt, Docker i inne. Krytyczna aplikacja, taka jak baza danych, powinna próbować radzić sobie z takimi błędami, zamiast działać na ślepo, jakby wszystko było w porządku.
Jeśli jądro uważa, że można stracić zapisy bez umierania z powodu paniki jądra, aplikacje muszą znaleźć sposób, aby sobie z tym poradzić.
Praktyczny wpływ jest taki, że znalazłem przypadek, w którym problem wielościeżkowy z SAN spowodował utratę zapisów, które wylądowały, powodując uszkodzenie bazy danych, ponieważ DBMS nie wiedział, że jego zapisy nie powiodły się. Nie śmieszne.