25.01.2008, 21:12 | #1 |
Участник
|
Глюки 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); Если выполнять данный запрос в джобе, то все проходит на ура. Если перед запросом отключить кеширование на таблице 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 |
Участник
|
А приведите пожалуйста перечень проводок и их Тип (TransType), которые на момент вызова запроса у вас добавлены в RecordViewCache.
Надо ж воспроизвести, чтоб починили |
|
27.01.2008, 18:33 | #3 |
Участник
|
|
|
28.01.2008, 13:50 | #4 |
Участник
|
Цитата:
Как воспроизвести глюк. 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 ); Если отключить кеш, InventTrans.disablecache(true); то вернет -5. Если поставить точное равенство 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; то тоже вернет -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 ); (в этом случае если посмотреть InventTrans.wasCached() то видно что запись закеширована - во всех предыдущих случаях - она незакеширована, потому что Лот2 вообще небыло в salesParmLine) Итог : Если по InventTrans создан RecordViewCache для какого то конкретного запроса (запрос по Лот1) то при других запросах ядро может глючить и возвращать пустую выборку (запрос по Лот2) Глюк исчезает если 1. отключить кеширование на буфере перед выполнением запроса или 2. прнудительно создать RecordViewCache перед выполнением запроса (В нашем примере Лот2) или 3. Изменить запрос так чтобы в нем не было перечисления через || |
|
|
За это сообщение автора поблагодарили: gl00mie (5). |
28.01.2008, 13:53 | #5 |
Участник
|
Более простой способ воспроизвести баг.
Пусть у нас есть 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 |
Участник
|
В KR3 тоже воспроизводится
|
|
28.01.2008, 14:34 | #7 |
Участник
|
Проверил - во всех случаях у меня возвращает -5.
Видимо уже починили. |
|
28.01.2008, 14:42 | #8 |
Участник
|
|
|
28.01.2008, 14:49 | #9 |
Участник
|
Вот мои результаты с запросами.
Цитата:
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 |
Участник
|
Чтобы обойти баг с кешированием по 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; } X++: \Classes\InventMovement\viewCacheInventTransId При таких изменениях кеш живет только на то время пока он нужен и не мешает при выполнении запросов по InventTrans - так что про багу можно забыть. (Баг проявлялся потому что кеш жил и после того как отработал метод UpdateNow() семейства классов InventUpdate - как правило в разносках проявлялось - теперь мы его своевременно убиваем и он не мешает) |
|
|
За это сообщение автора поблагодарили: denny (1), malex (1), gl00mie (5). |
Теги |
ax3.0 |
|
|