Skip to content

Commit c6e9725

Browse files
authored
pretect concurrent write WAL files to shared network folder
1 parent c8d196f commit c6e9725

File tree

1 file changed

+66
-21
lines changed

1 file changed

+66
-21
lines changed

pg_backup/archive_command.sh

Lines changed: 66 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,76 @@
11
#!/bin/bash
2-
3-
# заглушка, т.к. для archive_mode = 'on' требуется перезагрузка СУБД
2+
3+
# заглушка, т.к. при изменении значения параметра archive_mode требуется перезагрузка СУБД
44
# exit 0
5-
6-
# проверяем, скрипт должен запускаться с двумя параметрами
5+
6+
function log() {
7+
# echo $(date --rfc-3339=ns) $(hostname -s) $(basename "$SRC_FILE") "$1" &>> "$WAL_DIR/archive_wal.log"
8+
return 0
9+
}
10+
11+
WAL_DIR="/mnt/backup_db/archive_wal/cluster"
12+
SRC_FILE="$2" # откуда будем читать WAL файл
13+
DST_FILE="$WAL_DIR/$1.zst" # куда будем сохранять WAL файл
14+
LOCK_FILE="$WAL_DIR/$1.lock" # этот файл создаётся перед архиваций WAL файла и удаляется после
15+
16+
log "check"
17+
18+
# скрипт должен запускаться с двумя параметрами
719
test "$#" -ne 2 && echo "Error: 2 number of parameters expected, $# given" >&2 && exit 2
8-
9-
FILE_SRC="$2"
10-
FILE_DST="/mnt/backup_db/archive_wal/cluster/$1.zst"
11-
12-
test ! -f "$FILE_SRC" && echo "Error: file '$FILE_SRC' does not exist!" >&2 && exit 1
13-
test -f "$FILE_DST" && exit
14-
20+
test ! -f "$SRC_FILE" && echo "Error: WAL file '$SRC_FILE' does not exist!" >&2 && exit 1
21+
test -f "$DST_FILE" && test ! -f "$LOCK_FILE" && log "exists" && exit 0
22+
23+
log "does not exist"
24+
25+
: <<'COMMENT'
26+
Для надёжности архивирование WAL файлов могут настроить на мастере и репликах через параметр archive_mode=always.
27+
Запрещаем от разных экземпляров СУБД конкурентную запись WAL файлов в общие сетевые папки.
28+
Например, в основном и резервном ЦОДе могут быть разные сетевые папки.
29+
Дополнительные файлы и проверки нужны для решения проблемы переполнения диска или недоступности сетевого соединения,
30+
когда WAL файл может не записаться совсем или записаться только частично.
31+
COMMENT
32+
33+
SRV_FILE="$WAL_DIR/archive_server.$(hostname -s)" # сервер, на котором планируется архивирование WAL файла
34+
touch -m "$SRV_FILE" || exit # создаём файл или обновляем дату модификации файла
35+
36+
# разрешаем архивацию WAL файла только при наличии жёстких связанных ссылок (единый inode) между файлами $SRV_FILE и $LOCK_FILE
37+
# для отладки и просмотра кол-ва жёстких ссылок на файл используйте утилиту stat (не используйте ls, она кеширует информацию)
38+
if ln -T "$SRV_FILE" "$LOCK_FILE" &> /dev/null; then
39+
# если удалось создать файл $LOCK_FILE, то только этот (основной) процесс будет архивировать WAL файл (первая попытка)
40+
log "locked now"
41+
elif test "$SRV_FILE" -ef "$LOCK_FILE"; then
42+
# если файлы имеют одинаковый inode, то только этот (основной) процесс будет архивировать WAL файл (повторная попытка)
43+
log "locked already"
44+
else
45+
# иначе архивировать WAL файл пытается конкурирующий процесс, ждём несколько секунд завершения работы основного процесса
46+
for i in {1..50}; do
47+
log "waiting i=$i"
48+
sleep 0.2
49+
test -f "$DST_FILE" && test ! -f "$LOCK_FILE" && log "appeared" && exit 0
50+
done
51+
# не дождались, значит основной процесс сломался, WAL файл мог сохраниться только частично
52+
# передаём управление другому конкурирующему процессу, который станет основным (при повторном вызове этого скрипта)
53+
rm -f "$DST_FILE" && rm -f "$LOCK_FILE" # очерёдность удаления файлов важна
54+
MESSAGE="waiting timeout, deleted, unlocked"
55+
log "$MESSAGE"
56+
echo "Error: WAL file '$DST_FILE' $MESSAGE" >&2
57+
exit 1
58+
fi
59+
1560
# кол-во потоков сжатия
1661
ZSTD_THREADS=$(echo "$(nproc) / 4 + 1" | bc)
17-
18-
# кол-во файлов в очереди на архивирование
19-
ARCHIVE_STATUS_DIR=$(dirname $FILE_SRC)/archive_status
62+
63+
# подсчитываем кол-во WAL файлов в очереди на архивирование
64+
ARCHIVE_STATUS_DIR=$(dirname "$SRC_FILE")/archive_status
2065
WAL_FILES_QUEUE=$(find "$ARCHIVE_STATUS_DIR" -maxdepth 1 -type f -name "*.ready" -printf "." | wc --bytes)
21-
22-
# чем больше файлов в очереди, тем меньше степень сжатия (но больше скорость сжатия и размер сжатого файла)
23-
STEP=3
66+
67+
STEP=2
68+
# чем больше WAL файлов в очереди, тем меньше степень сжатия (но больше скорость сжатия и размер сжатого файла)
2469
# не ставьте большой уровень компрессии, это приводит к большому потреблению CPU, а экономия на размере файла несущественная
2570
ZSTD_LEVEL=$(echo "(9 * ${STEP} - ${WAL_FILES_QUEUE}) / ${STEP}" | bc)
2671
test "$ZSTD_LEVEL" -lt 1 && ZSTD_LEVEL=1
27-
28-
# архивируем файл
72+
73+
# архивируем WAL файл
2974
# в zstd без флага -B1M используются не все ядра (из-за небольшого размера файла?)
30-
# в zstd флаг -f использовать нельзя, т.к. если файл существует, то должна быть ошибка (защита от дурака от перезаписи нужного файла)
31-
ionice -c2 -n7 nice -n19 zstd -q -${ZSTD_LEVEL} -T${ZSTD_THREADS} -B1M "$FILE_SRC" -o "$FILE_DST"
75+
ionice -c2 -n7 -- nice -n19 -- zstd -q -f -${ZSTD_LEVEL} -T${ZSTD_THREADS} -B1M "$SRC_FILE" -o "$DST_FILE" \
76+
&& rm -f "$LOCK_FILE" && log "saved, unlocked"

0 commit comments

Comments
 (0)