Копирование оборвалось на 92%: как дозалить, а не начинать заново
Копирование восьми терабайт бэкапов на новый сервер шло всю ночь и оборвалось на 92% — сеть моргнула, или приёмник перезагрузился по расписанию апдейтов. Первый порыв — стереть всё и запустить заново, потому что докачка «поверх» кажется рискованной: а вдруг то, что уже долетело, битое. На практике риск ровно обратный: слепой перезапуск с нуля не решает проблему целостности, а просто прячет её на будущее, да ещё и тратит время и трафик впустую. Правильная докачка с проверкой уже полученного — быстрее и надёжнее, если не пропустить один обязательный шаг.
Содержание
- Почему «начать заново» — не самый безопасный вариант
- Как работает докачка: инструменты с поддержкой резюмирования
- Главная ловушка: докачка поверх повреждённого хвоста
- Шаг 1: что реально долетело, а что нет
- Шаг 2: проверка уже скопированного куска перед докачкой
- Шаг 3: докачка и финальная верификация всего результата
Почему «начать заново» — не самый безопасный вариант
Полный перекопир 8 ТБ по обычному каналу — это снова часы, а то и сутки. Если разрыв был не разовой случайностью (нестабильный VPN, перегруженный аплинк, сервер, который перезагружается по крону каждую ночь), вы рискуете упереться в тот же обрыв повторно — и на этот раз потерять уже не 8%, а весь прогресс заново. Полная перезаливка не гарантирует целостность сама по себе: она просто откладывает вопрос «а что, если оборвётся снова» на следующий цикл, вместо того чтобы решить его напрямую — проверкой того, что действительно долетело.
Есть и практическая причина: при копировании дерева из тысяч файлов «92%» почти всегда означает, что подавляющее большинство файлов скопировалось полностью и корректно, а под ударом — только один-два файла, которые передавались в момент обрыва. Удалять и перекапировать весь массив ради этих одного-двух файлов — бессмысленная трата ресурсов.
Как работает докачка: инструменты с поддержкой резюмирования
Общая идея резюмируемого копирования проста: инструмент не считает, что раз файла ещё нет целиком на приёмнике — значит, надо слать его с нуля. Вместо этого он смотрит, что уже есть на той стороне, и досылает только недостающее (или отличающееся).
Самый известный инструмент с такой поддержкой — rsync. Ключевые флаги:
-a(archive) — сохраняет права, время модификации, символьные ссылки;--partial— не удаляет недокачанный файл при обрыве; без этого флага rsync по умолчанию подчищает за собой временный файл, и та единица, что была «в полёте», улетает в никуда, и её придётся качать с нуля при повторном запуске;--partial-dir=.rsync-partial— держит недокачанные файлы в отдельной служебной директории, а не прямо по целевому пути. Это не даёт другим процессам случайно прочитать недописанный файл как готовый и явно показывает, что именно ещё не докачано;--info=progress2— сводный прогресс по всей передаче, а не по одному файлу.
При повторном запуске rsync не сравнивает файлы «есть — нет», а использует алгоритм дельта-передачи: для файла, который отличается по размеру или времени модификации от источника, он берёт то, что уже лежит на приёмнике, как «базовый» файл, режет его на блоки и считает по ним контрольные суммы (быстрая скользящая плюс более строгая), сравнивая их с блоками источника. Пересылаются только несовпавшие блоки. Это справедливо для обычного дозапуска без флагов --inplace/--append — именно они меняют это поведение, и об этом — следующий раздел.
Для отдельных файлов (не целых деревьев) та же идея реализована проще: wget -c и curl -C - докачивают файл с того байта, на котором оборвалась загрузка, ориентируясь на текущий размер локального файла. aria2c умеет то же самое плюс параллельные потоки на один файл. У robocopy в Windows есть режим /Z (restartable mode) для докачки файлов, оборвавшихся посреди передачи по SMB-шаре, и /ZB — тот же режим с откатом на обычное копирование, если не хватает прав на restartable-режим.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГлавная ловушка: докачка поверх повреждённого хвоста
Вот где начинаются реальные проблемы. Файл на приёмнике, оборвавшийся на 92%, не обязательно представляет собой ровно первые 92% корректных байт источника. В точке обрыва возможны варианты:
- последний записанный блок не долетел до диска целиком — питание пропало или процесс убило до
fsync, и на диске оказался мусор или частично перезаписанный блок; - файловая система заранее выделила место под файл (preallocation / sparse file), и «хвост» до места разрыва на самом деле нули, а не данные — размер файла на приёмнике может даже совпадать с ожидаемым, но содержимое — нет;
- сетевая ФС или сетевой диск (NFS, SMB, облачный примонтированный том) держит write-back кеш, который не был сброшен на момент разрыва соединения — клиент думает, что данные записаны, а на самом деле часть из них потеряна.
Ключевой момент: эта порча почти всегда локализована рядом с точкой обрыва, а не разбросана по всему файлу случайным образом. Именно поэтому слепая докачка «доклеить оставшиеся байты в конец» — операция с флагами --inplace или --append в чистом виде — опасна: они спроектированы для максимальной скорости именно за счёт того, что не перепроверяют уже присутствующие байты, а доверяют им и просто дописывают остаток. Если доверие не оправдано (повреждён не только хвост после точки записи, но и сам момент записи), результат — файл правильного размера, который открывается без ошибок, но содержит битый кусок ровно там, где случился обрыв. Мы уже разбирали похожий сценарий — rsync отчитался об успехе, а на приёмнике другой файл: нулевой код возврата инструмента подтверждает, что процесс передачи завершился штатно, а не то, что содержимое побайтово совпадает.
Обычный дозапуск rsync без --inplace/--append от этой проблемы защищён по конструкции: раз он всё равно пересчитывает контрольные суммы блоков существующего файла и сравнивает их с блоками источника, повреждённый участок просто не совпадёт по контрольной сумме и будет переслан заново — независимо от того, где именно он находится. Цена этой защиты — время и I/O на пересчёт блоков уже присутствующих данных, что для действительно больших файлов (сотни гигабайт и больше) может быть заметно медленнее, чем режим --append.
Шаг 1: что реально долетело, а что нет
Прежде чем что-то докачивать, нужно понять фактическое состояние на приёмнике.
Для целого дерева каталогов — сухой прогон без реальной передачи данных:
rsync -an -a --stats source/ user@dest:/path/dest/
Флаг -n (dry-run) покажет список файлов, которые rsync собирается передать, и сводную статистику по объёму — без единого байта реальной передачи. Это быстрый способ увидеть масштаб: один недокачанный файл на 40 ГБ или тысяча мелких файлов, которые не успели стартовать.
Для конкретного подозрительного файла — сравнение размеров на обеих сторонах:
stat -c '%s %n' /src/big.img
ssh user@dest stat -c '%s %n' /dst/big.img
Если размер на приёмнике меньше размера на источнике — файл однозначно был «в полёте» в момент обрыва, и именно с ним нужно разбираться отдельно от остального дерева. Если вы запускали исходную передачу с --log-file=..., лог покажет, какой файл был последним начатым (и не отмеченным как завершённый) — это экономит время на поиск на больших деревьях с тысячами файлов.
Шаг 2: проверка уже скопированного куска перед докачкой
Это тот самый шаг, который нельзя пропускать. Прежде чем дописывать остаток, убедитесь, что уже присутствующий на приёмнике кусок побайтово совпадает с тем же диапазоном источника:
# сколько байт реально долетело
SIZE=$(stat -c%s /dst/big.img)
# контрольная сумма первых SIZE байт источника и всего файла на приёмнике
head -c "$SIZE" /src/big.img | sha256sum
sha256sum /dst/big.img
Суммы совпали — префикс цел, можно безопасно дописывать остаток. Не совпали — доверять этому куску нельзя: докачка --append поверх него не исправит проблему, а зафиксирует её внутри итогового файла без единой ошибки на экране.
Делать эту проверку руками вручную для каждого файла неудобно, поэтому у rsync есть встроенный режим --append-verify: перед тем как продолжить передачу с места обрыва, он сверяет контрольную сумму уже присутствующих данных на обеих сторонах, и если она не совпадает — не станет слепо дописывать поверх, а перезальёт файл целиком. Это отличается от простого --append, который такую сверку не делает вовсе и рассчитан на случаи, где сохранность существующего префикса гарантирована другим способом (например, файл на источнике гарантированно неизменяемый лог, который просто дописывается).
Учитывайте, что сама сверка на большом файле — не бесплатная операция: чтобы проверить 900 ГБ из терабайтного файла, нужно прочитать и захешировать эти 900 ГБ на обеих сторонах. Это ограниченная по времени, разовая цена — в отличие от риска молча получить битые данные в результате.
Похожая ловушка есть и с самими контрольными суммами: совпадение суммы не всегда означает то, что кажется на первый взгляд, если сумма считалась не в тот момент или не с тем набором данных, что нужно — подробнее в статье «Контрольная сумма сошлась, а файл битый: когда MD5 вас обманет».
Шаг 3: докачка и финальная верификация всего результата
Когда ясно, что делать с проблемными файлами (докачивать с проверкой или перезаливать целиком), запускаем основную докачку:
rsync -a --partial --partial-dir=.rsync-partial \
--append-verify --info=progress2 \
--log-file=/var/log/resume-$(date +%F).log \
/src/ user@dest:/dst/
На нестабильном канале имеет смысл обернуть команду в цикл повтора — rsync можно прерывать и перезапускать сколько угодно раз, каждый следующий запуск продолжит с того места, на котором остановился предыдущий:
until rsync -a --partial --partial-dir=.rsync-partial --append-verify \
/src/ user@dest:/dst/; do
echo "оборвалось, пробуем снова через 30с"
sleep 30
done
Нулевой код возврата после такого цикла означает, что процесс передачи завершился штатно — но не то, что содержимое итогового дерева побайтово совпадает с источником. Финальная проверка обязательна, и она должна быть отдельным шагом, а не предположением:
rsync -an --checksum --itemize-changes /src/ user@dest:/dst/ | tee /var/log/verify.log
Флаг --checksum заставляет rsync сравнивать файлы по содержимому (через контрольные суммы), а не по быстрой эвристике «размер и время модификации совпали — значит, файл не трогаем». Именно быстрая эвристика — причина, по которой одиночный rsync -a без -c может молча посчитать повреждённый, но правильного размера файл готовым и пропустить его при следующем запуске. Пустой вывод --itemize-changes означает, что расхождений действительно нет. Непустой — список конкретных файлов, которые всё ещё отличаются, и именно с ними нужно разбираться точечно, а не перезаливать весь массив.
На объёмах в единицы и десятки терабайт полный проход с пересчётом контрольных сумм сам по себе занимает часы — однопоточный обход всего дерева с хешированием каждого байта не бесплатен. Если это узкое место, стоит посмотреть на параллельную и блочную проверку — подробнее в статье «sha256 от четырёх терабайт считается шесть часов: как проверять быстрее».
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
А что, если у меня вообще нет rsync — только исходный диск и Windows-шара?
Для докачки отдельных файлов через SMB подойдёт robocopy /Z (restartable mode) — он умеет продолжать с точки обрыва без повторной передачи уже переданных блоков файла. Финальную сверку всё равно стоит делать отдельно, вручную считая контрольные суммы или через robocopy /L для проверки без реального копирования.
Можно ли докачивать одно и то же дерево много раз подряд, если канал совсем нестабильный?
Да, в этом и смысл резюмируемых инструментов — они не хранят состояние сессии, а каждый раз заново сверяют текущее состояние приёмника с источником. Цикл с повтором и паузой между попытками — рабочий паттерн именно для нестабильных линков.
Обязательно ли делать финальную проверку контрольными суммами, если rsync и так использует их внутри при передаче?
Да, потому что внутренние контрольные суммы дельта-алгоритма проверяются только для файлов, которые rsync вообще решил передавать. Файлы, которые quick-check посчитал уже совпадающими по размеру и времени модификации, в передачу вообще не попадают — и если один из них на самом деле повреждён (например, из-за проблемы на диске приёмника, не связанной с текущим обрывом), обычный дозапуск это не увидит. Отдельный проход с --checksum — единственный способ закрыть этот пробел.
Что делать, если сама докачка каждый раз обрывается на одном и том же файле?
Это обычно сигнал не о сетевой проблеме, а о проблеме с самим файлом или носителем — повреждённый сектор на источнике, файл, который кто-то продолжает менять во время копирования, или лимит по размеру файла у файловой системы приёмника. Стоит проверить именно этот файл отдельно (dd с conv=noerror,sync для чтения с источника, если подозреваете bad-блок) прежде чем гонять его в цикле докачки бесконечно.
А если копирование идёт не rsync, а через облачный CLI (например, синхронизацию в объектное хранилище)?
Концепция та же: у таких инструментов обычно есть свой механизм докачки многочастевой загрузки и своя команда сверки (что-то вроде check в rclone или сравнение по ETag/хешу у объектных хранилищ) — но реализация уже проприетарная, поэтому надо явно проверять в документации конкретного инструмента, есть ли верификация уже загруженной части, а не полагаться на общее предположение «раз он резюмирует — значит, и проверяет».
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →