Транзакции графа объектов copy-on-write

THotPDF даёт вложенные savepoint для загруженных или собранных графов косвенных объектов без клонирования документа на старте транзакции

Жизненный цикл транзакции

PDF.BeginGraphTransaction('metadata edit');
try
  Info.AddStringValue('Producer', 'HotPDF');
  PDF.CommitGraphTransaction;
except
  PDF.RollbackGraphTransaction;
  raise;
end;

BeginGraphTransaction индексирует текущие косвенные корни и ставит наблюдателей мутаций, но не создаёт снимков; первая запись в существующий косвенный корень в этом кадре создаёт один снимок, а последующие записи в тот же корень переиспользуют его

CommitGraphTransaction оставляет текущий граф и отбрасывает или передаёт снимки без клонирования; RollbackGraphTransaction восстанавливает корневые объекты на месте, удаляет объекты, добавленные после savepoint, и восстанавливает выделение номеров объектов

Вложенные транзакции — независимые savepoint; внутренний откат возвращает к состоянию на входе во внутреннюю, а внутренний commit сохраняет достаточно истории для последующего внешнего отката

Автоматическая и явная подготовка к записи

THPDFDictionaryObject.AddValue, ReplaceValue и DeleteValue, THPDFArrayObject.AddObject и SetIndexedItem, а также изменения THPDFStreamObject.Length сами уведомляют объемлющий косвенный корень

Публичные скалярные поля и сырые байты stream не могут перехватить присваивание; вызывайте PrepareLoadedObjectForWrite перед записью в Value, изменением публичных флагов объекта, заменой указателей графа или записью через THPDFStreamObject.Stream

PDF.BeginGraphTransaction('raw stream edit');
try
  PDF.PrepareLoadedObjectForWrite(StreamObject);
  StreamObject.Stream.Size:= 0;
  StreamObject.Stream.WriteBuffer(Bytes[0], Length(Bytes));
  StreamObject.Length:= Length(Bytes);
  PDF.CommitGraphTransaction;
except
  PDF.RollbackGraphTransaction;
  raise;
end;

Память и производительность

Память снимков растёт по числу затронутых косвенных корней, а не по общему размеру документа; крупные payload stream после GraphTransactionMemoryThreshold (по умолчанию 16 MiB) вытесняются в собственные временные файлы

Откат подменяет сохранённые записи словарей, элементы массивов и хранилище stream обратно в существующий корень, избегая второго глубокого копирования; указатель корня остаётся стабильным, но указатели на прямых потомков после отката нужно получить заново

GetGraphTransactionStatistics возвращает Active, глубину вложенности Depth, SnapshotCount, AddedObjectCount, резидентные и вытесненные байты снимков и пиковые резидентные байты снимков

THPDFBeforeMutationEvent — низкоуровневая сигнатура наблюдателя, через которую мутации контейнеров и жизненного цикла маршрутизируются в снимок объемлющего косвенного корня

Область действия

Транзакции покрывают граф косвенных объектов, состояние жизненного цикла, добавленные корни и выделение номеров объектов; указатели вызывающего, массивы порядка загруженных страниц, кэши рендеринга и прочее побочное состояние фасада не журналируются

Пока транзакция активна, не удаляйте, не переставляйте и не заменяйте существующие записи в GetLoadedIndirectObjects; используйте методы графовых контейнеров и после отката заново получите прямых потомков