AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 13.12.2019, 13:36   #1  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Thumbs up AX 2012 R3. Включить OCC на InventSum и выжить
И снова хочу поделиться success-story.
Готовились к черной пятнице и проводили нагрузочные тестирования. Заметили блокировки при большом числе web-вызовов по операциям резервирования по заказам на продажу на InventSum. Нагрузка была такая, что даже номерную серию SalesId пришлось на последовательности в базе MSSQL перевести. Пришла в голову идея таки включить OCC (оптимистические блокировки) на InventSum.

Если посмотреть код на InventUpdateOnhand.ttsNotifyPreCommit, то можно заметить, что COMMIT выполняется логично, но крайне избыточно.
  1. Вставляются новые строки InventSumDelta -> InventSum (причем только ключевые поля без данных). Если строка DELTA-таблицы одна, то строка InventSum даже вставляется (см.п 3)
  2. Блокируются строки InventSum по наличию в InventSumDelta
  3. Далее идет обновление строк InventSum. Тут 2 варианта
    - если строка InventSumDelta была одна, то читаем пессимистически ее и выполняем ей суммирование количеств из InventSumDelta и WRITE (Insert для той самой строки из п.1). Тут без пессимистической блокировки было не обойтись
    - если строк InventSumDelta по нашей транзакции несколько, то ту строится динамический SQL и обновляются строки InventSum на основании группового запроса в InventSumDelta. Тут красиво читаются данные из InventSum и обновление происходит как поле базы += значение из InventSumDelta. InventSum.RecVersion не обновляется. В базе на измененные строки наложена блокировки. Никто не может теперь их читать. Все правильно
  4. Далее дополнительно проводится проверка на отрицательные остатки. И если что-то не так, то идет откат транзакции

Схема красивая рабочая с минимальными блокировками в конце комита, но ... Хочется включить оптимистические блокировки. Если в лоб на таблице InventSum включить OCC, то получаем проблему, когда групповой запрос не обновляет RecVersion, а единичный обновляет измененные строки из-за того, что вычитал старые данные, обновил в памяти кол-во и не упал на обновлении из-за совпадающего RecVersion. Ну и дополнительные выборки с пессимистиком, только чтобы залочить строки перед групповым обновлением.

