При выполнении объемных переносов данных между конфигурациями (например, при миграции из «Альфа-Авто 3» в «Альфа-Авто 5» с помощью специализированных обработок) разработчики часто сталкиваются с аварийной остановкой процесса. В процессе загрузки промежуточная таблица соответствий tempref.dbf и связанный с ней файл индексов tempref.cdx стремительно увеличиваются в объеме. Как только размер индексного файла достигает ровно 2 147 483 647 байт (2 ГБ), платформа прерывает выполнение с системной ошибкой вида Error#:-30 Determining File Length.
Разберем природу этого ограничения, выясним, почему вспомогательный индекс разрастается быстрее самих данных, и рассмотрим надежные способы решения проблемы в программном коде обработки переноса.
В платформе 1С:Предприятие для работы с файлами формата DBF и CDX используется встроенный объект XBase, базирующийся на 32-битной библиотеке CodeBase. Внутренняя адресация смещений в файле использует знаковое 32-битное целое число (signed 32-bit integer). Максимальное положительное значение для такого типа составляет 2^31 - 1, что в точности соответствует 2 097 152 КБ (2 ГБ).
Снять данное ограничение никакой системной настройкой, конфигурационным флагом или директивой невозможно: это фундаментальный предел используемого формата файлов. Когда размер файла пытается превысить эту отметку, файловый менеджер библиотеки не может вычислить корректную длину файла и генерирует сбой Error#:-30.
Проанализируем, почему индексный файл раздувается до критического объема даже при относительно небольшом исходном объеме переносимых справочников:
Записать(). При неупорядоченном поступлении ключей страницы индекса постоянно расщепляются, освободившиеся блоки не успевают переиспользоваться, и индекс накапливает избыточный служебный «мусор».CDX является составным (Compound Index). Если обработка создает в одном файле сразу несколько тегов по разным полям (например, по коду 7.7, типу объекта и GUID 8.x), размер файла кратно умножается на число этих тегов.Самый простой и эффективный способ уменьшить размер CDX в несколько раз без изменения общей логики переноса — отключить индексацию на момент записи строк. Мы сначала выгружаем все данные в чистый файл DBF, закрываем или отключаем файл индексов, а затем создаем индексы одним действием по полностью готовой таблице.
Посмотрим на пример рефакторинга кода создания временной таблицы:
// Создаем структуру DBF-файла без немедленного подключения CDX
ФайлDBF = Новый XBase;
ФайлDBF.Поля.Добавить("TYPE", "S", 50);
ФайлDBF.Поля.Добавить("ID77", "S", 50);
ФайлDBF.Поля.Добавить("UID8", "S", 36);
ФайлDBF.СоздатьФайл(ПутьКФайлуDBF);
// Заполняем таблицу записями БЕЗ открытых индексов
Для Каждого СтрДанных Из МассивОбъектов Цикл
ФайлDBF.Добавить();
ФайлDBF.TYPE = СтрДанных.Тип;
ФайлDBF.ID77 = СтрДанных.Идентификатор77;
ФайлDBF.UID8 = СтрДанных.Ссылка8Гуид;
ФайлDBF.Записать();
КонецЦикла;
ФайлDBF.ЗакрытьФайл();
// Создаем индекс по уже готовому монолитному файлу
ФайлDBF.ОткрытьФайл(ПутьКФайлуDBF);
ФайлDBF.Индексы.Добавить("MAINKEY", "TYPE + ID77", Ложь, Ложь);
ФайлDBF.СоздатьИндексныйФайл(ПутьКФайлуCDX);
ФайлDBF.ЗакрытьФайл();
При «холодном» построении индекса формируется идеально сбалансированное плотное дерево без пустых узлов и фрагментации. Это сокращает конечный размер файла tempref.cdx в 2–4 раза, что часто позволяет уложиться в 500–800 МБ вместо критических 2 ГБ.
Если алгоритм переноса устроен так, что доступ к уже добавленным записям через поиск по индексу требуется непосредственно в процессе работы, применим порционную обработку. Через каждые 50 000 – 100 000 добавленных строк выполним программную очистку индексного пространства:
Если СчетчикЗаписей % 50000 = 0 Тогда
// Сбрасываем изменения, перестраиваем индекс начисто
ФайлDBF.Сжать();
ФайлDBF.Переиндексировать();
КонецЕсли;
Вызов метода Переиндексировать() пересобирает дерево заново, полностью удаляя фрагментированные страницы и сбрасывая неиспользуемые байты.
В типовых правилах конвертации файл tempref.dbf обычно выступает глобальной свалкой соответствий: туда пишутся одновременно номенклатура, контрагенты, документы, партии и регистры. Разделим один гигантский файл на несколько независимых таблиц:
tempref_nomen.dbf).Разделение на изолированные хранилища гарантирует, что ни один из подчинённых файлов индексов физически не приблизится к лимиту в 2 ГБ.
Использование DBF в современных конфигурациях является устаревшим техническим решением. Если база-приемник работает на современной 64-битной платформе 1С:Предприятие 8.3, временные соответствия гораздо быстрее и безопаснее хранить напрямую в оперативной памяти с помощью объектов Соответствие или индексированной ТаблицыЗначений.
Рассмотрим, как заменить дисковый поиск по CDX на встроенное индексирование в оперативной памяти:
// Инициализация таблицы соответствий в памяти
ТаблицаСоответствий = Новый ТаблицаЗначений;
ТаблицаСоответствий.Колонки.Добавить("ТипОбъекта", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(50)));
ТаблицаСоответствий.Колонки.Добавить("ИД77", Новый ОписаниеТипов("Строка", Новый КвалификаторыСтроки(50)));
ТаблицаСоответствий.Колонки.Добавить("Ссылка8", Новый ОписаниеТипов("ЛюбаяСсылка"));
// Строим системный индекс платформы по ключевым колонкам
ТаблицаСоответствий.Индексы.Добавить("ТипОбъекта, ИД77");
// Быстрый поиск в цикле загрузки без ограничений по размеру
ПараметрыОтбора = Новый Структура;
ПараметрыОтбора.Вставить("ТипОбъекта", "Номенклатура");
ПараметрыОтбора.Вставить("ИД77", ТекИдентификатор77);
НайденныеСтроки = ТаблицаСоответствий.НайтиСтроки(ПараметрыОтбора);
Если НайденныеСтроки.Количество() > 0 Тогда
НайденнаяСсылка = НайденныеСтроки[0].Ссылка8;
Иначе
// Логика создания и добавления нового сопоставления
НоваяСтрока = ТаблицаСоответствий.Добавить();
НоваяСтрока.ТипОбъекта = "Номенклатура";
НоваяСтрока.ИД77 = ТекИдентификатор77;
НоваяСтрока.Ссылка8 = СозданнаяСсылка;
КонецЕсли;
Такой подход не только полностью устраняет ошибку Error#:-30 Determining File Length, но и ускоряет поиск связей в десятки раз за счет исключения дисковых операций ввода-вывода.