![]() |
#1 |
Участник
|
Расчет сводника
Добрый день.
Периодически (не каждый день) расчет сводника прерывается с ошибкой "Невозможно отредактировать запись в Чистые потребности (ReqTrans). Возник конфликт обновления из-за того, что другой пользовательский процесс выполняет удаление записи или изменение одного или нескольких полей в записи.", ну далее "Покрытие номенклатуры частично обновлено.", а у помощников соответственно "Главный процесс помечен как процесс, в котором произошел сбой". АХ2009. Считаем с помощниками, кол-во пробовали менять, чаще используем 4-6 штук. В методе ReqTrans.update() сделали стандартную конструкцию оптимист.модели, чтобы хоть что-то понять, а также поймать ошибку и вывести номенклатуру на которой крашится - не помогло, номенклатуры разные. X++: catch (Exception::Deadlock) { retry; } catch (Exception::UpdateConflict) { if (appl.ttsLevel() == 0) { if (xSession::currentRetryCount() < #RetryNum) { retry; } } else { warning(strfmt("Ошибка при обновлении ном-ры %1", this.ItemId)); throw Exception::UpdateConflict; } Стек расчета стандартный X++: (S)\Data Dictionary\Tables\ReqTrans\Methods\update,57, (S)\Data Dictionary\Tables\ReqTrans\Methods\deleteExplosionCoverageTrans,19, (S)\Data Dictionary\Tables\ReqTrans\Methods\deleteExplosionCoverage,53, (S)\Classes\ReqCalc\deleteItemRequirement,69, (S)\Classes\ReqCalcScheduleItemTable\insertDataRegeneration,88, (S)\Classes\ReqCalcScheduleItemTable\insertData,23, (S)\Classes\ReqCalc\updateDat,10, (S)\Classes\ReqCalc\run,15, (S)\Classes\ReqCalcScheduleItemTable\run,52, (S)\Classes\ReqProcessExternThread\initAndRunReqCalc,15, (S)\Classes\ReqProcessExternThread\run,27, (S)\Classes\BatchRun\runJobStatic,63 |
|
![]() |
#2 |
Участник
|
Нормально.
Exception::UpdateConflict не прерывает транзакцию (поэтому appl.ttsLevel() > 0) и обрабатывается "ближайшим" try..catch внутри транзакции. Поэтому при обработке этого исключения в catch делают перехват и вызов другого типа исключения Exception::UpdateConflictNotRecovered Посмотрите пример в классе \Classes\Tutorial_RunbaseBatch\run То же самое справедливо и для Exception::DuplicateKeyException Try Catch в методе update на таблице
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
|
За это сообщение автора поблагодарили: Player1 (1). |
![]() |
#3 |
Moderator
|
В общем - дело давнее: Насколько я помню, в стандартном сводном DAX2009 была бага, связанная с кэшированием чистых потребностей. Когда происходит вызов reqTrans.update() туда как второй (насколько я помню) параметр передается ссылка на класс ReqTransCache, где кэш с чистыми потребностями хранится. И после обновления, reqTrans.update() складывает в reqTransCache обновленную копию записи.
При этом где-то как раз в недрах методов deleteExplosionCoverage и deleteExplosionCoverageTrans этот второй параметр не передается и в reqTransCache остаются необновленные данные, при использовании которых и случается updateConflict. (На самом деле - там не update conflict в чистом виде, а просто попытка записать в БД устаревшую копию записи, с неверным recVersion). Я это лечил простым способом - при вызове цепочки deleteExplosion() я новым параметром передавал ReqTransCache и дальше передавал его глубже в reqTrans.update() (и возможно в reqTrans.delete() и reqTrans.insert() - я уже не помню есть там такой параметр или нет). После того как я это сделал, проблема вылечилась и система у клиента уже 12 лет работает. P.S. Поправка - судя по коду D365FO передается не reqTransCache, а reqPlanData. Но из reqPlanData внутри update вынимается ReqTransCache и туда обновленные данные складываются. Но в целом - подход к решению это не меняет. Над убедится что из deleteExplosion* в метод update передается ссылка на правильный класс. Последний раз редактировалось fed; 16.03.2023 в 12:52. |
|
|
За это сообщение автора поблагодарили: Player1 (5). |
![]() |
#4 |
Участник
|
Цитата:
Сообщение от fed
![]() В общем - дело давнее: Насколько я помню, в стандартном сводном DAX2009 была бага, связанная с кэшированием чистых потребностей. Когда происходит вызов reqTrans.update() туда как второй (насколько я помню) параметр передается ссылка на класс ReqTransCache, где кэш с чистыми потребностями хранится. И после обновления, reqTrans.update() складывает в reqTransCache обновленную копию записи.
При этом где-то как раз в недрах методов deleteExplosionCoverage и deleteExplosionCoverageTrans этот второй параметр не передается и в reqTransCache остаются необновленные данные, при использовании которых и случается updateConflict. (На самом деле - там не update conflict в чистом виде, а просто попытка записать в БД устаревшую копию записи, с неверным recVersion). Я это лечил простым способом - при вызове цепочки deleteExplosion() я новым параметром передавал ReqTransCache и дальше передавал его глубже в reqTrans.update() (и возможно в reqTrans.delete() и reqTrans.insert() - я уже не помню есть там такой параметр или нет). После того как я это сделал, проблема вылечилась и система у клиента уже 12 лет работает. P.S. Поправка - судя по коду D365FO передается не reqTransCache, а reqPlanData. Но из reqPlanData внутри update вынимается ReqTransCache и туда обновленные данные складываются. Но в целом - подход к решению это не меняет. Над убедится что из deleteExplosion* в метод update передается ссылка на правильный класс. В ReqTrans.update() X++: public server void update(ReqPlanData _reqPlanData = null, boolean _reduceCovQty = true) { ReqTransCache reqTransCache = _reqPlanData ? _reqPlanData.reqTransCache() : null; X++: if (reqTransCache)
reqTransCache.update(this); X++: server void deleteExplosionCoverageTrans(ReqPlanData _reqPlanData = null) X++: if (reqTrans.isDerivedDemand()) { // reqTrans has to be updated but it could be it has been derived deleted by the previous record in this select statement // we need to ensure the record still exists if ( reqTransCache.exists(reqTrans) || ReqTrans::findRecId(reqTrans.RecId).RecId) { reqTrans.CovQty = 0; reqTrans.update(reqPlanData); } } else if (reqTrans.isPlannedOrder()) { setupDim = reqPlanData.newReqSetupDim(_setup,reqTrans.CovInventDimId); if (reqPlanData.mustKeepPlannedOrder(setupDim,reqTrans,setApprovedPlannedOrder)) { reqTrans.CovQty = 0; reqTrans.update(reqPlanData); } else { reqTrans.deleteExplosionCoverage(reqPlanData); doDelete = true; } } else doDelete = true; |
|
![]() |
#5 |
Moderator
|
Как идея: Попробуйте поставить в reqTrans.update() if(reqTransCache==NULL) breakpoint и посмотрите откуда этот метод без кэша вызывается при сводном планировании.
Возможно еще, что проблема происходит внутри класса ReqTransUpdate. Оно там точно reqTrans обновляет/удаляет/вставляет и, насколько я помню, делает это без reqPlanData. При этом класс этот вызывается при обработке очередной номенклатуры в ReqCalc и возможно это туда надо reqPlanData передавать. (Но может быть это у меня ложные воспоминания). Я просто точно помню что у меня на проекте возникали точно такие же проблемы с обновлением из deleteExplosion и это точно было вызвано неактуальным кэшем. |
|
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|