Сохранение в поток (постановка)
Требования к новому алгоритму сохранения в поток были первоначально сформированы в результате дискуссии на сайте gsbelarus.com:
- Увеличение скорости минимум в 10 раз;
- Уменьшение размера файла минимум в 10 раз (при использовании двоичного формата);
- Возможность сохранения в дфоичном формате, в тексте и в XML;
- Предусмотреть возможность сохранения только объектов измененных после указанной даты;
- Предусмотреть возможность инкрементного сохранения объектов;
- Предусмотреть возможность расширенного и краткого отображения информации о ходе процесса. Дать пользователю возможность прервать процесс в любой момент времени;
- Предусмотреть возможность запуска процесса из командной строки;
- Предусмотреть возможность сохранения всей базы данных в потоке;
- Процедура должна обрабатывать миллион записей на компьютере с 512 Мб оперативной памяти за приемлемое время;
- Формирование потока данных и загрузка его на базу должна осуществляться не в монопольном режиме.
- Предусмотреть возможность блокировки на изменение записей, переданных на другую базу.
- Реализовать корректный механизм замены и объединения данных объектов.
См. сравнение механизмов репликации и сохранения данных в поток в статье Сохранение в поток и репликация.
Содержание |
Сценарии использования сохранения в поток
| Сценарий | Чего хотим добиться |
|---|---|
| Пользователь выбирает объект или группу объектов и сохраняет их в поток. | Минимум десятикратного ускорения по сравнению с действующим алгоритмом. Меньшего размера файла. Контроля над ходом процесса. Возможности прервать процесс в любой момент времени. |
| Пользователь указывает сохранить в поток объекты, измененные после указанной даты. | Аналогично предыдущему. Система производит в базе данных поиск всех объектов, измененных после указанной даты (как быть со множествами?). |
| Разработчик сохраняет настройку в формате XML. | Текстовый файл, содержащий настройку ввиде форматированного XML, можно сравнить с прежней версией настройки и таким образом определить что было изменено. Такие настройки можно помещать в хранилище StarTeam и использовать систему контроля версий. |
| Пользователь производит загрузку потока на базу данных. | Ускорения процесса. Контроля над ходом выполнения и возможности прервать процесс в любой момент времени. Внятного разрешения конфликтных ситуаций: Обновлять записи всегда, Обновлять записи только если они новее, чем те, что находятся в базе данных, Пропускать записи. Возможности выставить действие по-умолчанию как для всего процесса, так и для объектов конкретного типа. Возможности сравнивать данные объектов при возникновении конфликта перезаписи уже существующего объекта. Возможности слияния данных двух объектов (подобно той, которая существует в механизме объединения двух записей). |
| Пользователь имеет головную базу и несколько перефирийных, на которых происходит редактирование данных. Периодически данные с перефирийных баз передаются на головную для консолидации, причем изменения от головной базы данных не передаются на перефирийные и изменения от одной перефирийной базы данных не передаются на другую. | Возможности инкрементной передачи данных. Т.е. перадаваться должны только объекты измененные (добавленные) с момента последней передачи. Возможности передавать информацию об удаленных записях. |
| Пользователь имеет несколько баз между которыми необходим полный обмен информацией (репликация). | Замены внешней утилиты репликации. Передачи только инкремента данных. Разрешения конфликтов в автоматическом и/или ручном режиме. |
Увеличение скорости
Беглый анализ показывает, что основной проблемой существующего сохранения является использование рекурсии и бизнес объектов. Сейчас работает так:
- начинаем сохранять запись определенного типа (запись бизнес-объекта);
- создаем клиент датасет и переносим в него данные полей сохраняемой записи;
- записываем в поток служебную информацию и данные клиент датасета;
- находим поля-ссылки, создаем экземпляры бизнес-объектов соответствующего типа и рекурсивно вызываем процедуру сохранения в поток, т.е. переходим к п. 1.
Таким образом, в процессе сохранения объект одного и того же типа может создаваться неограниченное число раз.
Почему использование бизнес-объекта замедляет процесс?
- Бизнес-объект является наследником от класса TIBDataSet и уже в силу этого он медленее при обращении к данным чем легковесный TIBSQL;
- Формирование, последующий парсинг и переформирование запроса бизнес-объекта занимают значительное время;
- При создании экземпляра бизнес-объекта в скрипт-контрол загружаются скрипт-функции перекрытых методов;
Использование пула объектов сведет к минимуму затраты по первым двум пунктам, но проблема отключения макросов для конкретного объекта остается не решенной.
Уменьшение размера файла
Уменьшения размера файла мы добьемся за счет отказа от рекурсии при формировании потока данных из-за которой мы имеем следующую внутреннюю структуру файла:
Служебная информация и заголовок датасета записываются практически перед каждой записью, что существенно раздувает файл. В новом формате, записи одного типа будут сгруппированы:
Реализация
Переработанный алгоритм сохранения в поток
Нам понадобятся следующие структуры данных:
| BO | Список (пул) бизнес-объектов; |
| CL | Список клиент-датасетов, где будет храниться информация бизнес-объектов. Списки BO и CL содержат всегда одинаковой количество элементов. Бизнес объекту с номером N из списка BO соответствует клиент-датасет с таким же номером из списка CL. |
| L | Список идентификаторов записей, помещенных в поток. |
| Q | Список с номерами объектов из пула BO, показывающий очередность их помещения в поток. |
Концептуально, алгоритм сохранения в поток выглядит следующим образом:
- Вызывается метод SaveToStream2, куда передается:
- поток для записи данных;
- список закладок, если надо сохранить выделенные пользователем записи. Список можно не указывать, тогда то, какие записи будут сохранены, определяется четвертым параметром;
- детальный датасет (если есть). Изначально передача детального датасета предназначалась для копирования таких сущностей, как накладная, где мы должны скопировать шапку (мастер) и позиции (детальный датасет). Но для любой сущности могут появиться дополнительные информационные таблицы (бизнес-объект -- справочник пользователя), т.е. таблицы связанные 1-1, которые также необходимо скопировать. Мы решали эту проблему в коде дублирования записи;
- флаг сохранять все записи или только текущую.
- В зависимости от настроек внутри метода организуется цикл по записям (выделенным или всем) или обрабатывается только текущая запись.
- Для каждой записи вызывается метод _SaveToStream2, куда передается:
- Идентификатор записи;
- Тип и подтип записи;
- Метод _SaveToStream2 проверяет в списке L не был ли сохранен уже объект с таким идентификатором. Если такой объект уже был сохранен, то метод завершает свое выполнение.
- Проверяем наличие RUID у объекта. Если нет, то формируем его.
- Если объект еще не сохранялся, то проверяется пул BO на наличие объекта нужного нам класса и подтипа. Если нашли, то берем его номер. Если нет, то создаем объект и помещаем в пул. В соответствие этому объекту создаем клиент-датасет и помещаем его в список CL. Бизнес объекту устанавливаем SubSet=ByID. Присваиваем идентификатор и открываем бизнес объект. Если датасет пуст, то имеем ошибку о чем и сигнализируем с помощью исключения.
- Записываем в список L идентификатор сохраняемого объекта, а в список Q -- номер объекта в списке BO.
- Добавляем запись в сопоставленный клиент-датасет из списка CL и заполняем поля значениями полей бизнес-объекта.
Writer -- объект для записи данных
В существующем алгоритме, код записи информации в поток перемешан с кодом формирования данных для записи. Планируется, отделить его, выделив отдельный абстрактный класс Writer, от которого будут наследоваться BinaryStreamWriter, XMLWriter, TextWriter.
Тестирование
В основу методики тестирования положено сравнение между собой двух баз, основанных на единой, третьей, исходной базе.
- Сначала подготавливается исходная база. Это может быть либо совершенно чистый эталон, либо база с загруженными настройками, либо произвольная база с настройками и данными.
- Далее, делаются две копии этой базы: A и B.
- На базе А создаются объекты данных, которые затем сохраняются в поток и записываются в дисковый файл.
- Данный файл загружается на базу B.
- По окончании загрузки базы сравниваются между собой (например, с помощью утилиты сравнения данных программы IBExpert). Работа алгоритма сохранения в поток считается корректной, если результирующие базы идентичны между собой за исключением значений первичных ключей.
Тестирование одно/двунаправленной репликации между N базами осуществляется аналогичным образом. По окончании полного круга репликации все N баз должны быть идентичны между собой.
Подлежит тестированию:
- Сохранение простого одинарного объекта (одна запись в таблице без внешних ссылок).
- Сохранение нескольких объектов одного типа.
- Сохранение нескольких древовидных объектов. Сохраняются элементы, промежуточные в иерархии, проверяется сохранение родительских и вложенных элементов.
- Сохранение данных следующих типов: целое число (короткое и длинное), число с фиксированной точкой, число с плавающей точкой, строка, дата, время, дата и время, двоичный объект. Для строки проверяется корректность сохранения в случае содержания символов: кавычка одинарная, кавычка двойная, знаки больше и меньше, а также проверяется сохранение символов кирилического алфавита.
- Сохранение циклических ссылок между двумя записями в двух таблицах. Например, компания и расчетный счет.
- Сохранение циклических ссылок между двумя записями в одной таблице.
- Сохранение циклических ссылок между тремя записями в трех разных таблицах.
- Сохранение документа с позициями.
- Сохранение группы документа с позициями.
- Сохранение записи и записи из таблицы, присоединенной один-к-одному.
- Сохранение записи и записей из N таблиц, присоединенных последовательно один-к-одному. Как минимум проверить при N = 2.
- Сохранение записи с атрибутами типа множество. Проверить, когда во множестве: ноль, один и несколько элементов. Проверить ситуацию, когда таблица-связка кроме двух ключей содержит еще и дополнительные данные.
- Сохранение не менее миллиона записей разных типов. Проверить, взяв за основу реальную базу данных размером больше 10 Гб. В этом случае получение базы B осуществить следующим образом: взять базу с данными. Сделать ее копию. Определить объекты для переноса и удалить их с копии. Перенести объекты с первой базы на вторую. Сравнить базы.
Необходимо также проверить идентичность работы старого алгоритма сохранения в поток и нового. Для этого:
- Берем эталонную базу с комплексной автоматизацией.
- Сохраняем с нее все настройки используя старый алгоритм.
- Сохраняем с нее все настройки используя новый алгоритм.
- Загружаем настройки на эталон старым алгоритмом.
- Загружаем настройки на эталон новым алгоритмом.
- Сравниваем две базы.
Все процессы по тестированию должны быть автоматизированы и выполняться с помощью макросов, написанных на Гедымине.

