22.07.2011, 18:03 | #1 |
Developer
|
Отмена закрытия склада. Оптимизация.
Здравствуйте!
На форуме подобных исправлений не нашел. Имеем AX2009 RU5 + MS SQL 2008 R2 Отмена склада идет раз в 10 дольше, чем само закрытие. Причина: неумелое использование index hint. Подробности: В методах \Classes\InventCostClosingCancel_WorkInvent\reverseInventoryAdjustments() и \Classes\InventCostClosingCancel_WorkInvent\duplicateSettlements() в запросах используется index hint DateVoucherIdx, что приводит к Key Lookup (он же Clustered Index Seek с LOOKUP). Данный индекс состоит из полей TransDate, Voucher, RecId. Но в запросах также учавствуют условия по таким полям, как ItemId, InventTransCurrency_RU, Cancelled, SettleModel. Для таких условий целесообразней использовать (из существующих) индекс ItemDateIdx. Решение: 1. В методах \Classes\InventCostClosingCancel_WorkInvent\reverseInventoryAdjustments() и \Classes\InventCostClosingCancel_WorkInvent\duplicateSettlements() убрать index hint DateVoucherIdx (или поменять на «более подходящий» индекс). 2. В качестве оптимизации, можно добавить в индекс ItemDateIdx поля Voucher, InventTransCurrency_RU, Cancelled, SettleModel, TransRecId. В результате отмена идет минимум в два раза быстрее самого закрытия (специально не замерял). Выше упомянутые измененные методы X++: /// <summary> /// Reverses the inventory adjustemts on the inventTrans records made during an inventory closing.. /// </summary> protected void reverseInventoryAdjustments() { InventTrans inventTrans; InventSettlement inventSettlement; Map inventTransMap; ; inventTransMap = new Map(Types::Int64, Types::Record); // First of all, select all the inventTrans records that are necessary while select forupdate * from inventTrans where inventTrans.ItemId == itemId join TableId from inventSettlement where inventSettlement.TransRecId == inventTrans.RecId && inventSettlement.ItemId == itemId && inventSettlement.Voucher == cancelClosing.Voucher && inventSettlement.TransDate == cancelClosing.TransDate // <GEEU> && inventSettlement.InventTransCurrency_RU == inventTransCurrency // </GEEU> && inventSettlement.Cancelled == NoYes::No && inventSettlement.TransRecId != 0 && (inventSettlement.SettleModel != InventSettleModel::PhysicalValue || (inventSettlement.SettleModel == InventSettleModel::PhysicalValue && inventTrans.StatusIssue == StatusIssue::Deducted)) { inventTransMap.insert(inventTrans.RecId, inventTrans); } while select sum(QtySettled),sum(CostAmountSettled),sum(CostAmountAdjustment) from inventSettlement // VALY, 22.07.2011 --> // ускоряем отмену закрытия склада // закоментирован код: // index hint DateVoucherIdx // VALY, 22.07.2011 <-- group by TransRecId where inventSettlement.ItemId == itemId && inventSettlement.Voucher == cancelClosing.Voucher && inventSettlement.TransDate == cancelClosing.TransDate // <GEEU> && inventSettlement.InventTransCurrency_RU == inventTransCurrency // </GEEU> && inventSettlement.Cancelled == NoYes::No && inventSettlement.TransRecId != 0 && inventSettlement.SettleModel != InventSettleModel::PhysicalValue { if (inventTransMap.exists(inventSettlement.TransRecId)) { this.updateFinancialCostAmount(inventSettlement, inventTransMap.lookup(inventSettlement.TransRecId)); } else { this.updateFinancialCostAmount(inventSettlement); } } while select forceplaceholders sum(CostAmountAdjustment) from inventSettlement // VALY, 22.07.2011 --> // ускоряем отмену закрытия склада // закоментирован код: // index hint DateVoucherIdx // VALY, 22.07.2011 <-- group by TransRecId where inventSettlement.ItemId == itemId && inventSettlement.Voucher == cancelClosing.Voucher && inventSettlement.TransDate == cancelClosing.TransDate // <GEEU> && inventSettlement.InventTransCurrency_RU == inventTransCurrency // </GEEU> && inventSettlement.Cancelled == NoYes::No && inventSettlement.TransRecId != 0 && inventSettlement.SettleModel == InventSettleModel::PhysicalValue exists join inventTrans index hint RecId where inventTrans.RecId == inventSettlement.TransRecId && inventTrans.StatusIssue == StatusIssue::Deducted { if (inventTransMap.exists(inventSettlement.TransRecId)) { this.updatePhysicalCostAmount(inventSettlement, inventTransMap.lookup(inventSettlement.TransRecId)); } else { this.updatePhysicalCostAmount(inventSettlement); } } } X++: /// <summary> /// Duplicates the inventsettlement records and marks them as canceled. /// </summary> protected void duplicateSettlements() { InventSettlement cancelSettlenent; InventSettlement inventSettlement; InventTrans inventTrans; Voucher newVoucher = inventClosing.Voucher; TransDate newTransDate = inventClosing.TransDate; NoYes notPosted = NoYes::No; NoYes notCanceled = NoYes::No; #LOCALMACRO.InventSettlementFieldsChanged TransRecId, InventTransId, ItemId, newTransDate, // New TransDate newVoucher, // New Voucher SettleTransId, QtySettled, CostAmountSettled, CostAmountAdjustment, BalanceSheetAccount, OperationsAccount, notCanceled, // Canceled = No SettleModel, Dimension, BalanceSheetPosting, OperationsPosting, ItemGroupId, notPosted, // Posted = No SettleType // <GEEU> , inventTransCurrency, MarkupCode_RU // </GEEU> #ENDMACRO ; // First of all, duplicate all the settlements not type physical value inventSettlement.skipDataMethods(true); insert_recordset inventSettlement ( #InventSettlementFields ) select #InventSettlementFieldsChanged from cancelSettlenent // VALY, 22.07.2011 --> // ускоряем отмену закрытия склада // закоментирован код: // index hint DateVoucherIdx // VALY, 22.07.2011 <-- where cancelSettlenent.Voucher == cancelClosing.Voucher && cancelSettlenent.TransDate == cancelClosing.TransDate // <GEEU> && cancelSettlenent.InventTransCurrency_RU == inventTransCurrency // </GEEU> && cancelSettlenent.ItemId == itemId && cancelSettlenent.Cancelled == NoYes::No && cancelSettlenent.TransRecId != 0 && cancelSettlenent.SettleModel != InventSettleModel::PhysicalValue; // Now duplicate all the ones that are physical value and where the transaction is still // not financially updated // First of all, duplicate all the settlements not type physical value inventSettlement.skipDataMethods(true); insert_recordset inventSettlement ( #InventSettlementFields ) select #InventSettlementFieldsChanged from cancelSettlenent // VALY, 22.07.2011 --> // ускоряем отмену закрытия склада // закоментирован код: // index hint DateVoucherIdx // VALY, 22.07.2011 <-- where cancelSettlenent.Voucher == cancelClosing.Voucher && cancelSettlenent.TransDate == cancelClosing.TransDate // <GEEU> && cancelSettlenent.InventTransCurrency_RU == inventTransCurrency // </GEEU> && cancelSettlenent.ItemId == itemId && cancelSettlenent.Cancelled == NoYes::No && cancelSettlenent.TransRecId != 0 && cancelSettlenent.SettleModel == InventSettleModel::PhysicalValue exists join inventTrans index hint RecId where inventTrans.RecId == cancelSettlenent.TransRecId && inventTrans.StatusIssue == StatusIssue::Deducted; // Now I can update all the records that I have created and reverse the signs // Additionally, cancelled will be set to yes, so that this statement does not touch // records that have already been reversed inventSettlement.skipDataMethods(true); update_recordset inventSettlement setting Cancelled = NoYes::Yes, QtySettled = -1 * inventSettlement.QtySettled, CostAmountSettled = -1 * inventSettlement.CostAmountSettled, CostAmountAdjustment = -1 * inventSettlement.CostAmountAdjustment where inventSettlement.Voucher == newVoucher && inventSettlement.TransDate == newTransDate // <GEEU> && inventSettlement.InventTransCurrency_RU == inventTransCurrency // </GEEU> && inventSettlement.Cancelled == NoYes::No && inventSettlement.ItemId == itemId; } Последний раз редактировалось vallys; 22.07.2011 в 19:50. |
|
|
За это сообщение автора поблагодарили: Logger (3), alek_frm (1), Dark Light (2), androzavr (1). |
21.06.2012, 15:14 | #2 |
Участник
|
Не особо помогло. Пошёл уже 8й час отмены закрытия.
Закрывался склад где-то с час. |
|
21.06.2012, 21:59 | #3 |
Участник
|
Отмена в пакете идет намного быстрее за счет распараллеливания. Попробуйте.
|
|
22.06.2012, 09:37 | #4 |
Участник
|
|
|
22.06.2012, 10:26 | #5 |
Модератор
|
Вы главное версию уточните, а то в 4.0 оно так параллельно (с двух клиентов) отменится - хвостов не найдете. Разъедется InventTrans с InventSum и InventSum с GL
__________________
-ТСЯ или -ТЬСЯ ? |
|
22.06.2012, 10:53 | #6 |
Участник
|
|
|
22.06.2012, 11:14 | #7 |
Участник
|
помню на 3.0 делал оптимизацию отмены склада...закрывался склад 4 часа, а при откате сеттелмент откатывался за 20 минут, и документ ГК больше часа. Сделали "собственный" способ отмены проводок по ГК: брали оригинальный документ ГК по закрытию и стонировали его. В итоге время отката сократилось в 3 раза
|
|
22.08.2012, 16:45 | #8 |
Участник
|
DAX2009 SP1 RU8
Коллеги, помогите, чем можете Запустили отмену закрытия, количество помощников 30, отменяется уже несколько дней, прогресс 83% и почти не двигается дальше, куча блокировок, помощники мешают друг другу. Что посоветуете, как остановить эту отмену? Что будет если убить часть помощников? Какие еще варианты? Если отмена остановится с ошибкой, насколько я понимаю, отката отмены полного не будет? Придется джобами удалять? |
|
22.08.2012, 17:16 | #9 |
Участник
|
Можно прервать пакетную обработку принудительно - там кнопка для этого есть.
По идее не должно быть блокировок если вы отменяете только одно закрытие, так как каждый помощник отменяет только пересчет по проводкам одной номенклатуры. |
|
22.08.2012, 17:23 | #10 |
Участник
|
Цитата:
Помощники мешают друг другу на обновлении таблицы сопоставлений и постоянно возникают dead locks. |
|
22.08.2012, 17:24 | #11 |
Участник
|
Еще если не ошибаюсь была бага - в случае когда отменяем много пересчетов, то отмена конкретных InventSettlement шла не в обратном порядке, а как получится. из-за этого могло получиться что в один момент времени 2 разных помощника отменяют складские сопоставления по одной и той же номенклатуре но из разных пересчетов (отсюда и блокировки и конфликты обновления записей с откатом транзакций и даже с выпадением помощников в ошибку).
чтобы такого не было модифицировали немного класс отмены, чтобы он ставил дополнительные зависимости между задачами и отмена шла бы строго в обратной хронологической последовательности, |
|
22.08.2012, 17:27 | #12 |
Участник
|
Вот фикс.
|
|
|
За это сообщение автора поблагодарили: Bega (5). |
22.08.2012, 17:29 | #13 |
Участник
|
Цитата:
Дальше для целого пакета Функции - Смена статуса и в ответ выбрать "Отмена" Аксапта подождет немного и прервет выполнение пакета. |
|
22.08.2012, 17:40 | #14 |
Участник
|
Она прервет выполнение, но не откатит то, что уже наколбасила, то есть часть номенклатур будет отменена, а часть - нет. Так?
|
|
22.08.2012, 17:43 | #15 |
Участник
|
Цитата:
Сообщение от Logger
Еще если не ошибаюсь была бага - в случае когда отменяем много пересчетов, то отмена конкретных InventSettlement шла не в обратном порядке, а как получится. из-за этого могло получиться что в один момент времени 2 разных помощника отменяют складские сопоставления по одной и той же номенклатуре но из разных пересчетов (отсюда и блокировки и конфликты обновления записей с откатом транзакций и даже с выпадением помощников в ошибку).
чтобы такого не было модифицировали немного класс отмены, чтобы он ставил дополнительные зависимости между задачами и отмена шла бы строго в обратной хронологической последовательности, |
|
22.08.2012, 17:53 | #16 |
Moderator
|
1. Можно смело отменить весь BatchJob
2. Затем надо зайти в исходное закрытие (которое отменяешь) и снова щелкнуть кнопочку "Отмена". Дело в том, что один хелпер отменяет сопоставления по одной номенклатуре и отменяет это в транзакции (так что закрытие по одной номенклатуре либо отменится целиком, либо останется неотмененным). Когда ты снова запускаешь отмену закрытия, оно пробегает по списку inventSettlement неотмененных и генерирует список номенклатур для отмены. А о причине дидлоков я писал в статье - вторая половина. Лучший способ предотвратить это - построение дополнительных индексов по inventSettlement и prodTableJour |
|
|
За это сообщение автора поблагодарили: Bega (5), androzavr (1), Logger (3). |
22.08.2012, 17:54 | #17 |
Moderator
|
Кстати - осторожнее с отменой закрытия при средней себестоимости - похоже что там есть баг. Если отменить закрытие, запостить еще приходов или расходов и снова закрыть - результаты будут нехорошими. Например на проводках переноса случиться пересоставление - qtySettled>qty...
|
|
|
За это сообщение автора поблагодарили: Penguin (1), S.Kuskov (1). |
22.08.2012, 18:15 | #18 |
Участник
|
|
|
23.08.2012, 08:19 | #19 |
Участник
|
Кстати, я нашел запрос, точнее это оказался апдейт, который вызывал тормоза.
Класс InventCostClosingCancel_WorkInvent метод duplicateSettlements() X++: update_recordset inventSettlement setting Cancelled = NoYes::Yes, QtySettled = -1 * inventSettlement.QtySettled, CostAmountSettled = -1 * inventSettlement.CostAmountSettled, CostAmountAdjustment = -1 * inventSettlement.CostAmountAdjustment where inventSettlement.Voucher == newVoucher && inventSettlement.TransDate == newTransDate // <GEEU> && inventSettlement.InventTransCurrency_RU == inventTransCurrency // </GEEU> && inventSettlement.Cancelled == NoYes::No && inventSettlement.ItemId == itemId; Как указать в update_recordset индекс я не нашел, поэтому изменил индекс ItemDateIdx, добавив в него поле Voucher. После этого склад отменяется за минуты вместо часов. |
|
|
За это сообщение автора поблагодарили: mazzy (2), Bega (5), androzavr (1). |
23.08.2012, 10:34 | #20 |
Developer
|
Цитата:
Цитата:
P.S. Из тех полей что писал в первом сообщении можно и нужно добавлять как раз поле Voucher |
|