Por que rename() demora mais quando fsync() é chamado primeiro?

Por que rename() demora mais quando fsync() é chamado primeiro?

Neste teste, por que rename() demora mais quando fsync() é chamado primeiro?

Ambiente: btrfs, HDD mecânico, contêiner Debian 9, rodando no kernel 5.0.17-200.fc29.x86_64.

Comando de teste: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 os 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

A estratégia atual dpkg(por exemplo, no Debian 9) é mais sofisticada do que você imagina. Não tenho certeza se isso realmente afetaria este caso. Se você quiser mais detalhes, há alguns antecedentes nesta questão:O AIO fsync poderia melhorar o desempenho do dpkg?

Não sei se isso é relevante, mas me ocorre que em alguns sistemas de arquivos fsync() também pode sincronizar efetivamente o diretório. Isso é para garantir que os arquivos recém-criados estejam visíveis no disco antes que fsync() retorne. Eu li em algum lugar que isso não acontece no ext2, mas acontece no ext4. Como evidência parcial, verext4: faça fsync para sincronizar o diretório pai sem diário de verdade desta vez

Caso você esteja surpreso com os synctempos finais, posso confirmar que o patch dpkgpara substituir as chamadas individuais de fsync() por uma chamada de sincronização global () parece manter o tempo geral baixo para cerca de 13s. E não encontrei nada inadequado nisso no meu sistema. dpkgsimplesmente parei de usar essa abordagem por causa de outros efeitos colaterais potenciais.[1][2]

Responder1

Com base na descrição do commit, espero que os atrasos rename() sejam causados ​​porBtrfs: sincroniza o log após registrar o novo nome. Isso foi adicionado no kernel v4.19.

Faça com que o log do novo nome do arquivo (que acontece ao criar um link físico ou renomear) persista no log.

Esta abordagem não só é mais simples, [...] mas também nos dá o mesmo comportamento que ext4, xfs e f2fs (possivelmente outros sistemas de arquivos também).

Não acredito que a segunda frase esteja correta!

Para ser justo, devo salientar dpkgque estou esquecendo de fsync() os diretórios que contêm os arquivos, antes de registrar o pacote como instalado corretamente. Mas esse comportamento do Btrfs não é exatamente uma combinação perfeita com o resto do Linux.

Não acredito que o XFS sincronize a nova entrada de diretório dentro de rename() (ou seja, espere deliberadamente que ela seja persistida). Minha presunção contra qualquer gravação de sincronização dentro do XFS rename() é parcialmente baseada neste tópico:https://marc.info/?l=linux-xfs&m=139863577410237&w=2

Para ext4, mencionei evidências de quefsync()pode sincronizar a nova entrada do diretório antes que ela retorne. Mas não acredito que rename() do ext4 faça isso.

Eu criei um link para discussões recentes sobreOperações AIO fsync()e como eles podem permitir lotes eficientes de atualizações de metadados. Não tem havido muita discussão sobre um hipotético rename() AIO, porque a suposição usual é que rename() não é uma operação síncrona!

(btrfs me parece um pouco suspeito em geral. Ou seja, vejo que essa correção de bug de integridade de dados ocorreu nos últimos lançamentos e não foi a única correção que parece assustadora noregistro de alteraçõespara esses lançamentos).


Eu acho que os atrasos rename() devem ser acionados pelo BTRFS_NEED_LOG_SYNCretorno da última linha dobtrfs_log_new_name().

A maneira como descobri isso foi usandotempo desligado. Ele agrega o tempo de espera por rastreamento de pilha. Os rastreamentos de pilha são assim:

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

informação relacionada