Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

261
Vistas
Escribir programas para hacer frente a errores de E/S que causan escrituras perdidas en Linux

TL; DR: si el kernel de Linux pierde una escritura de E/S almacenada en búfer , ¿hay alguna forma de que la aplicación lo descubra?

Sé que tiene que fsync() el archivo (y su directorio principal) para mayor durabilidad . La pregunta es si el kernel 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 recuperarse o abortar?

Piense en aplicaciones de bases de datos, etc., donde el orden de las escrituras y la durabilidad de las escrituras pueden ser cruciales.

¿Escritos perdidos? ¿Cómo?

La capa de bloque del kernel de Linux puede, en algunas circunstancias, perder las solicitudes de E/S almacenadas en búfer que se han enviado correctamente mediante 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(...) en fs/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 write() de la aplicación ya habrá regresado sin error, parece que no hay forma de informar un error a la aplicación.

¿Detectándolos?

No estoy tan familiarizado con las fuentes del kernel, pero creo que 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 o cómo la aplicación puede averiguarlo cuando fsync() s el archivo para confirmar que está en el disco.

Parece que wait_on_page_writeback_range(...) en mm/filemap.c podría ser do_sync_mapping_range(...) en fs/sync.c que a su vez es llamado por sys_sync_file_range(...) . Devuelve -EIO si no se pudieron escribir uno o más búferes.

Si, como supongo, esto se propaga al resultado de fsync() , entonces si la aplicación entra en pánico y se recupera si recibe un error de E/S de fsync() y sabe cómo volver a hacer su trabajo cuando se reinicia, eso debería ser suficiente salvaguardia?

Presumiblemente, no hay forma de que la aplicación sepa qué desplazamientos de bytes en un archivo corresponden a las páginas perdidas para que pueda reescribirlas si sabe cómo, pero si la aplicación repite todo su trabajo pendiente desde la última fsync() exitosa del archivo, y eso reescribe cualquier búfer de kernel sucio correspondiente a escrituras perdidas en el archivo, eso debería borrar cualquier indicador de error de E/S en las páginas perdidas y permitir que se complete el siguiente fsync() , ¿verdad?

¿Hay entonces alguna otra circunstancia inofensiva en la que fsync() pueda devolver -EIO en la que rescatar y rehacer el trabajo sería demasiado drástico?

¿Por qué?

Por supuesto, tales errores no deberían ocurrir. En este caso, el error surgió de una interacción desafortunada entre los valores predeterminados del controlador dm-multipath y el código de sentido utilizado por la SAN para informar la falla en la asignación de almacenamiento de aprovisionamiento delgado. Pero esta no es la única circunstancia en la que pueden ocurrir: 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 un kernel panic, las aplicaciones tienen que encontrar una manera de hacerle frente.

El impacto práctico es que encontré un caso en el que un problema de rutas múltiples con una SAN causó escrituras perdidas que terminaron causando daños en la base de datos porque el DBMS no sabía que sus escrituras habían fallado. No es divertido.

over 4 years ago · Santiago Trujillo
5 Respuestas
Responde la pregunta

0

Use el indicador O_SYNC cuando abra el archivo. Garantiza que los datos se escriban en el disco.

Si esto no te satisface, no habrá nada.

over 4 years ago · Santiago Trujillo Denunciar

0

write (2) proporciona menos de lo que espera. La página del manual es muy abierta sobre la semántica de una llamada exitosa a write() :

Un retorno exitoso de write() no garantiza que los datos se hayan enviado al disco. De hecho, en algunas implementaciones con errores, ni siquiera garantiza que el espacio se haya reservado con éxito para los datos. La única forma de estar seguro es llamar a fsync (2) una vez que haya terminado de escribir todos sus datos.

Podemos concluir que una write() exitosa simplemente significa que los datos han llegado a las instalaciones de almacenamiento en búfer del kernel. Si falla la persistencia del búfer, un acceso posterior al descriptor de archivo devolverá el código de error. Como último recurso, puede ser close() . La página del manual de la llamada al sistema close (2) contiene la siguiente oración:

Es muy posible que los errores en una operación de write anterior (2) se notifiquen primero en el close final ().

Si su aplicación necesita conservar la escritura de datos, debe usar fsync / fsyncdata regularmente:

fsync() transfiere ("descarga") todos los datos internos modificados de (es decir, páginas de caché de búfer modificadas para) el archivo al que hace referencia el descriptor de archivo fd al dispositivo de disco (u otro dispositivo de almacenamiento permanente) para que toda la información modificada se puede recuperar incluso después de que el sistema fallara o se reiniciara. Esto incluye escribir o vaciar un caché de disco, si está presente. La llamada se bloquea hasta que el dispositivo informa que la transferencia se ha completado.

over 4 years ago · Santiago Trujillo Denunciar

0

Compruebe el valor de retorno de close. close puede fallar mientras que las escrituras almacenadas en búfer parecen tener éxito.

over 4 years ago · Santiago Trujillo Denunciar

0

fsync() devuelve -EIO si el kernel perdió una escritura

(Nota: la primera parte hace referencia a núcleos más antiguos; se actualiza a continuación para reflejar los núcleos modernos)

Parece que la escritura de búfer asíncrono en end_buffer_async_write(...) fallas establecen un indicador -EIO en la página de búfer sucio fallida para el archivo :

 set_bit(AS_EIO, &page->mapping->flags); set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); SetPageError(page);

