TL; DR: Si el kernel de Linux pierde una escritura de E / S almacenada en un búfer , ¿hay alguna forma de que la aplicación se entere?
Sé que tiene que tener fsync()
el archivo (y su directorio principal) para mayor durabilidad . La pregunta es si el núcleo pierde buffers sucios que están pendientes de escritura debido a un error de E / S, ¿cómo puede la aplicación detectar esto y recuperarlo o cancelarlo?
Piense en aplicaciones de bases de datos, etc., donde el orden de las escrituras y la durabilidad de la escritura pueden ser cruciales.
Perdido escribe? ¿Cómo?
La capa de bloque del kernel de Linux puede, en algunas circunstancias, perder solicitudes de E / S almacenadas en búfer que hayan sido enviadas con éxito write()
, pwrite()
etc., con un error como:
Buffer I/O error on device dm-0, logical block 12345
lost page write due to I/O error on dm-0
(Ver end_buffer_write_sync(...)
y end_buffer_async_write(...)
enfs/buffer.c
).
En los núcleos más nuevos, el error contendrá "escritura de página asíncrona perdida" , como:
Buffer I/O error on dev dm-0, logical block 12345, lost async page write
Dado que la aplicación write()
ya habrá regresado sin error, parece que no hay forma de informar un error a la aplicación.
¿Detectarlos?
No estoy tan familiarizado con las fuentes del núcleo, pero creo que se establece AS_EIO
en el búfer que no se pudo escribir si está haciendo una escritura asíncrona:
set_bit(AS_EIO, &page->mapping->flags);
set_buffer_write_io_error(bh);
clear_buffer_uptodate(bh);
SetPageError(page);
pero no me queda claro si la aplicación puede enterarse de esto o cómo puede hacerlo cuando más tarde sea fsync()
el archivo para confirmar que está en el disco.
Parece que wait_on_page_writeback_range(...)
enmm/filemap.c
poder por do_sync_mapping_range(...)
enfs/sync.c
que se llama turno sys_sync_file_range(...)
. Regresa -EIO
si no se pueden escribir uno o más buffers.
Si, como supongo, esto se propaga a fsync()
resultado, entonces si la aplicación entra en pánico y se rescata si recibe un error de E / S fsync()
y sabe cómo volver a hacer su trabajo cuando se reinicia, ¿debería ser suficiente protección?
Presumiblemente no hay forma de que la aplicación sepa qué desplazamientos de bytes en un archivo corresponden a las páginas perdidas, por lo que puede reescribirlas si lo sabe, pero si la aplicación repite todo su trabajo pendiente desde el último éxito fsync()
del archivo, y eso reescribe cualquier almacenamiento intermedio de kernel sucio correspondiente a escrituras perdidas en el archivo, que debería borrar cualquier indicador de error de E / S en las páginas perdidas y permitir fsync()
que se complete la siguiente , ¿verdad?
¿Existen entonces otras circunstancias inofensivas donde fsync()
pueda regresar -EIO
donde rescatar y rehacer el trabajo sería demasiado drástico?
¿Por qué?
Por supuesto, tales errores no deberían suceder. En este caso, el error surgió de una desafortunada interacción entre los dm-multipath
valores predeterminados del controlador y el código de detección utilizado por la SAN para informar la falla en la asignación del almacenamiento de aprovisionamiento delgado. Pero esta no es la única circunstancia donde pueden suceder; también he visto informes de LVM de aprovisionamiento delgado, por ejemplo, como lo usan libvirt, Docker y más. Una aplicación crítica como una base de datos debería tratar de hacer frente a tales errores, en lugar de continuar ciegamente como si todo estuviera bien.
Si el kernel piensa que está bien perder escrituras sin morir con el pánico del kernel, las aplicaciones tienen que encontrar una manera de hacer frente.
El impacto práctico es que encontré un caso en el que un problema de múltiples rutas con una SAN causó escrituras perdidas que terminaron causando corrupción en la base de datos porque el DBMS no sabía que sus escrituras habían fallado. No es divertido.