Почему rename() выполняется дольше, если fsync() вызывается первым?

Почему rename() выполняется дольше, если fsync() вызывается первым?

Почему в этом тесте rename() выполняется дольше, если fsync() вызывается первым?

Окружение: btrfs, механический жесткий диск, контейнер Debian 9, работающий на ядре 5.0.17-200.fc29.x86_64.

Тестовая команда: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

Сравните результаты FORCE_UNSAFE_IO=""с 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

Текущая стратегия dpkg(например, в Debian 9) более сложна, чем вы можете себе представить. Я не уверен, что это действительно повлияет на этот случай. Если вам нужны подробности, вот некоторая предыстория в этом вопросе:Может ли AIO fsync улучшить производительность dpkg?

Я не знаю, относится ли это к делу, но мне кажется, что на некоторых файловых системах fsync() может эффективно синхронизировать и каталог. Это делается для того, чтобы убедиться, что вновь созданные файлы видны на диске до того, как fsync() вернет управление. Я где-то читал, что этого не происходит на ext2, но происходит на ext4. В качестве частичного доказательства см.ext4: на этот раз сделайте fsync для реальной синхронизации родительского каталога в no-journal

Если вас удивляют конечные syncтайминги, я могу подтвердить, что исправление dpkgдля замены отдельных вызовов fsync() глобальным вызовом sync(), похоже, позволяет сократить общее время до 13 с. И я не нашел ничего неадекватного в этом на своей системе. dpkgПросто прекратил использовать этот подход из-за других потенциальных побочных эффектов.[1][2]

решение1

На основании описания коммита я предполагаю, что задержки rename() вызваныBtrfs: синхронизация журнала после регистрации нового имени. Это было добавлено в ядре v4.19.

Сделайте так, чтобы регистрация нового имени файла (что происходит при создании жесткой ссылки или переименовании) сохранялась в журнале.

Этот подход не только проще, [...] но и обеспечивает такое же поведение, как у ext4, xfs и f2fs (возможно, и других файловых систем).

Я не верю, что второе предложение верно!

Справедливости ради я должен отметить, dpkgчто забывает fsync() для каталогов, содержащих файлы, прежде чем он запишет пакет как правильно установленный. Но такое поведение btrfs не совсем идеально подходит для остальной части Linux.

Я не верю, что XFS синхронизирует новую запись каталога внутри rename() (т.е. намеренно ждет, пока она будет сохранена). Мое предположение против любых синхронных записей внутри XFS rename() частично основано на этой теме:https://marc.info/?l=linux-xfs&m=139863577410237&w=2

Для ext4 я привел доказательства того, чтоfsync()может синхронизировать новую запись каталога перед возвратом. Но я не верю, что rename() ext4 делает это.

Я ссылался на недавние обсуждения по темеОперации AIO fsync(), и как они могли бы обеспечить эффективную пакетную обработку обновлений метаданных. Не было большого обсуждения гипотетического AIO rename(), поскольку обычно предполагается, что rename() не является синхронной операцией!

(btrfs в целом кажется мне немного подозрительным. То есть, я вижу, что это исправление ошибки целостности данных было в последних нескольких релизах, и это было не единственное пугающе звучащее исправление вжурнал измененийдля этих релизов).


Я думаю, что задержки rename() должны быть вызваны BTRFS_NEED_LOG_SYNCвозвращаемым значением из последней строкиbtrfs_log_new_name().

Я нашел это, используяoffcputime. Он агрегирует время ожидания по трассировке стека. Трассировка стека выглядит так:

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

Связанный контент