TL; DR: Si le noyau Linux perd une écriture d'E / S tamponnée , y a-t-il un moyen pour l'application de le savoir?
Je sais que vous devez fsync()
le fichier (et son répertoire parent) pour la durabilité . La question est de savoir si le noyau perd les tampons sales qui sont en attente d'écriture en raison d'une erreur d'E / S, comment l'application peut-elle détecter cela et récupérer ou abandonner?
Pensez aux applications de base de données, etc., où l'ordre des écritures et la durabilité des écritures peuvent être cruciaux.
Écritures perdues? Comment?
La couche de blocs du noyau Linux peut dans certaines circonstances perdre les demandes d'E / S tamponnées qui ont été soumises avec succès par write()
, pwrite()
etc., avec une erreur comme:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(Voir end_buffer_write_sync(...)
et end_buffer_async_write(...)
dansfs/buffer.c
).
Sur les noyaux plus récents, l'erreur contiendra à la place "écriture de page asynchrone perdue" , comme:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Étant donné que l'application write()
sera déjà retournée sans erreur, il ne semble y avoir aucun moyen de signaler une erreur à l'application.
Les détecter?
Je ne suis pas très familier avec les sources du noyau, mais je pense qu'il est défini AS_EIO
sur le tampon qui n'a pas pu être écrit s'il fait une écriture asynchrone:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
mais je ne sais pas si ou comment l'application peut le découvrir plus tard fsync()
le fichier pour confirmer qu il est sur le disque.
Il ressemble wait_on_page_writeback_range(...)
enmm/filemap.c
puissance par do_sync_mapping_range(...)
dansfs/sync.c
ce qui est à son tour appelé par sys_sync_file_range(...)
. Il retourne-EIO
si un ou plusieurs tampons n'ont pas pu être écrits.
Si, comme je le suppose, cela se propage au fsync()
résultat de, alors si l'application panique et renonce si elle reçoit une erreur d'E / S fsync()
et sait comment refaire son travail lors du redémarrage, cela devrait être une sauvegarde suffisante?
Il n'y a probablement aucun moyen pour l'application de savoir quels décalages d'octets dans un fichier correspondent aux pages perdues afin qu'elle puisse les réécrire si elle sait comment, mais si l'application répète tout son travail en attente depuis le dernier succès fsync()
du fichier, et cela réécrit des tampons de noyau sales correspondant à des écritures perdues sur le fichier, qui devraient effacer tous les indicateurs d'erreur d'E / S sur les pages perdues et permettre au suivant fsync()
de se terminer - n'est-ce pas?
Y a-t-il alors d'autres circonstances, inoffensives, où fsync()
peuvent revenir -EIO
où renflouer et refaire des travaux serait trop drastique?
Pourquoi?
Bien entendu, de telles erreurs ne devraient pas se produire. Dans ce cas, l'erreur provenait d'une interaction malheureuse entre les dm-multipath
valeurs par défaut du pilote et le code de détection utilisé par le SAN pour signaler l'échec de l'allocation du stockage alloué de manière dynamique. Mais ce n'est pas la seule circonstance où ils peuvent se produire - j'ai également vu des rapports de LVM à provisionnement fin par exemple, tel qu'utilisé par libvirt, Docker, etc. Une application critique comme une base de données devrait essayer de faire face à de telles erreurs, plutôt que de continuer aveuglément comme si tout allait bien.
Si le noyau pense qu'il est acceptable de perdre des écritures sans mourir avec une panique du noyau, les applications doivent trouver un moyen de faire face.
L'impact pratique est que j'ai trouvé un cas où un problème de trajets multiples avec un SAN a causé des écritures perdues qui ont abouti à une corruption de la base de données parce que le SGBD ne savait pas que ses écritures avaient échoué. Pas drôle.