En esta prueba, ¿por qué rename() tarda más cuando se llama primero a fsync()?
Entorno: btrfs, HDD mecánico, contenedor Debian 9, ejecutándose en el kernel 5.0.17-200.fc29.x86_64.
Comando de prueba:dpkg -r linux-image-4.9.0-9-amd64 >/dev/null 2>&1 && sync && time perf_4.9 trace --no-inherit -s dpkg $FORCE_UNSAFE_IO -i linux-image-4.9.0-9-amd64_4.9.168-1_amd64.deb && time sync
Compare los resultados de FORCE_UNSAFE_IO=""
vs.FORCE_UNSAFE_IO="--force-unsafe-io"
dpkg (31632), 374488 events, 100.0%
syscall calls total min avg max stddev
(msec) (msec) (msec) (msec) (%)
--------------- -------- --------- --------- --------- --------- ------
fsync 3442 14849.586 0.002 4.314 149.959 4.11%
rename 8463 14573.509 0.003 1.722 358.675 4.80%
wait4 7 8043.762 0.004 1149.109 8028.468 99.78%
read 44025 2151.135 0.000 0.049 3.732 0.57%
open 19301 213.628 0.002 0.011 0.375 0.90%
write 7846 165.460 0.003 0.021 0.149 0.42%
sync_file_range 6834 96.513 0.001 0.014 0.822 2.20%
...
real 0m41.703s
user 0m9.709s
sys 0m6.586s
real 0m0.162s
user 0m0.000s
sys 0m0.003s
dpkg (1919), 334232 events, 100.0%
syscall calls total min avg max stddev
(msec) (msec) (msec) (msec) (%)
--------------- -------- --------- --------- --------- --------- ------
wait4 7 8290.981 0.007 1184.426 8279.676 99.84%
read 44399 2168.096 0.000 0.049 2.146 0.50%
fsync 25 653.530 0.006 26.141 68.754 8.65%
rename 8463 522.282 0.003 0.062 69.620 22.53%
open 12467 163.671 0.002 0.013 0.217 0.97%
write 7846 160.979 0.003 0.021 0.356 0.50%
sync_file_range 3417 89.676 0.010 0.026 0.841 2.05%
...
real 0m13.498s
user 0m9.643s
sys 0m5.517s
real 0m0.146s
user 0m0.000s
sys 0m0.004s
La estrategia actual dpkg
(por ejemplo, en Debian 9) es más sofisticada de lo que imagina. Sin embargo, no estoy seguro de que realmente afecte este caso. Si desea más detalles, hay algunos antecedentes en esta pregunta:¿AIO fsync podría mejorar el rendimiento de dpkg?
No sé si esto es relevante, pero se me ocurre que en algunos sistemas de archivos fsync() también puede sincronizar efectivamente el directorio. Esto es para garantizar que los archivos recién creados sean visibles en el disco antes de que regrese fsync(). Leí en alguna parte que esto no sucede en ext2, pero sí en ext4. Como evidencia parcial, verext4: haga que fsync sincronice el directorio principal sin diario de verdad esta vez
En caso de que le sorprendan los sync
tiempos finales, puedo confirmar que la aplicación de parches dpkg
para reemplazar las llamadas fsync() individuales con una llamada sync() global parece mantener el tiempo total en aproximadamente 13 segundos. Y no he encontrado nada inadecuado en eso en mi sistema. dpkg
Simplemente dejé de usar ese enfoque debido a otros posibles efectos secundarios.[1][2]
Respuesta1
Según la descripción de la confirmación, espero que los retrasos en el cambio de nombre () sean causados porBtrfs: registro de sincronización después de registrar un nuevo nombre. Esto se agregó en el kernel v4.19.
Haga que el registro del nuevo nombre de archivo (lo que ocurre al crear un enlace físico o cambiar el nombre) persista en el registro.
Este enfoque no sólo es más simple, [...] sino que también nos da el mismo comportamiento que ext4, xfs y f2fs (posiblemente también otros sistemas de archivos).
¡No creo que la segunda frase sea correcta!
Para ser justos, debo señalar dpkg
que se olvida fsync() de los directorios que contienen los archivos, antes de que registre el paquete como instalado correctamente. Pero este comportamiento de btrfs no coincide exactamente con el resto de Linux.
No creo que XFS sincronice la nueva entrada del directorio dentro de rename() (es decir, espera deliberadamente a que persista). Mi presunción contra cualquier escritura de sincronización dentro de XFS rename() se basa en parte en este hilo:https://marc.info/?l=linux-xfs&m=139863577410237&w=2
Para ext4, mencioné evidencia de quesincronización()podría sincronizar la nueva entrada del directorio antes de que regrese. Pero no creo que rename() de ext4 lo haga.
Me vinculé a discusiones recientes sobreOperaciones AIO fsync()y cómo podrían permitir el procesamiento por lotes eficiente de actualizaciones de metadatos. No ha habido mucha discusión sobre un hipotético rename() de AIO, porque la suposición habitual es que rename() no es una operación sincrónica.
(Btrfs me parece un poco sospechoso en general. Es decir, veo que esta corrección de errores de integridad de datos se realizó en las últimas versiones y no fue la única solución que suena aterradora en elregistro de cambiospara estos lanzamientos).
Creo que los retrasos en el cambio de nombre () deben ser provocados por el BTRFS_NEED_LOG_SYNC
retorno de la última línea debtrfs_log_new_name().
La forma en que encontré esto fue usandotiempo fuera de CPU. Agrega el tiempo de espera por seguimiento de pila. Los seguimientos de la pila se ven así:
io_schedule_timeout
wait_for_completion_io
write_all_supers
btrfs_sync_log
btrfs_sync_file
do_fsync
__x64_sys_fsync
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
9735954
io_schedule_timeout
wait_for_completion_io
write_all_supers
btrfs_sync_log
btrfs_rename2
vfs_rename
do_renameat2
__x64_sys_rename
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
9147785
io_schedule
bit_wait_io
__wait_on_bit
out_of_line_wait_on_bit
write_all_supers
btrfs_sync_log
btrfs_sync_file
do_fsync
__x64_sys_fsync
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
4478158
io_schedule
bit_wait_io
__wait_on_bit
out_of_line_wait_on_bit
write_all_supers
btrfs_sync_log
btrfs_rename2
vfs_rename
do_renameat2
__x64_sys_rename
do_syscall_64
entry_SYSCALL_64_after_hwframe
- dpkg (23528)
4376109