TL; DR: Wenn der Linux-Kernel einen gepufferten E / A-Schreibvorgang verliert , kann die Anwendung dies herausfinden?
Ich weiß, dass Sie fsync()
die Datei (und das übergeordnete Verzeichnis) für die Haltbarkeit benötigen . Die Frage ist, ob der Kernel fehlerhafte Puffer verliert, deren Schreibvorgang aufgrund eines E / A-Fehlers aussteht. Wie kann die Anwendung dies erkennen und wiederherstellen oder abbrechen?
Denken Sie an Datenbankanwendungen usw., bei denen die Reihenfolge der Schreibvorgänge und die Schreibdauer von entscheidender Bedeutung sein können.
Verlorene schreibt? Wie?
Die Linux - Kernel-Block Schicht kann unter bestimmten Umständen verlieren I / O - Anfragen gepuffert , die erfolgreich durch eingereicht wurde write()
, pwrite()
wie usw., mit einem Fehler:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(Siehe end_buffer_write_sync(...)
und end_buffer_async_write(...)
infs/buffer.c
).
Auf neueren Kerneln enthält der Fehler stattdessen "Lost Async Page Write" , wie:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Da die Anwendung write()
bereits fehlerfrei zurückgegeben wurde, scheint es keine Möglichkeit zu geben, einen Fehler an die Anwendung zurückzumelden.
Sie erkennen?
Ich bin mit den Kernelquellen nicht so vertraut, aber ich denke, dass sie AS_EIO
auf den Puffer gesetzt sind, der nicht ausgeschrieben werden konnte, wenn ein asynchroner Schreibvorgang ausgeführt wird:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
Es ist mir jedoch unklar, ob oder wie die Anwendung dies herausfinden kann, wenn sie später in fsync()
der Datei bestätigt, dass sie sich auf der Festplatte befindet.
Es sieht aus wie wait_on_page_writeback_range(...)
inmm/filemap.c
Macht, do_sync_mapping_range(...)
infs/sync.c
der wiederum von genannt wird sys_sync_file_range(...)
. Es wird zurückgegeben, -EIO
wenn ein oder mehrere Puffer nicht geschrieben werden konnten.
Wenn sich dies, wie ich vermute, auf fsync()
das Ergebnis auswirkt, wenn die App in Panik gerät und ausfällt, wenn ein E / A-Fehler auftritt fsync()
und weiß, wie sie ihre Arbeit beim Neustart erneut ausführen kann, sollte dies eine ausreichende Sicherheit sein?
Vermutlich kann die App nicht erkennen, welche Byte-Offsets in einer Datei den verlorenen Seiten entsprechen, sodass sie diese neu schreiben kann, wenn sie weiß, wie, aber wenn die App alle anstehenden Arbeiten seit dem letzten erfolgreichen fsync()
der Datei wiederholt und diese neu schreibt Alle schmutzigen Kernel-Puffer, die verlorenen Schreibvorgängen für die Datei entsprechen, sollten alle E / A-Fehlerflags auf den verlorenen Seiten löschen und den nächsten fsync()
Abschluss ermöglichen - richtig?
Gibt es dann noch andere harmlose Umstände, fsync()
unter -EIO
denen die Rettung und Wiederholung von Arbeiten zu drastisch wäre?
Warum?
Natürlich sollten solche Fehler nicht auftreten. In diesem Fall ist der Fehler auf eine unglückliche Interaktion zwischen den dm-multipath
Standardeinstellungen des Treibers und dem vom SAN verwendeten Erfassungscode zurückzuführen, der den Fehler beim Zuweisen von Thin Provisioning-Speicher meldet. Dies ist jedoch nicht der einzige Umstand, unter dem sie auftreten können. Ich habe auch Berichte darüber von beispielsweise Thin Provisioning LVM gesehen, wie sie von libvirt, Docker und anderen verwendet werden. Eine kritische Anwendung wie eine Datenbank sollte versuchen, mit solchen Fehlern umzugehen, anstatt blind weiterzumachen, als ob alles in Ordnung wäre.
Wenn der Kernel der Meinung ist, dass es in Ordnung ist, Schreibvorgänge zu verlieren, ohne an einer Kernel-Panik zu sterben, müssen Anwendungen einen Weg finden, um damit umzugehen.
Die praktische Auswirkung ist, dass ich einen Fall gefunden habe, in dem ein Multipath-Problem mit einem SAN zu verlorenen Schreibvorgängen führte, die zu einer Beschädigung der Datenbank führten, weil das DBMS nicht wusste, dass seine Schreibvorgänge fehlgeschlagen waren. Kein Spaß.