Началось с жалобы на битые картинки в одной статье корпоративной вики. Обычное дело: кто-то что-то не так вставил.
В тот же день выяснилось, что пропали почти все картинки, загруженные за пять месяцев. На следующий день закончилась последняя проверка уцелевших каталогов, и стало ясно, что резервные копии в этой истории бесполезны: самая старая из них сделана больше чем через неделю после того, как файлов не стало.
Разбор ниже: как файлы исчезали, почему этого долго не замечали, как проверялись пути восстановления, и какая одна проверка поймала бы всё это в феврале.
Сервис - корпоративная вики крупной розничной сети, и сама она не на 1С. Но устроена она так же, как база 1С с присоединёнными файлами в томах на диске: запись о файле в базе, сам файл отдельно. Если у вас файлы хранятся в томах, ближе к концу есть раздел про ваш случай, с отчётом из Библиотеки стандартных подсистем (БСП) и запросом.
Записи есть, файлов нет
Первым делом сравнили две величины, которые обязаны сходиться.
В базе 4 016 записей о вложениях, суммарно 552 мегабайта. Это картинки, вставленные в статьи: у каждой есть запись с ключом, размером и типом.
В хранилище на диске 662 файла, суммарно 89 мегабайт.
Разница - 3 354 записи, за которыми нет ничего. И вторая деталь, которая сразу сказала больше первой: старейший уцелевший файл датирован восьмым июля. Всё, что загружали раньше, отсутствует целиком.
Порча диска или выборочная потеря дают дыры вразброс. Ровная граница по дате означает событие, после которого файлы начали сохраняться.
Корень: настройка "хранить локально" без места хранения
Приложение работало в контейнере, то есть в изолированной среде запуска со своей файловой системой, которая живёт только до пересоздания контейнера. Пересоздают его при обновлении сервиса и при смене настроек. Всё, что записано внутрь и не выведено наружу, при этом исчезает.
Чтобы файлы переживали пересоздание, каталог хранения выводят наружу - подключают к нему постоянное хранилище с хоста. В настройках приложения при этом стоит "хранить файлы локально" и указан путь.
Так и было настроено, кроме одного шага. Хранилище с хоста завели четвёртого февраля и не подключили к контейнеру. До восьмого июля оно простояло пустым.
Путь в настройках существовал, приложение исправно в него писало, только писало оно внутрь контейнера. Каждое пересоздание стирало всё загруженное с прошлого раза.
Настройка "хранить локально" плюс неподключённое хранилище на деле означает "не хранить вовсе", причём с отложенным эффектом. Приложение работает, файлы отдаются, всё выглядит исправно до первого пересоздания.
Почему этого долго не замечали
Потому что база оставалась целой.
Запись о вложении никуда не девалась. Пользователь открывает статью, браузер запрашивает картинку по ключу, приложение находит запись, честно отдаёт перенаправление на файл - и только на последнем шаге приходит "не найдено".
Отказ приходит на конце цепочки, при обращении к конкретному файлу. Проверка работоспособности сервиса его не видит: сервис и база отвечают, страницы открываются. Заметить может только человек, который открыл статью с картинкой.
Почему такие люди не подняли тревогу раньше, в разборе не записано. Статьи при этом читали: у самой просматриваемой из пострадавших 120 просмотров. Моё предположение - каждый считал свою битую картинку единичным случаем, но это предположение.
Целостность базы создаёт полную иллюзию сохранности файлов. Та же иллюзия встретится в разделе про 1С.
Масштаб
284 живых статьи остались без изображений. Разбивка по разделам вики: 79, 47, 45, 27, 20, 18, 6 и ещё 42 в прочих.
Средний размер у записей и у уцелевших файлов почти один. 552 мегабайта на 4 016 записей - это 137 килобайт на вложение, 89 мегабайт на 662 файла - 134 килобайта. Расхождение два процента. Значит, июльские загрузки по размеру не отличаются от февральских, и потерянный объём можно оценить по записям: около 463 мегабайт.
Ещё одно число, которое легко неверно сравнить с предыдущими. Записей без файлов 3 354, а у живых, неархивных документов всего 1 748 ключей вложений, и без файлов из них 1 741. Остальные записи к живым документам не привязаны, поэтому ущерб для читателей вики считается по 1 741, а не по 3 354. Откуда это число, разберу ниже.
Три пути восстановления: два закрыты, третий зависит от авторов
Восстановить с серверов не удалось, но проверено было всё, и по каждому пути есть доказательство.
Путь первый: резервные копии
Копии виртуальной машины хранились семь дней. Разбор шёл 23 июля, значит самая старая доступная копия была примерно от шестнадцатого.
Потеря завершилась восьмого июля - последним пересозданием контейнера, после которого хранилище наконец подключили.
Между концом потери и началом окна восемь дней. Все копии сделаны уже после неё, и ни одного из пропавших файлов в них нет.
Копии при этом были и делали то, для чего заведены. Недельное окно защищает от ошибки, которую заметили в течение недели. Эту заметили почти через полгода после начала и через две недели после конца.
Путь второй: следы на дисках
Мёртвых контейнеров на хосте не осталось, остатков старых слоёв файловой системы контейнера с загрузками тоже. Второе хранилище, оставшееся от ранней неудачной попытки настройки, тоже пустое. Искать на сервере было нечего.
Оставалась последняя зацепка. В хранилище нашлись два каталога, похожих на резервные копии: 368 файлов в одном и 361 папка с файлами в другом.
Проверили по ключам. Взяли 1 748 ключей вложений живых документов и поискали каждый: в рабочем хранилище, в первом каталоге и во втором. На месте оказалось 7. Из первого каталога - ноль совпадений, из второго - тоже ноль. Не найдено 1 741.
Семь найденных из 1 748 при 662 уцелевших файлах выглядят странно, но сходятся: пропало всё, что загружено до восьмого июля, а живые документы почти целиком ссылались на старые вложения. К каким документам относятся остальные уцелевшие файлы, разбор не раскладывал.
Оба каталога содержали файлы почти только одного пользователя, и почти все они относились к уже архивным документам. К пострадавшим статьям они отношения не имели.
Соблазн в такой ситуации - сказать "нашлись какие-то бэкапы, часть восстановим" и закрыть тему. Сверка по ключам дала ноль совпадений за один проход. Лучше знать это сразу, чем месяц обещать людям, что что-то восстановится.
Путь третий: оригиналы у авторов
Из 284 пострадавших статей лишь несколько приходят из разделов, которые синхронизируются извне. Эти восстановятся сами.
Остальные написаны руками, и скриншоты в них вставляли прямо в редактор вики. Других копий в системах нет. Единственный источник - то, что сохранилось у самих авторов, и сколько там сохранилось, неизвестно.
Практический вывод простой: картинка, вставленная прямо в редактор, часто существует в одном экземпляре, и этим экземпляром становится хранилище вики.
Что сделали и вторая иллюзия потери
Хранилище перевели на объектное - внешнее по отношению к приложению, переживающее любое пересоздание. Уцелевшие 662 файла перенесли туда.
И тут же получили вторую потерю, которой не было.
Файлы перенесли утилитой копирования, и у файлов без расширения в имени она не проставила тип содержимого. Хранилище стало отдавать их с общим двоичным типом и заголовком, который запрещает браузеру угадывать тип по содержимому. Картинки перестали отображаться.
Со стороны пользователя картина та же, что и при настоящей пропаже: ссылка живая и файл на месте, а изображения нет. Вторая иллюзия потери поверх настоящей.
Лечится дозаписью типа - он хранится в базе, в той самой записи о вложении. Дописали 657 объектам из 662. После любой миграции файлов стоит открыть несколько штук в браузере: код ответа "успешно" ещё не значит, что картинку покажут.
Заодно всплыло смешанное содержимое: вики отдавалась по защищённому протоколу, хранилище по обычному, браузер такое блокирует. Решили без новой записи в DNS: то же имя на другом порту с уже имеющимся сертификатом.
Проверка, которая поймала бы всё это в феврале
Сравнить количество записей о вложениях в базе с количеством файлов в хранилище.
Четвёртого февраля завели хранилище. С февраля же пошли записи о вложениях. Хранилище оставалось пустым. Одно сравнение двух чисел или даже двух дат - и вопрос закрывается за минуту.
Этот вопрос не задавали почти полгода: мониторинг следит за живостью сервиса, а согласованность двух хранилищ в него не входит.
Важная оговорка про то, где считать. Если считать файлы по пути, который видит приложение, то есть изнутри контейнера, всё сходится до первого пересоздания. Считать надо там, где файлы обязаны лежать, в подключаемом хранилище на хосте. Там с февраля был ноль.
Практическая форма проверки такая. Раз в сутки берём число записей о вложениях, число файлов в хранилище и разницу между ними. Счётчик показывает, сколько пропало. Плюс синтетический зонд: взять случайный ключ из базы и попробовать скачать файл. Зонд проверяет, что вся цепочка выдачи файла доходит до конца.
Ни то, ни другое на момент разбора не внедрено.
В 1С та же развилка: тома хранения файлов
Раздел для тех, у кого присоединённые файлы хранятся в томах на диске. Если у вас файлы лежат в самой информационной базе, можно пропустить: файл уезжает в резервную копию базы вместе со своей записью.
С томами устройство то же, что у вики. Запись о файле живёт в справочнике: у справочника "Версии файлов" и у каждого справочника присоединённых файлов есть реквизиты "Тип хранения файла", "Том" и "Путь к файлу". Сам файл лежит в каталоге тома, на диске сервера или в сетевой папке. В Управлении торговлей (УТ) на БСП 3.1.11, где я это проверял, справочников присоединённых файлов 142, и у всех 142 эти три реквизита есть.
Значит и класс отказа тот же. Первый сценарий: копию базы снимают каждую ночь, а каталог тома в задание копирования не включили. Рабочая база при этом живёт спокойно, отказ появится только после восстановления из копии, когда записи вернутся, а файлы нет.
Второй сценарий: том перенесли на другой диск или сетевую папку подключили заново под другим именем, а путь в карточке тома остался прежним. Для уже загруженных файлов база целая и карточки открываются. Отказ приходит в момент, когда пользователь открывает сам файл.
И тот же вывод про копии: резервная копия информационной базы файлов из томов не содержит. Если каталог тома не копируется отдельно, у вас "бэкапы были" в том же смысле, что и в этой истории.
Руками сверять ничего не нужно: в БСП для этого есть отчёт "Проверка целостности тома", он открывается из списка томов хранения файлов и из карточки тома. Отчёт раскладывает файлы по статусам: "Целостные данные", "Отсутствуют данные в томе на диске" и "Лишние файлы (есть на диске, но сведения о них отсутствуют)". Второй статус и есть вики из этой статьи: запись есть, файла нет.
Отчёт запускают, когда подозрение уже появилось. Для ежедневного счётчика, как в разделе выше, хватает числа записей по томам:
ВЫБРАТЬ ВерсииФайлов.Том КАК Том, КОЛИЧЕСТВО(*) КАК Записей ИЗ Справочник.ВерсииФайлов КАК ВерсииФайлов ГДЕ ВерсииФайлов.ТипХраненияФайла = ЗНАЧЕНИЕ(Перечисление.ТипыХраненияФайлов.ВТомахНаДиске) СГРУППИРОВАТЬ ПО ВерсииФайлов.Том
Запрос проверен на той же УТ, отрабатывает без ошибок. Для присоединённых файлов он пишется к их справочникам. Число файлов в каталоге тома даёт НайтиФайлы(КаталогТома, "*", Истина), вызванный на сервере под учётной записью, у которой есть доступ к каталогу. Из результата надо отбросить подкаталоги проверкой ЭтоФайл(): том раскладывает файлы по вложенным папкам.
Точного совпадения от такого счётчика не ждите, у него другая задача: разница между записями и файлами не должна расти. Выросла за сутки - повод открыть отчёт целостности, пока окно копий каталога тома ещё не закрылось.
Первый сценарий этот счётчик на рабочей базе не поймает, там всё сходится. Его ловит только та же сверка на базе, восстановленной из копии вместе с копией каталога тома. Если тестовая база у вас и так разворачивается из свежей копии прода, достаточно запускать отчёт целостности там.
Есть проверка ещё дешевле: дата самого старого файла в каталоге тома. В вики она сразу показала бы восьмое июля при записях с февраля.
Границы применимости
Механизм работает для связки "локальное хранение плюс контейнер". На объектном хранилище его нет по построению, поэтому туда и переехали.
Целостность базы не гарантирует наличия файлов. Обратное - отсутствие записи при наличии файла - в этом разборе не проверялось.
Промах копий - свойство недельного окна и позднего обнаружения. С окном длиннее в старых снимках нашлись бы файлы, которые дожили до ближайшей ночной копии. Собирать их пришлось бы из многих снимков, и сколько бы нашлось, этот разбор не мерил.
Не проверено, есть ли такая же ошибка подключения хранилища у соседних сервисов на том же хосте.
Из неизмеренного: сколько людей это реально задело и как часто открывались пострадавшие статьи, чисел по всей выборке нет. Сколько авторов перезалили оригиналы после, тоже неизвестно.
Открытый вопрос
Спор тут напрашивается про зону ответственности: если хранение настраивал разработчик, а хранилище подключал администратор, формально каждый сделал свою часть. Мне интереснее другое - должно ли приложение отказываться стартовать, если каталог хранения не является точкой подключения внешнего хранилища? Проверка тривиальная, а цена её отсутствия здесь - пять месяцев картинок.
Для 1С вопрос звучит так: включён ли у вас каталог тома в то же задание копирования, что и база, и когда вы последний раз запускали отчёт проверки целостности тома?
Из той же серии про копии, которым верят не проверяя. Здесь копии были, но начались позже потери. В соседнем разборе копия снималась каждый день, только не с той базы, и там же замерено, сколько на самом деле длится восстановление: Резервная копия делалась каждый день. Не той базы.
Другие наши инструменты:
- Тестовая база из бэкапа рабочей, на автомате - разворачивает копию рабочей базы по расписанию. Каждый такой прогон заодно доказывает, что копия восстанавливается, и на этой базе удобно запускать отчёт целостности тома.
- Карта объёмов базы 1С - из чего база состоит и какие таблицы дают ей размер. Помогает решить, держать файлы в базе или выносить в тома.
- Чек-ап СУБД под 1С - настройки сервера баз данных, включая модель восстановления и обслуживание. Смотреть до того, как понадобится копия.
- Чек-ап чистоты базы 1С - сколько займёт уборка базы и что при ней сломается.
Вступайте в нашу телеграмм-группу Инфостарт