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

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 25.01.2008, 21:12   #1  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Глюки RecordViewCache
Обнаружен баг при использовании кеширования RecordViewCache
В некоторых случаях некорректно происходит выборка данных из закешированной таблицы (запрос на БД вообще не уходит - выборка пустая).
Ax 3.0 SP3 Oracle

Выявлен следующим образом :
В работе семейства классов SalesFormLetter / PurchFormletter сделаны кастомизации приводящие к запросам по InventTrans c использованием фильтра по статусам проводок.

Например такой запрос

X++:
        select sum(qty) from inventTrans
            index hint TransIdIdx
            where inventTrans.inventTransId         == _inventTransId   &&
                (inventTrans.statusIssue == StatusIssue::ReservPhysical ||
                inventTrans.statusIssue == StatusIssue::ReservOrdered   ||
                inventTrans.statusIssue == StatusIssue::OnOrder);
при использовании в методе UpdateNow() SalesFormleter-а может выдать пустую выборку (причем никакого запроса к БД не уходит ! ).

Если выполнять данный запрос в джобе, то все проходит на ура.

Если перед запросом отключить кеширование на таблице
X++:
inventTrans.disableCache(true);
то тоже все проходит на ура как в случае джоба.

К сожалению, построить пример, который бы гарантированно воспроизвел ошибку не получилось.
Есть примеры в рабочей базе для заказа и закупки (для закупки запрос несколько отличается
X++:
        select
            firstOnly
        InventTrans
        order by
            DateFinancial desc
        where
            InventTrans.ItemId        == _itemId &&
            InventTrans.StatusReceipt == StatusReceipt::Purchased &&
            InventTrans.StatusIssue   == StatusIssue::None &&
            (
                InventTrans.TransType     == InventTransType::Purch ||
                InventTrans.TransType     == InventTransType::BOMLine ||
                InventTrans.TransType     == InventTransType::BOMMain ||
                InventTrans.TransType     == InventTransType::InventCounting ||
                InventTrans.TransType     == InventTransType::InventLossProfit ||
                InventTrans.TransType     == InventTransType::InventTransaction
            );
)
для которых глюк воспроизводится гарантированно.

Самое интересное, что если в запросе убрать перечисление статусов, то есть написать так (условие на точное равентсво):

X++:
        select sum(qty) from inventTrans
            index hint TransIdIdx
            where inventTrans.inventTransId         == _inventTransId   &&
                (inventTrans.statusIssue == StatusIssue::ReservPhysical);
или заменить перечисление на диапазон значений, то тоже все отрабатывает.
X++:
        select sum(qty) from inventTrans
            index hint TransIdIdx
            where inventTrans.inventTransId         == _inventTransId   &&
                           inventTrans.StatusIssue >= StatusIssue::ReservPhysical
                     && inventTrans.StatusIssue <= StatusIssue::OnOrder;
Если в методе
\Classes\InventMovement\viewCacheInventTransId
закомментировать создание RecordViewCache
(а именнно в этом методе создается RecordViewCache при работе SalesFormLetter) то тоже глюк исчезает.


Глядя на такое хочется вообще отключить кеширование RecordViewCache в методе
\Classes\InventMovement\viewCacheInventTransId

(тогда оно не будет использоваться при смене статуса проводки).
Вопрос в том не слишком ли сильно просядет в таком случае производительность системы...
Старый 25.01.2008, 22:04   #2  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
А приведите пожалуйста перечень проводок и их Тип (TransType), которые на момент вызова запроса у вас добавлены в RecordViewCache.

Надо ж воспроизвести, чтоб починили
Старый 27.01.2008, 18:33   #3  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от kashperuk Посмотреть сообщение
А приведите пожалуйста перечень проводок и их Тип (TransType), которые на момент вызова запроса у вас добавлены в RecordViewCache.

Надо ж воспроизвести, чтоб починили
Попробую что-нить придумать.
С наскока не получилось самому сгенерить пример.
Старый 28.01.2008, 13:50   #4  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от kashperuk Посмотреть сообщение
А приведите пожалуйста перечень проводок и их Тип (TransType), которые на момент вызова запроса у вас добавлены в RecordViewCache.

Надо ж воспроизвести, чтоб починили

Как воспроизвести глюк.

1. Создаем заказ
Строки заказа
Лот1 Количество 4 (2 физрезерв; 2 скомплектовано)
Лот2 Количество 5 (5 физрезерв)

2. Обрабатываем накладную по заказу (SalesformLetterInvoice) по скомплектованному.
Перед обработкой в SalesParmLine должна быть одна запись которая соответсвует Лот1.
Взводим галку SalesParmLine.closed (для воспроизведения бага галку взводить необязательно - но с ней будет проще - меньше проводок перебирать. Так как с взведенной SalesParmLine.closed после обработки по строке Лот1 не будет проводок в физрезерве - меньеш строк в отладчике перебирать)

