Принципы работы commit и flush
Contents
Принципы работы commit и flush#
Применение Session.commit, Session.commitWork, flush и flush(true), чтобы не достичь лимита загружаемых ячеек#
session.commit - применяет изменения сессии к базе данных с успешным завершением транзакции, очищает транзакционный кэш после операции, обнуляет счетчик загруженных ячеек.
session.commitWork - применяет изменения сессии к базе данных без завершения транзакции, очищает транзакционный кэш после операции, обнуляет счетчик загруженных ячеек.
session.flush - применяет изменения сессии к базе данных без завершения транзакции. Есть параметр cleanTransactCache, если true очищает транзакционный кэш (загруженные объекты) после операции, обнуляет счетчик загруженных ячеек. Если false (по умолчанию), то происходит только синхронизация данных в БД, загруженные объекты остаются в кеше.
Внимание
Рекомендуется использовать session.commitWork вместо session.flush(true)
session.rollback() - откатывает текущую транзакцию и полностью очищает память.
Оптимальная стратегия коммитов:#
PostgreSQL плохо работает с длинными транзакциями, так как:
Долгие транзакции удерживают соединения и могут привести к нехватке пула соединений
Долгая незавершенная транзакция может блокировать работу других пользователей
Поэтому рекомендуется использовать коммиты:
по окончанию логически завершенного блока операций с изменениями в данных
перед открытием модального окна
избегать коммитов в серверных методах, так как кто-то может вызвать метод в другом контексте и ожидать полный откат при ошибке, а получить лишь частичный, что касается изменения после коммита на методе.
При обработке больших объемов данных, чтобы не достичь лимита загружаемых ячеек, надо сбрасывать пачками данные в БД с очисткой транзакционного кэша при помощи commitWork
Пример:
var counter = 0
for (rv <- new ASelect {
val id: NLongColumn = asNLong("id")
val idBalAcc: NLongColumn = asNLong("idBalAcc")
val nSum: NNumberColumn = asNNumber("nSum")
SQL(sQuery)
on("dExecDate" -> dExecDate, "idaDep" -> LongPgArray(idaDep), "idaEmployee" -> LongPgArray(idaEmployee), "idaAcc"-> LongPgArray(idaAcc.toList), "idBisObj" -> idBisObj, "idDepOwner" -> idDepOwner)
}) {
counter += 1
insertByParent(Asf_InventoryApi().load(idpInventory)) :/ { rop =>
setnOrder(rop, counter.nn)
setidInvNumb(rop, rv.id())
setidAcc(rop, rv.idBalAcc())
setnSum(rop, rv.nSum())
setnQtyRemain(rop, 1.nn)
rop
}
if (counter % 1000 === 0)
session.commitWork()
}
session.commitWork()
Отличия commit и flush#
Действие |
flush() |
flush(true) |
commit() |
commitWork() |
---|---|---|---|---|
Запись в БД |
Да |
Да |
Да |
Да |
Фиксация транзакции |
Нет |
Нет |
Да |
Нет |
Очистка памяти |
Нет |
Частично |
Да |
Да |
Возможность отката |
Да |
Да |
Нет |
Да |
Очистка кэша без коммита#
Для очистки кеша и синхронизации данных с БД рекомендуется использовать метод commitWork(). Он полезен в случаях, когда:
Требуется освободить ресурсы без изменения данных в БД.
Обрабатывается большой объем данных и важно избежать накопления записей в памяти.
Необходимо минимизировать риск потери транзакционной целостности из-за раннего коммита.
Пример использования:
Загрузка объектов из БД частями. Используя реляционный запрос получает значения первичных ключей для массовой прогрузки объектов в память. Количество одновременно загружаемых объектов указывает в параметре chunkSize. Разработчик должен сам очищать память (вызывать commitWork() или commit()) внутри переданного обработчика.
{{{
val ids: List[NLong] = ...
Btk_BulkProcessPkg().chunkedQuery(
SomeClassApi(),
500,
"select id from SomeClass where id = Any({ids})",
Seq("ids" -> LongPgArray(ids)),
Seq(Btk_ObjectAta) // прогрзука коллекций
) { rops =>
rops.foreach { rop =>
//обработка объекта
}
//очистка памяти
session.commitWork()
}
}}}
Автокоммиты#
Автокоммит может происходить в нескольких случаях:
После выполнения пользовательских операций, если они вызвали изменения в базе данных. Операция выполняется, вызывается flush() для записи изменений в БД, а затем происходит commit().
При обновлении данных refresh(). Если в памяти есть несохраненные изменения и вызывается refresh(), система сначала выполняет flush(), фиксирует изменения в памяти и затем отправляет запрос в БД, что приводит к коммиту.
На onRefresh() или onRefreshExt() c реляционным запросом. На открытии выборки происходит коммит, если используется реляционный запрос на onRefresh без аннотации @FlushBefore(mode = FlushBeforeMode.Disabled). Из-за чего могут возникать ситуации, когда происходит неочевидный коммит при открытии отображения с закладкой или выпадающим списком, на которых не указана аннотация.
Примечание
Автокоммит для реляционных запросов на onRefresh() или onRefreshExt() происходит, чтобы в БД отобразились внесенный пользователем изменения и выполняемый запрос мог их считать.