отсортировать файл на месте, продолжая запись в него

Вопрос или проблема

Это кажется достаточно распространенной проблемой, что, вероятно, это дубликат, но я не смог найти никаких других похожих вопросов.

У меня есть что-то вроде лог-файла, к которому я добавляю данные, но я также хочу, чтобы он оставался отсортированным, поэтому у меня есть что-то вроде этого:

echo "foo" >> file.txt
LC_ALL=C sort -k1,1 -u -o file.txt file.txt

Это действительно небольшой файл (<1000 строк), поэтому запись и сортировка должны быть очень быстрыми. Однако иногда у меня может быть около 5 событий в секунду, но некоторые из них теряются. Я был очень удивлен, увидев эту проблему с такой низкой пропускной способностью приложения.

Добавление в файл должно работать каждый раз, поэтому, вероятно, проблема возникает при сортировке. Я думал, что -o (записать результат в файл вместо стандартного вывода) решит проблемы с конкурентностью, но, возможно, это не так.

Существует ли правильный способ сортировки файла, если другой процесс может записывать в него?

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

  • завершением чтения данных из входного файла утилитой sort и началом фактической сортировки, и
  • переименованием временного выходного файла утилитой sort в имя реального выходного файла (предоставленного аргументом -o) незадолго до выхода.

Я действительно не вижу способа предотвратить это, кроме как временно остановить любой процесс, записывающий в файл, перед сортировкой, а затем снова запустить их, когда сортировка будет завершена. Это можно сделать с помощью kill -STOP и kill -CONT соответственно.

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

На системе GNU:

$ seq 10 > file
$ strace sort -o file file
[...]
openat(AT_FDCWD, "file", O_WRONLY|O_CREAT|O_CLOEXEC, 0666) = 3
dup2(3, 1)                              = 1
close(3)                                = 0
openat(AT_FDCWD, "file", O_RDONLY|O_CLOEXEC) = 3
[...]
read(3, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", 512) = 21
read(3, "", 512)                        = 0
lseek(3, 0, SEEK_CUR)                   = 21
close(3)                                = 0
ftruncate(1, 0)                         = 0
fstat(1, {st_mode=S_IFREG|0775, st_size=0, ...}) = 0
write(1, "1\n10\n2\n3\n4\n5\n6\n7\n8\n9\n", 21) = 21
close(1)                                = 0

Как вы можете видеть выше, sort действительно открывает выходной файл для записи без обрезки даже перед его открытием для чтения, чтобы убедиться, что он сможет записывать в него, но затем позже, после чтения и сортировки входных данных, обрезает его перед записью отсортированного содержимого.

Таким образом, если в файл были добавлены дополнительные строки между тем, как sort изначально прочитал его содержимое и отсортировал его, они будут потеряны.

Возможно, еще хуже, если эти дополнительные строки будут добавлены в то время, как sort записывает отсортированный вывод, они будут перемешиваться с отсортированными строками, вероятно, будут записаны посередине некоторых отсортированных строк, поскольку sort будет записывать вывод блоками.

Чтобы этого избежать, вы можете сделать:

sort < file 1<> file

Где оболочка (не sort) откроет file для чтения на fd 0 и для записи на fd 1 без обрезки. Технически <> открывает файл в режиме чтения+записи. Здесь нам не важно чтение, важно только то, что флаг O_TRUNC не передается в open(), как это было бы с > (и, очевидно, мы не можем использовать >>, которое не обрезает, но добавляет вывод в конец).

Тогда отсортированный вывод будет записан поверх входных данных, и если в интервал добавились какие-то данные, они останутся там (несортированными).

Этот подход должен работать на любой системе, не только на GNU, и будет иметь преимущество в сохранении метаданных файла на таких системах, как OpenBSD, где sort -o file создает новый file.

Ответ или решение

Сортировка файла на месте в условиях конкуренции записи

Актуальная задача

Сортировка файла, к которому одновременно могут записываться данные, является распространенной и актуальной задачей в области информационных технологий. Ваша ситуация с лог-файлом, который часто обновляется, требует тщательного подхода, чтобы избежать потери данных при их сортировке. Стандартные утилиты, такие как sort, могут не обеспечивать безопасность данных, если они используются в условиях конкурентного доступа.

Проблема

Когда вы хотите отсортировать файл, который может изменяться в процессе работы, возникает несколько критических моментов:

  1. Чтение и запись: Утилита sort сначала читает файл, а затем сортирует и записывает его обратно. Если в промежутке между этими операциями происходят изменения в файле, новые записи могут быть утеряны или перемешаны с уже отсортированными данными.

  2. Конкурентный доступ: Если другой процесс продолжает записывать в файл, это создает возможность для «прыжков», когда индекс данных изменяется, тем самым нарушая порядок при записи в файл.

Решение

Во избежание этих проблем можно использовать несколько подходов:

  1. Сигналы для управления процессами:

    • Вы можете временно приостановить процесс записи в файл, используя сигналы kill -STOP и kill -CONT. Однако это может быть неэффективно и неудобно для применения.
  2. Использование командной оболочки:
    Чтобы избежать усечения данных, используйте перенаправление ввода-вывода без обрезки файла. Это можно сделать с помощью следующей команды:

    sort < file.txt 1> file.txt

    В этом случае файл будет открыт для чтения и записи одновременно, не обрезая содержимое. Данные, которые были добавлены во время процесса сортировки, останутся в файле, но они останутся неотсортированными.

  3. Создание временного файла:
    Более надежным решением будет использование временного файла. Вы можете сначала отсортировать данные в новом файле, а затем заменить оригинальный файл:

    sort file.txt > temp.txt && mv temp.txt file.txt

    Это гарантирует, что во время сортировки файл не будет изменен, и что только отсортированные данные будут записаны в оригинальный файл.

Заключение

Сортировка файла в условиях конкуренции записи требует внимательного подхода, чтобы гарантировать целостность данных. Использование перенаправления ввода-вывода или создание временного файла являются эффективными и безопасными способами решения этой задачи. Эти методы минимизируют риск потери данных и обеспечивают аккуратную сортировку.

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

Оцените материал
Добавить комментарий

Капча загружается...