Принципы работы 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() происходит, чтобы в БД отобразились внесенный пользователем изменения и выполняемый запрос мог их считать.