Чего удалось достичь:
  1. При вставке новых записей InventSumDelta->InventSum вставляются всегда, даже если строка в InventSumDelta одна. Причем вставляются уже с данными для одной строки Delta-таблицы.
    X++:
            if (inventSumDeltaCnt == 1)
            {
                select ItemId, InventDimId,
                    #InventSumDeltaMax
                from inventSumDelta
                    group by ItemId, InventDimId
                    where inventSumDelta.ttsId          == this.ttsId() &&
                          inventSumDelta.IsAggregated   == NoYes::No
                        notexists join inventSum
                        where inventSum.ItemId         == inventSumDelta.ItemId &&
                              inventSum.InventDimId    == inventSumDelta.InventDimId;
            }
  2. Строки вообще не блокируются перед обновлением. По крайней мере при резервировании товара

    X++:
    protected void lockInventSum()
    {
        InventSum           inventSum;
        InventSumDeltaDim   inventSumDeltaDim;
    
        /*
        if (!this.parmDoOnhandCheck()) //!doSummarizedOnhandCheck && !doDirectOnhandCheck && !checkOnHandForWHSItems
        */
        if (!doSummarizedOnhandCheck && !doDirectOnhandCheck)
        {
            return;
        }
  3. При обновлении работает исключительно групповая процедура (прямой SQL) на основании групповой подвыборки InventSumDelta с оптимизациями
    • - для одной строки InventSumDelta, которая была вставлена ранее вызов вообще не выполняется
    • - если строка одна и обновляется, то подвыборка-InventSumDelta не выполняется. Все есть табличной переменной InventSumDelta и в динамический SQL подставляются константы
    • - обновлятся RecVersion в строках InventSum
      RecVersion = ' = FLOOR(RAND()*2147483647)+1'

В итоге блокировки InventSum ушли и подсистема резервирования (по сути любые операции обновления InventSum) стала выдерживать гораздо большие нагрузки от веб-сервисов. Если случаются коллизии при резервировании, когда 2 клиента пытаются одновременно зарезервировать последнюю единицу товара, то транзакция откатится на этапе проверки отрицательных остатков (как было и раньше).
Огромная торговая компания розница + интернет магазин в черную пятницу отлично себя чувствовали при онлайн резервировании.
За это сообщение автора поблагодарили: AlGol (2), Logger (10), gl00mie (15), SRF (7), imir (2).
Старый 16.12.2019, 05:01   #2  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,039 / 1633 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от kgksoft Посмотреть сообщение
Заметили блокировки при большом числе web-вызовов по операциям резервирования по заказам на продажу на InventSum. Нагрузка была такая, что даже номерную серию SalesId пришлось на последовательности в базе MSSQL перевести. Пришла в голову идея таки включить OCC (оптимистические блокировки) на InventSum.
Поздравляю с решением проблемы, но описание не очень понятно.
1. О каком кол-ве операций(в час к примеру) идет речь
2. Почему не использовали предварительное выделение номеров
3. Как я понял вы сделали обновление на SQL вне зависимости от кол-ва строк. OCC это же сво-во в АХ, как оно влияет вообще на ?
4. Какой объем InventSum если выполнить следующий скрипт
X++:
select count(*) as number, Closed,ClosedQty from InventSum (nolock)
Group by Closed, ClosedQty
Старый 16.12.2019, 11:00   #3  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Цитата:
Сообщение от trud Посмотреть сообщение
Поздравляю с решением проблемы, но описание не очень понятно.
1. О каком кол-ве операций(в час к примеру) идет речь
2. Почему не использовали предварительное выделение номеров
3. Как я понял вы сделали обновление на SQL вне зависимости от кол-ва строк. OCC это же сво-во в АХ, как оно влияет вообще на ?
4. Какой объем InventSum если выполнить следующий скрипт
О, хоть кому-то интересен мой "прорыв года".
  1. около 5 вызовов в секунду, но это тяжелых корзин (1-5 позиций), логика резервирования довольно тяжелая 0,5-10 секунд на вызов. Физически описываемая выше операция естественно в самом конце на комите.
  2. Предварительное выделение было включено, но оно работает в пределах одного АОСа, а у меня их много и расходуются номера не оптимально при больших предварительных значениях. К тому же уже написан функционал по быстрой настройке любой номерной серии на использование 64-бит или номерных серий MSSQL. Это намного быстрее, чем стандарт с его блокировкой строки.
  3. Да, ОСС это свойство таблицы. И кода, который выбирает InventSum forUpdate не так много. Главное даже не это свойство, а то что не нужно вообще делать выборку для блокировки. Во время тестирования наблюдались блокировки на вызовах select updlock, которые пытались залочить строки для обновления. После переделки блокировки ушли.
    Происходило две операции (Блокировка и Обновление), а теперь блокировка происходит на уровне SQL одной операцией update qty += delta.
    По идее, та же проблема должна быть и с кодом по обновлению WHSInventReserve, но проблемы с этой таблицей не наблюдал и отложил переделку до следующего раза.

  4. number---Closed---ClosedQty
    884676---0---0
    17619789---0---1
    2699511---1---1
Старый 17.12.2019, 07:14   #4  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,039 / 1633 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от kgksoft Посмотреть сообщение
Во время тестирования наблюдались блокировки на вызовах select updlock, которые пытались залочить строки для обновления. После переделки блокировки ушли.
Происходило две операции (Блокировка и Обновление), а теперь блокировка происходит на уровне SQL одной операцией update qty += delta.
А о каких операциях идет речь? Разве в стандарте кто-то лочит InventSum, кроме этого финального триггера? или это какой-то кастомный функционал?
Т.е. в ttsNotifyPreCommit происходит блокировка (с pessimistic lock) InventSum в разрезе ItemId-InventDimId, далее по этому же ключу происходит обновление(при этом используются 2 ветки - прямой SQL и одна запись). Т.е. от того что вы просто поменяете все на прямой SQL думаю ничего принципиально не изменится, все равно будут накладываться блокировки в разрезе ItemId-InventDimId(что правильно).
Старый 17.12.2019, 10:56   #5  
kgksoft is offline
kgksoft
Участник
 
37 / 107 (4) +++++
Регистрация: 24.12.2003
Цитата:
Сообщение от trud Посмотреть сообщение
А о каких операциях идет речь? Разве в стандарте кто-то лочит InventSum, кроме этого финального триггера? или это какой-то кастомный функционал?
Т.е. в ttsNotifyPreCommit происходит блокировка (с pessimistic lock) InventSum в разрезе ItemId-InventDimId, далее по этому же ключу происходит обновление(при этом используются 2 ветки - прямой SQL и одна запись). Т.е. от того что вы просто поменяете все на прямой SQL думаю ничего принципиально не изменится, все равно будут накладываться блокировки в разрезе ItemId-InventDimId(что правильно).
Все верно. Ничего нестандартного в процессе резервирования не использовали. Но вставка, блокировка + обновление медленнее, чем одна операция по обновлению. Это высоконагруженные веб-сервисы и тут каждая лишняя операция в базу данных дорога. А у меня в базе доходит до 90000 операций в секунду.

В моем случае было много параллельных операций по резервированию и разрезервированию товара. Товар и аналитики могли пересекаться. Именно при большой нагрузке с интернета и розницы происходили небольшие блокировки на InventSum. В стандарте все верно написано, но с моими исправлениями эта подсистема позволяет держать большие нагрузки по параллельной работе с одинаковыми аналитиками. Обновление InventSum перестало быть самым узким местом при резервирование большого потока заказов.

Тестировали протоком реальных заказов JMeter, потом шла операция разрезервирования этих заказов. Скорость была около 300 вызовов в минуту. Повторюсь внутри не только резервирование, а и построение цепочек заказов (закупки + перемещения + заказы на продажу).
Старый 17.12.2019, 12:24   #6  
trud is offline
trud
Участник
Лучший по профессии 2017
 
1,039 / 1633 (57) ++++++++
Регистрация: 07.06.2003
Записей в блоге: 1
Цитата:
Сообщение от kgksoft Посмотреть сообщение
Все верно. Ничего нестандартного в процессе резервирования не использовали. Но вставка, блокировка + обновление медленнее, чем одна операция по обновлению. Это высоконагруженные веб-сервисы и тут каждая лишняя операция в базу данных дорога. А у меня в базе доходит до 90000 операций в секунду.
Ну вот тут не сходится математика. т.е. у вас 5 заказов в секунду, 90к операций, вы оптимизировали 5 обновлений-вставок 1 записи, и говорите что это как-то заметно должно повлиять?
Цитата:
Сообщение от kgksoft Посмотреть сообщение
В моем случае было много параллельных операций по резервированию и разрезервированию товара.
Но ведь параллельные операции InventSum не блокируют, она блокируется в самом конце, т.е. важно сколько параллельных завершений транзакций было и сколько выполнялась проверка остатков в этом завершение транзакции. Вы это замеряли?
Если у вас до 5 позиций, то это проверка должна выполняться мнгновенно и никаких блокировкок быть не должно, ну т.е. вы сами посчитайте, 5 заказов в секунду, наихудший случай, когда в каждом одна и таже аналитика и номенклатура, завершение к примеру идет 100мс (что довольно долго) и то не получается блокировок
НО
В начальных версиях 2012 R3 существовал баг, когда из-за неправильных индексов по InventSumDelta, InventSumDeltaDim эта проверка остатков выбирала неправильный план и выполнялась довольно долго. тогда да, на любой серьезной нагрузке система вставала
Проблема собственно описана https://axology.wordpress.com/2013/1...umdelta-table/ решением было удаление индексов(всех кроме 1) на указанных таблицах
За это сообщение автора поблагодарили: Logger (5), axotnik88 (1).
Теги
inventsum, inventsumdelta, occ

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
emeadaxsupport: BOM Journal postings in AX 2012 R3 vs. earlier versions of AX 2012 Blog bot DAX Blogs 0 03.10.2015 02:35
Dynamics AX Sustained Engineering: Microsoft Dynamics AX 2012 R3 RTM Warehouse Management: How to prevent the creation of two inventDim records considered identical in Dynamics AX 2012 R3 RTM Blog bot DAX Blogs 0 22.12.2014 19:12
emeadaxsupport: AX Performance Troubleshooting Checklist Part 2 Blog bot DAX Blogs 0 09.09.2014 16:11
DAX: Calling all developers: how to ease the learning curve of Microsoft Dynamics AX 2012 R3 Blog bot DAX Blogs 0 27.08.2014 22:11
DAX: Microsoft Dynamics AX 2012 R3 is now available! Blog bot DAX Blogs 1 02.05.2014 23:00

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 15:28.