que luego es detectado por wait_on_page_writeback_range(...) como lo llama do_sync_mapping_range(...) como lo llama sys_sync_file_range(...) como lo llama sys_sync_file_range2(...) para implementar la llamada de la biblioteca C fsync() .

¡Pero sólo una vez!

Este comentario sobre sys_sync_file_range

 168 * SYNC_FILE_RANGE_WAIT_BEFORE and SYNC_FILE_RANGE_WAIT_AFTER will detect any 169 * I/O errors or ENOSPC conditions and will return those to the caller, after 170 * clearing the EIO and ENOSPC flags in the address_space.

sugiere que cuando fsync() devuelve -EIO o (no documentado en la página de manual) -ENOSPC , borrará el estado de error para que un fsync() posterior informe el éxito aunque las páginas nunca se hayan escrito.

Efectivamente, wait_on_page_writeback_range(...)borra los bits de error cuando los prueba :

 301 /* Check for outstanding write errors */ 302 if (test_and_clear_bit(AS_ENOSPC, &mapping->flags)) 303 ret = -ENOSPC; 304 if (test_and_clear_bit(AS_EIO, &mapping->flags)) 305 ret = -EIO;

Entonces, si la aplicación espera que pueda volver a intentar fsync() hasta que tenga éxito y confíe en que los datos están en el disco, está terriblemente mal.

Estoy bastante seguro de que esta es la fuente de la corrupción de datos que encontré en el DBMS. Vuelve a intentar fsync() y piensa que todo estará bien cuando tenga éxito.

¿Está esto permitido?

Los documentos POSIX/SuS en fsync() realmente no especifican esto de ninguna manera:

Si la función fsync() falla, no se garantiza que se hayan completado las operaciones de E/S pendientes.

La página de manual de Linux para fsync() simplemente no dice nada sobre lo que sucede en caso de falla.

Entonces parece que el significado de los errores de fsync() es "No sé qué pasó con tus escrituras, podría haber funcionado o no, mejor inténtalo de nuevo para estar seguro".

Núcleos más nuevos

En 4.9, end_buffer_async_write establece -EIO en la página, solo a través mapping_set_error .

 buffer_io_error(bh, ", lost async page write"); mapping_set_error(page->mapping, -EIO); set_buffer_write_io_error(bh); clear_buffer_uptodate(bh); SetPageError(page);

En el lado de la sincronización, creo que es similar, aunque la estructura ahora es bastante compleja de seguir. filemap_check_errors en mm/filemap.c ahora hace:

 if (test_bit(AS_EIO, &mapping->flags) && test_and_clear_bit(AS_EIO, &mapping->flags)) ret = -EIO;

que tiene el mismo efecto. Las comprobaciones de errores parecen pasar todas por filemap_check_errors , que hace una prueba y borra:

 if (test_bit(AS_EIO, &mapping->flags) && test_and_clear_bit(AS_EIO, &mapping->flags)) ret = -EIO; return ret;

Estoy usando btrfs en mi computadora portátil, pero cuando creo un bucle invertido ext4 para probar en /mnt/tmp y configuro una sonda de rendimiento en él:

 sudo dd if=/dev/zero of=/tmp/ext bs=1M count=100 sudo mke2fs -j -T ext4 /tmp/ext sudo mount -o loop /tmp/ext /mnt/tmp sudo perf probe filemap_check_errors sudo perf record -g -e probe:end_buffer_async_write -e probe:filemap_check_errors dd if=/dev/zero of=/mnt/tmp/test bs=4k count=1 conv=fsync

Encuentro la siguiente pila de llamadas en perf report -T :

 ---__GI___libc_fsync entry_SYSCALL_64_fastpath sys_fsync do_fsync vfs_fsync_range ext4_sync_file filemap_write_and_wait_range filemap_check_errors

Una lectura sugiere que sí, los núcleos modernos se comportan de la misma manera.

Esto parece significar que si fsync() (o presumiblemente write() o close() ) devuelve -EIO , el archivo se encuentra en un estado indefinido entre la última vez que fsync() d o close() y su fecha más reciente write() diez estados.

Prueba

He implementado un caso de prueba para demostrar este comportamiento .

Trascendencia

Un DBMS puede hacer frente a esto ingresando a la recuperación de fallas. ¿Cómo diablos se supone que una aplicación de usuario normal puede hacer frente a esto? La página de manual de fsync() no advierte que significa "fsync-if-you-feels-like-it" y espero que muchas aplicaciones no se adapten bien a este comportamiento.

Informes de errores

  • https://bugzilla.kernel.org/show_bug.cgi?id=194755
  • https://bugzilla.kernel.org/show_bug.cgi?id=194757

Otras lecturas

lwn.net se refirió a esto en el artículo "Manejo de errores de capa de bloque mejorado" .

Hilo de la lista de correo de postgresql.org .

over 4 years ago · Santiago Trujillo Denunciar

0

Dado que la escritura () de la aplicación ya habrá regresado sin error, parece que no hay forma de informar un error a la aplicación.

No estoy de acuerdo. La write puede regresar sin error si la escritura simplemente se pone en cola, pero el error se informará en la próxima operación que requerirá la escritura real en el disco, eso significa en el próximo fsync , posiblemente en una siguiente escritura si el sistema decide vaciar el caché. y al menos en el cierre del último archivo.

Esa es la razón por la cual es esencial que la aplicación pruebe el valor de retorno de close para detectar posibles errores de escritura.

Si realmente necesita poder realizar un procesamiento inteligente de errores, debe asumir que todo lo que se escribió desde la última fsync exitosa puede haber fallado y que, en todo eso, al menos algo ha fallado.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda