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_EIObufor, 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, -EIOjeś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 -EIOktó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-multipathustawieniami 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.