TL;DR : Si le noyau Linux perd une écriture d'E/S en mémoire tampon L'application a-t-elle un moyen de le savoir ?
Je sais que tu dois fsync()
le fichier (et son répertoire parent) pour la durabilité . La question est 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 le détecter 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.
Des écrits perdus ? Comment ?
La couche de blocs du noyau Linux peut dans certaines circonstances perdre les demandes d'E/S mises en mémoire tampon qui ont été soumises avec succès par write()
, pwrite()
etc., avec une erreur du type :
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(...)
y end_buffer_async_write(...)
en fs/buffer.c
).
Sur les noyaux plus récents, l'erreur contiendra plutôt "lost async page write". comme :
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Puisque l'application write()
sera déjà retourné sans erreur, il semble qu'il n'y ait aucun moyen de signaler une erreur à l'application.
Vous les détectez ?
Je ne suis pas très familier avec les sources du noyau, mais je pensez à qu'il fixe AS_EIO
sur le tampon qui n'a pas pu être écrit s'il s'agit d'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 vois pas très bien si ou comment l'application peut découvrir cela plus tard. fsync()
s le fichier pour confirmer qu'il est sur le disque.
On dirait que wait_on_page_writeback_range(...)
en mm/filemap.c
pourrait par do_sync_mapping_range(...)
en fs/sync.c
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 à fsync()
puis si l'application panique et abandonne si elle reçoit une erreur d'E/S de la part de fsync()
et sait comment refaire son travail lorsqu'il est redémarré, cela devrait être une garantie suffisante ?
Il n'y a vraisemblablement aucun moyen pour l'application de savoir dont Les décalages d'octets dans un fichier correspondent aux pages perdues, de sorte qu'il peut les réécrire s'il sait comment, mais si l'application répète tout son travail en attente depuis le dernier succès de l'opération de réécriture, il n'y a pas de problème. fsync()
du fichier, et qui réécrit tous les tampons du noyau sales correspondant aux écritures perdues dans le fichier, ce qui devrait effacer tous les drapeaux d'erreur d'E/S sur les pages perdues et permettre à la page suivante de s'exécuter. fsync()
à compléter - n'est-ce pas ?
Y a-t-il donc d'autres circonstances inoffensives où fsync()
peut revenir -EIO
où se désister et refaire le travail serait trop radical ?
Pourquoi ?
Bien sûr, de telles erreurs ne devraient pas se produire. Dans ce cas, l'erreur provient d'une interaction malencontreuse entre la fonction dm-multipath
et le code de détection utilisé par le SAN pour signaler l'échec de l'allocation d'espace de stockage à allocation légère. Mais ce n'est pas la seule circonstance dans laquelle ils peut J'ai également vu des rapports à ce sujet dans des LVM à provisionnement fin, par exemple, tels qu'utilisés 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 possible de perdre des écritures sans mourir d'une panique du noyau, les applications doivent trouver un moyen d'y faire face.
L'impact pratique est que j'ai trouvé un cas où un problème de multivoie avec un SAN a causé des écritures perdues qui ont fini par provoquer une corruption de la base de données parce que le SGBD ne savait pas que ses écritures avaient échoué. Ce n'est pas drôle.
1 votes
Je crains que cela ne nécessite des champs supplémentaires dans la table SystemFileTable pour stocker et mémoriser ces conditions d'erreur. Et une possibilité pour le processus en espace utilisateur de les recevoir ou de les inspecter lors des appels suivants. (est-ce que fsync() et close() renvoient ce genre d'informations ? historique informations ?)
0 votes
@joop Merci. J'ai juste posté une réponse avec ce que je pense qu'il se passe, cela me dérange d'avoir un contrôle de bon sens puisque vous semblez en savoir plus sur ce qui se passe que les personnes qui ont posté des variantes évidentes de "write() needs close() ou fsync() pour la durabilité" sans lire la question ?
0 votes
BTW : Je pense que vous devriez vraiment vous plonger dans les sources du noyau. Les systèmes de fichiers journalisés souffriraient probablement du même genre de problèmes. Sans parler de la gestion de la partition swap. Puisque celles-ci vivent dans l'espace noyau, la gestion de ces conditions sera probablement un peu plus rigide. writev() , qui est visible depuis l'espace utilisateur, semble également être un endroit à regarder. [ à Craig : oui parce que je connais votre nom, et je sais que vous n'êtes pas un idiot complet ;-]
0 votes
@joop Ouais, je suis en train de fouiller dans les sources depuis quelques heures maintenant. J'adore le fait qu'un novice en matière de noyau puisse suivre ce qui se passe raisonnablement bien, avec de belles sources propres. J'essaie actuellement de vérifier que
fsync()
englibc
est soutenu par lesys_sync_file_range
syscall, auquel cas je suis à peu près sûr de savoir ce qui ne va pas maintenant - et que lesfsync
a besoin d'un gros avertissement à propos des tentatives.0 votes
Je pense que le comportement des tentatives est différent pour l'utilisation interne du noyau et pour l'espace utilisateur. L'utilisation du système devrait réessayer et paniquer, l'espace utilisateur devrait (peut-être réessayer et) mais échouer dès que possible. Effacer les drapeaux d'erreur entre les requêtes ne semble pas correct. Qualité de la source : au moins, ils ne coulent pas la valeur de retour de malloc()s ! BTW il y a bien sûr beaucoup d'options d'échec dans le système. (comme umount le périphérique, etc.)
0 votes
Dans le cas d'une application critique, il suffit d'utiliser
O_SYNC|O_DIRECT
combinaison à l'ouverture.0 votes
Si vous n'êtes pas gêné par les performances abyssales qui en résultent. Les écritures synchrones sont terribles. Vous ne pouvez pas faire cela de manière réaliste dans quelque chose comme un SGBD, à moins que vous n'ayez adopté le style Oracle et réimplémenté la moitié des capacités d'E/S du noyau dans votre SGBD, avec des threads d'écriture dédiés, la gestion du cache, etc. Dans ce cas, vous pouvez tout aussi bien utiliser AIO. Dans ce cas, vous pouvez tout aussi bien utiliser AIO.
fsync()
et les E/S tamponnées entièrement. Ma question est de savoir comment on peut utiliser les E/S en mémoire tampon etfsync()
correctement et en toute sécurité.0 votes
Bien sûr, cela serait terriblement mauvais en termes de performances, mais c'est un prix à payer. Si vous voulez lire quelque chose sur la garantie des propriétés ACID pour les SGBD, vous pouvez lire ce qui suit quora.com/ . Les questions intéressantes trouvent des réponses très simples.
0 votes
@Jean-BaptisteYunès Je suis d'accord, bonne explication. Mais encore une fois, c'est un peu hors sujet. Je me demande si Utilisation d'E/S en mémoire tampon il est possible de détecter et de gérer correctement les erreurs d'E/S où le noyau perd des écritures. J'ai répondu moi-même à cette question, ci-dessous, après des recherches plus approfondies. Maintenant, si je voulais réécrire complètement le SGBD pour utiliser AIO ou un pool de threads avec des E/S directes synchrones ou autre, bien sûr, je pourrais suivre votre suggestion. Mais cela devrait être possible et (comme indiqué ci-dessous) il est possible de le faire correctement avec des E/S en mémoire tampon et une utilisation prudente de fsync.
0 votes
@Jean-BaptisteYunès Par analogie, si je demandais "comment garer une voiture", vous m'auriez d'abord expliqué "ne garez pas la voiture, prenez un taxi pour ne pas avoir à le faire", puis "voici comment fonctionnent les règles de stationnement". Les deux sont intéressants, mais n'ont pas grand-chose à voir avec le problème initial.
1 votes
Je suis d'accord, je n'ai pas été très juste. Hélas votre réponse n'est pas très satisfaisante, je veux dire qu'il n'y a pas de solution facile (surprenant ?).
2 votes
@Jean-BaptisteYunès Vrai. Pour le SGBD avec lequel je travaille, "crash and enter redo" est acceptable. Pour la plupart des applications, ce n'est pas une option et elles doivent tolérer les performances horribles des E/S synchrones ou accepter un comportement mal défini et la corruption en cas d'erreur d'E/S.