3. Смотрим отладчик.
В методе SalesformLetterInvoice.Updatenow()
После цикла
X++:
    while (salesParmLine)
    {
    ...
    }
Стоит наш код (кастомизация)
1. Цикл по всем строкам заказа по которым осталось необработанное количество.
Для каждой строки такой запрос

X++:
    select sum(qty) from inventTrans
        index hint TransIdIdx
        where inventTrans.inventTransId         == _inventTransId   &&
            (inventTrans.statusIssue == StatusIssue::ReservPhysical ||
             inventTrans.statusIssue == StatusIssue::ReservOrdered  ||
             inventTrans.statusIssue == StatusIssue::OnOrder
            );
В нашем примере для строки Лот2 он вернет 0.
Если отключить кеш,
InventTrans.disablecache(true);
то вернет -5.

Если поставить точное равенство

X++:
    select sum(qty) from inventTrans
        index hint TransIdIdx
        where inventTrans.inventTransId         == _inventTransId   &&
            (inventTrans.statusIssue == StatusIssue::ReservPhysical);
то вернет -5

Если поставить диапазон значений

X++:
    select sum(qty) from inventTrans
        index hint TransIdIdx
        where inventTrans.inventTransId         == _inventTransId   &&
                       inventTrans.StatusIssue >= StatusIssue::ReservPhysical
                    && inventTrans.StatusIssue <= StatusIssue::OnOrder;
что эквивалентно перечислению, так как значения в енуме идут подряд
то тоже вернет -5

И теперь внимание !
Если принудительно перед выполнением запроса создать RecordViewCache
X++:
    object             object;
    ;
...
    object = InventTrans::viewCacheInventTransId(_InventTransId, true);
    select sum(qty) from inventTrans
        index hint TransIdIdx
        where inventTrans.inventTransId         == _inventTransId   &&
            (inventTrans.statusIssue == StatusIssue::ReservPhysical ||
             inventTrans.statusIssue == StatusIssue::ReservOrdered  ||
             inventTrans.statusIssue == StatusIssue::OnOrder
            );
то тоже вернет -5 !!!
(в этом случае если посмотреть InventTrans.wasCached() то видно что запись закеширована - во всех предыдущих случаях - она незакеширована, потому что Лот2 вообще небыло в salesParmLine)


Итог :
Если по InventTrans создан RecordViewCache для какого то конкретного запроса (запрос по Лот1)

то при других запросах ядро может глючить и возвращать пустую выборку (запрос по Лот2)

Глюк исчезает если
1. отключить кеширование на буфере перед выполнением запроса
или
2. прнудительно создать RecordViewCache перед выполнением запроса (В нашем примере Лот2)
или
3. Изменить запрос так чтобы в нем не было перечисления через ||
За это сообщение автора поблагодарили: gl00mie (5).
Старый 28.01.2008, 13:53   #5  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Более простой способ воспроизвести баг.
Пусть у нас есть 2 лота из описания выше, выполняем для них такой код

X++:
// Запускать на сервере - в джоб поставить недостаточно, потому что джоб всегда на клиенте исполняется.
// лучше поставить в статический метод какого то класса
server static void test()
{
    InventTransID       InventTransID;
    Object              Object;
    Object              Object2;
    Qty grd_calcQty(InventTransId    _inventTransId, boolean _disablecache, str _s)
    {
        InventTrans inventTrans;
        ;
        inventTrans = null;
        if (_disablecache)
            inventTrans.disableCache(true);

        select sum(qty) from inventTrans
            index hint TransIdIdx
            where inventTrans.inventTransId         == _inventTransId   &&
                       //    inventTrans.StatusIssue >= StatusIssue::ReservPhysical
                       // && inventTrans.StatusIssue <= StatusIssue::OnOrder

                (inventTrans.statusIssue == StatusIssue::ReservPhysical ||
                inventTrans.statusIssue  == StatusIssue::ReservOrdered   ||
                inventTrans.statusIssue  == StatusIssue::OnOrder)

                ;
        info(strFMT("%3 DisableCache = %1 Количество %2",
            inventTrans.disableCache(),
            inventTrans.Qty,
            _s)); // здесь в плохом случае возвращает 0 для ЛОТ2. Если все в порядке то -5
    }
    ;
    ttsBegin;

    InventTransID = "ЛОТ1"; //  ЛОТ1 Количество 4 (2 физрезерв; 2 скомплектовано)
    Object = InventTrans::viewCacheInventTransId(InventTransId, true);
//    GRD_calcQty(InventTransId);


    InventTransID = "ЛОТ2"; //  ЛОТ2 Количество 5 (5 физрезерв)

    GRD_calcQty(InventTransId, false, "Кеширование не отключали.");
    GRD_calcQty(InventTransId, true , "Отключили кеширование.");

    Object2 = InventTrans::viewCacheInventTransId(InventTransId, true);
    GRD_calcQty(InventTransId, false, "Кеширование не отключали. Создан RecordViewCache по второму лоту");
    GRD_calcQty(InventTransId, true , "Отключили кеширование.  Создан RecordViewCache по второму лоту");

    ttsCommit;
}

Последний раз редактировалось Logger; 28.01.2008 в 14:02. Причина: подправил джоб
Старый 28.01.2008, 14:22   #6  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
В KR3 тоже воспроизводится
Старый 28.01.2008, 14:34   #7  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Проверил - во всех случаях у меня возвращает -5.
Видимо уже починили.
Старый 28.01.2008, 14:42   #8  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Цитата:
Сообщение от kashperuk Посмотреть сообщение
Проверил - во всех случаях у меня возвращает -5.
Видимо уже починили.
Ваня, а билд у тебя какой ?
Старый 28.01.2008, 14:49   #9  
kashperuk is offline
kashperuk
Участник
Аватар для kashperuk
MCBMSS
Соотечественники
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,361 / 2084 (78) +++++++++
Регистрация: 30.05.2004
Адрес: Atlanta, GA, USA
Вот мои результаты с запросами.
Цитата:
Info SQL statement SQL statement: (InventTrans) SELECT A.ITEMID,A.STATUSISSUE,A.INTERCOMPANYINVENTDIMTRANSFER3,A.DATEPHYSICAL,A.QTY,A.COSTAMOUNTPOSTED,A.CURRENCYCODE,A.TRANSTYPE,A.TRANSREFID,A.INVOICEID,A.VOUCHER,A.INVENTTRANSIDTRANSFER,A.DATEEXPECTED,A.DATEFINANCIAL,A.COSTAMOUNTPHYSICAL,A.INVENTTRANSID,A.STATUSRECEIPT,A.PACKINGSLIPRETURNED,A.INVOICERETURNED,A.PACKINGSLIPID,A.VOUCHERPHYSICAL,A.COSTAMOUNTADJUSTMENT,A.SHIPPINGDATEREQUESTED,A.SHIPPINGDATECONFIRMED,A.QTYSETTLED,A.COSTAMOUNTSETTLED,A.VALUEOPEN,A.DIRECTION,A.ACTIVITYNUMBER,A.DATESTATUS,A.COSTAMOUNTSTD,A.DATECLOSED,A.PICKINGROUTEID,A.INVENTTRANSIDFATHER,A.COSTAMOUNTOPERATIONS,A.ITEMROUTEID,A.ITEMBOMID,A.INVENTTRANSIDRETURN,A.PROJID,A.PROJCATEGORYID,A.INVENTDIMID,A.INVENTDIMFIXED,A.DATEINVENT,A.CUSTVENDAC,A.TRANSCHILDREFID,A.TRANSCHILDTYPE,A.TIMEEXPECTED,A.REVENUEAMOUNTPHYSICAL,A.ASSETID,A.PROJADJUSTREFID,A.TAXAMOUNTPHYSICAL,A.ASSETBOOKID,A.INVENTREFTRANSID,A.PROBABILITYID,A.RECVERSION,A.RECID FROM INVENTTRANS A WHERE ((DATAAREAID=?) AND (INVENTTRANSID=?)) [ID=445, Reused=No]

Info SQL statement SQL statement: (InventTrans) SELECT SUM(A.QTY) FROM INVENTTRANS A WHERE ((DATAAREAID=?) AND ((INVENTTRANSID=?) AND (((STATUSISSUE=?) OR (STATUSISSUE=?)) OR (STATUSISSUE=?)))) [ID=446, Reused=No]

Info SQL statement Кеширование не отключали. DisableCache = false Количество -5.00
Info SQL statement SQL statement: (InventTrans) SELECT SUM(A.QTY) FROM INVENTTRANS A WHERE ((DATAAREAID=?) AND ((INVENTTRANSID=?) AND (((STATUSISSUE=?) OR (STATUSISSUE=?)) OR (STATUSISSUE=?)))) [ID=447, Reused=No]

Info SQL statement Отключили кеширование. DisableCache = true Количество -5.00
Info SQL statement SQL statement: (InventTrans) SELECT A.ITEMID,A.STATUSISSUE,A.INTERCOMPANYINVENTDIMTRANSFER3,A.DATEPHYSICAL,A.QTY,A.COSTAMOUNTPOSTED,A.CURRENCYCODE,A.TRANSTYPE,A.TRANSREFID,A.INVOICEID,A.VOUCHER,A.INVENTTRANSIDTRANSFER,A.DATEEXPECTED,A.DATEFINANCIAL,A.COSTAMOUNTPHYSICAL,A.INVENTTRANSID,A.STATUSRECEIPT,A.PACKINGSLIPRETURNED,A.INVOICERETURNED,A.PACKINGSLIPID,A.VOUCHERPHYSICAL,A.COSTAMOUNTADJUSTMENT,A.SHIPPINGDATEREQUESTED,A.SHIPPINGDATECONFIRMED,A.QTYSETTLED,A.COSTAMOUNTSETTLED,A.VALUEOPEN,A.DIRECTION,A.ACTIVITYNUMBER,A.DATESTATUS,A.COSTAMOUNTSTD,A.DATECLOSED,A.PICKINGROUTEID,A.INVENTTRANSIDFATHER,A.COSTAMOUNTOPERATIONS,A.ITEMROUTEID,A.ITEMBOMID,A.INVENTTRANSIDRETURN,A.PROJID,A.PROJCATEGORYID,A.INVENTDIMID,A.INVENTDIMFIXED,A.DATEINVENT,A.CUSTVENDAC,A.TRANSCHILDREFID,A.TRANSCHILDTYPE,A.TIMEEXPECTED,A.REVENUEAMOUNTPHYSICAL,A.ASSETID,A.PROJADJUSTREFID,A.TAXAMOUNTPHYSICAL,A.ASSETBOOKID,A.INVENTREFTRANSID,A.PROBABILITYID,A.RECVERSION,A.RECID FROM INVENTTRANS A WHERE ((DATAAREAID=?) AND (INVENTTRANSID=?)) [ID=445, Reused=Yes]

Info SQL statement Кеширование не отключали. Создан RecordViewCache по второму лоту DisableCache = false Количество -5.00
Info SQL statement SQL statement: (InventTrans) SELECT SUM(A.QTY) FROM INVENTTRANS A WHERE ((DATAAREAID=?) AND ((INVENTTRANSID=?) AND (((STATUSISSUE=?) OR (STATUSISSUE=?)) OR (STATUSISSUE=?)))) [ID=447, Reused=Yes]

Info SQL statement Отключили кеширование. Создан RecordViewCache по второму лоту DisableCache = true Количество -5.00
Билд у меня:
5.0.436.0

На 4ке проверять не буду - если там это не пофиксино, то единственный способ добиться, чтобы пофиксили - через службу поддержки их долбить.
Старый 22.04.2008, 14:12   #10  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,952 / 3230 (115) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Чтобы обойти баг с кешированием по InventTrans - как правило перед выполнением запроса по InventTrans в кастомизациях отключают кеш
InventTrans.disableCache(true);

Но в системе есть куча стандартных мест где используется такой кеш.

Надежнее сделать следующую кастомизацию.
Как правило кеш используется при вызове метода
X++:
\Data Dictionary\Tables\InventTrans\Methods\viewCacheInventTransId
из
X++:
\Classes\InventMovement\viewCacheInventTransId
поэтому лучше сделать так
Заводим метод
X++:
\Classes\InventMovement\ClearViewCacheInventTransId
X++:
void ClearViewCacheInventTransId()
{
    viewCacheInventTrans    = NULL;
}
и вызываем в семействе классов InventUpdate - везде где было кеширование, то есть вызов
X++:
\Classes\InventMovement\viewCacheInventTransId
Например в конце метода updateNow() в классах InventUpd_...

При таких изменениях кеш живет только на то время пока он нужен и не мешает при выполнении запросов по InventTrans - так что про багу можно забыть. (Баг проявлялся потому что кеш жил и после того как отработал метод UpdateNow() семейства классов InventUpdate - как правило в разносках проявлялось - теперь мы его своевременно убиваем и он не мешает)
За это сообщение автора поблагодарили: denny (1), malex (1), gl00mie (5).
Теги
ax3.0

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
Развивающая игра с xRecord.wasCached(), RecordViewCache EVGL DAX: База знаний и проекты 4 12.12.2008 10:20
! "по выбранному курсору нельзя инициализировать recordViewCache" netbas DAX: Программирование 9 20.12.2007 15:24
Глюки в Query с разными типами Join (в т.ч. NonExistsJoin) к одной таблице gl00mie DAX: Программирование 10 14.02.2007 13:22
Глюки в Заказах Pegiy DAX: Функционал 4 17.03.2004 15:09
Глюки с разноской складских проводок на счета ГК Yury DAX: Программирование 0 11.04.2003 19:10

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

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

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