28.09.2020, 09:33 | #1 |
Участник
|
DAX09: зависание AX при выборе в AOT меню ПКМ -> Надстройки
Всем привет. Подскажите с чего начать при поиске вот такой проблемы:
Открываю в AOT таблицу и поле (таб. Ledgerjournaltrans поле Due но может быть и другие поля), жму ПКМ -> Надстройки - система зависает на 1 минуту. После меню отображается. Возможно выполняется какой то запрос? Просто читал статью: https://axapta.mazzy.ru/lib/querytuning/ Пытался сделать как в статье, но в журнале трассировок операторов SQL не выводится никакой запрос. Возможно это и не запрос даже? Тогда что может быть.. P.S. Данная проблема проявляется не на всех таблицах и их полях. Только на некоторых. Последний раз редактировалось oleggy; 28.09.2020 в 09:50. |
|
28.09.2020, 10:43 | #2 |
Участник
|
Скорее всего тормозит запро по таблице xref*
Попробуйте перестроить индексы и статистику по всем табличкам xref* А лучше всего добейтесь чтобы у вас долгие запросы sql логировались и тогда по ним будет ясно. |
|
28.09.2020, 11:21 | #3 |
Участник
|
она не зависает, а вычитывает практически весь АОТ из псевдо-таблицы UtilElements.
Эта таблица отражает содержимое AOD файлов и содержит всю бизнес-логику. При последующих выборках, UtilElements будет брать объекты из кэша, поэтому зависаний не заметно. логика такая - аксапта читает из AOD файлов только те объекты, которые нужны для работы данного клиента. Как правило, это одна-две формы, десяток классов и несколько других объектов. Первое чтение будет немного дольше, но на десятке-другом объектов это не заметно. при нажатии ПКМ -> Надстройки срабатывает класс SysContextMenu с инструментами разработчика. Этот класс вычитывает почти весь AOT и соответственно тормозит в первый раз. Примерно такая же бодяга происходит в окне с проектами разработчика при ПКМ -> Создать проект тут либо полностью переделать SysContextMenu либо признать, что это торможение - плата за более плавную работу в дальнейшем, и смириться чтобы смириться, можно придумать другую рационализацию. Например, в JetBrains продуктах с таким количеством объектов вообще невозможно работать, постоянно. =========== и да, UtilElements - псевдо-таблица, которая не хранится в SQL во всех аксаптах, кроме ax2012. поэтому запросы к этой таблице невозможно отследить средствами SQL Последний раз редактировалось mazzy; 28.09.2020 в 11:23. |
|
07.10.2020, 05:10 | #4 |
Участник
|
Цитата:
Т.е. не только висит 1 минуту, но и при повторной попытке снова висит? По идее должно со второй попытки не должно тормозить. По таблице Ledgerjournaltrans. Ее размеры: Что это может быть? У меня догатка что в коде не происходит сохранения в кэш. Т.к. после ожидания 1 минуты и выбора какого то пункта в подменю Перекрестные ссылки, выводится как обычно таблица перекрестных ссылок но в добавок выводится infolog - Недопустимый тип контейнера данных. Если словить его: [c] \Classes\Info\add 14 [c] \Classes\Global\error 3 [c] \Classes\AfDataContainerDescriptors\createDataContainerDescriptor 69 [c] \Classes\AfDataContainerDescriptors\getDataContainerDescriptor 46 [c] \Classes\AfDataContainer\new 23 [c] \Classes\AfStronglyTypedDataContainer\new 20 [c] \Classes\CustCollectEInvoice_CustTable_NO\new 3 В методе createDataContainerDescriptor - 69: X++: private static AfDataContainerDescriptor createDataContainerDescriptor(str _dataContainerType, Map _constructionContext) { str methodName; SysDictClass metadataDictClass; container dataContainerMetadata, dataContainerDescriptorPackage; AfDataContainerDescriptor dataContainerDescriptor; // Check if the requested data container descriptor is persisted in the runtime cache dataContainerDescriptorPackage = AifRuntimeCacheManager::retrieveEntry(AifRuntimeCacheEntryType::AfDataContainerDescriptors, _dataContainerType); if (conlen(dataContainerDescriptorPackage) > 0) { // Construct the data container descriptor. dataContainerDescriptor = AfDataContainerDescriptor::constructFromPackage(dataContainerDescriptorPackage, _constructionContext); } else { // Get a dictionary for AfDataContainerDescriptors class. metadataDictClass = new SysDictClass(classnum(AfDataContainerDescriptors)); // Build the name of the method. methodName = strfmt('get\%1Metadata', _dataContainerType); // Check if the method exists. if (metadataDictClass.hasStaticMethod(methodName)) { // The method exists, call it to get the metadata. new ExecutePermission().assert(); // BP deviation documented dataContainerMetadata = metadataDictClass.callStatic(methodName, _dataContainerType); CodeAccessPermission::revertAssert(); } else { // The method does not exist, try to get Axd data container metadata. dataContainerMetadata = AfDataContainerDescriptors::createAxdDataContainerMetadata(_dataContainerType); } if (conlen(dataContainerMetadata) > 0) { // Check the type of the returned metadata if (_dataContainerType != conpeek(dataContainerMetadata, #DataContainerTypeIndex)) { // The type of the returned metadata does not match the requested data container type, // throw an error. throw error("@SYS125458"); } // Construct a data container descriptor from the metadata. dataContainerDescriptor = AfDataContainerDescriptor::constructFromMetadata(dataContainerMetadata, _constructionContext); } else { // The metadata was not found. Throw an error. throw error("@SYS125462"); // <- ошибка тут } // Persist the data container descriptor in the runtime cache AifRuntimeCacheManager::cacheEntry(AifRuntimeCacheEntryType::AfDataContainerDescriptors, _dataContainerType, dataContainerDescriptor.pack()); } return dataContainerDescriptor; } Указывает на то что контейнер dataContainerMetadata - пустой. Я конечно буду смотреть по коду, где данный контейнер должен заполнятся. Просто вдруг кто сталкивался? Последний раз редактировалось oleggy; 07.10.2020 в 06:00. |
|
11.11.2020, 06:40 | #5 |
Участник
|
Вроде разобрался из за чего в AX09 постоянно зависает. Дело в том что не происходит сохранение в кэш.
А не происходит из-за того что при обсчете всего AOT наталкивается на такой класс (см. ниже) который пытается инициализировать, генерируется исключение. Выделил данный класс в простой джоб. У всех так? X++: static void Job561(Args _args) { CustDocument_CustTable custDocument_CustTable; ; custDocument_CustTable = new CustDocument_CustTable(); } Если провалится в new в данном классе видно что тут: [c] \Classes\AfDataContainerDescriptors\createAxdDataContainerMetadata 19 [c] \Classes\AfDataContainerDescriptors\createDataContainerDescriptor 50 [c] \Classes\AfDataContainerDescriptors\getDataContainerDescriptor 46 [c] \Classes\AfDataContainer\new 23 [c] \Classes\AfStronglyTypedDataContainer\new 20 [c] \Classes\CustDocument_CustTable\new 3 [c] \Classes\AfDataContainerDescriptors\createAxdDataContainerMetadata 19 [c] \Classes\AfDataContainerDescriptors\createDataContainerDescriptor 50 [c] \Classes\AfDataContainerDescriptors\getDataContainerDescriptor 46 [c] \Classes\AfDataContainer\new 23 [c] \Classes\AfStronglyTypedDataContainer\new 20 [c] \Classes\CustDocument_CustTable\new 3 пытается найти класс которого нет в AOT - AxdCustDocument. Из-за этого и валится. Я посмотрел, этого класса и не было раньше. Странно. Создал класс заглушку: X++: class AxdCustDocument extends AxdBase { } AifActionInfoList getActionList() { AifActionInfoList ret; ret = super(); return ret; } void getConstraintList(Common _curRec, AifConstraintList _constraintList) { super(_curRec, _constraintList); } LabelString getLabel() { LabelString ret; ret = super(); return ret; } Но теперь валится Ошибка времени выполнения: QueryBuildDataSource Объект не инициализирован. (C)\Classes\QueryBuildDataSource\table (C)\Classes\AfDataContainerDescriptor\createDataItemsFromAxdDataSource - line 38 (C)\Classes\AfDataContainerDescriptor\constructFromMetadata - line 121 (C)\Classes\AfDataContainerDescriptors\createDataContainerDescriptor - line 64 (C)\Classes\AfDataContainerDescriptors\getDataContainerDescriptor - line 46 (C)\Classes\AfDataContainer\new - line 23 (C)\Classes\AfStronglyTypedDataContainer\new - line 20 (C)\Classes\CustDocument_CustTable\new - line 3 Ошибка тут: X++: private void createDataItemsFromAxdDataSource(AxdBase _axd, Map _constructionContext) { Query axdQuery; QueryBuildDataSource dataSource, childDataSource; int childDataSourceIndex, childDataSourceCount; AxInternalBase axbc; SysDictTable dictTable; Common table; Set tableDataItems; Set dataSourceDataItems; Set excludedDataItems; str documentHashDataItemName; int nextSerializationOrder = 1; // Get Axd query axdQuery = _axd.getQuery(); // Get data source dataSource = axdQuery.dataSourceName(this.get_DataSourceName()); // Create data item descriptors from the data source dictTable = new SysDictTable(dataSource.table()); // <-- тут ошибка table = dictTable.makeRecord(); axbc = AxInternalBase::construct(table); if (axbc != null) ... } Причем this.get_DataSourceName() возвращает CustTable, все должно быть корректно. но dataSource равен null. Подскажите почему он равен null ? Последний раз редактировалось oleggy; 11.11.2020 в 08:05. |
|
11.11.2020, 10:28 | #6 |
Участник
|
Как правило, для обслуживающих (сервисных) функций Axapta класс инициализируется только для того, чтобы проверить наличие или отсутствие неких методов или свойств самого класса. Поэтому значение переменных внутри класса в этих случаях не требуется. Вот какие-нибудь "заглушки" там и сделайте на этот случай
Т.е. прямо в методе new() сделайте код обхода, чтобы класс все-таки был инициализирован. Пусть и с явно "кривыми" данными. А "кривизну" проверяйте уже в рабочих методах. Ну, или просто перенесите инициализацию тех объектов, которые создаются в методе new() в первый исполняемый метод класса, чтобы в new() ничего не было Если же в метод new() у класса передаются какие-то параметры, то можно опираться на тот факт, что в обслуживающих функциях эти параметры никогда не передаются при инициализации класса. Соответственно, делать обход по факту отсутствия переданных параметров PS: кто там настаивал, что перекрывать метод new() вообще и передавать в него параметры в частности - это хорошо? Цитата:
Причем this.get_DataSourceName() возвращает CustTable, все должно быть корректно.
но dataSource равен null. Подскажите почему он равен null ? В общем, в данном конкретном случае Вы "слишком глубоко копаете". Не надо дальше самого класса CustDocument_CustTable пытаться что-то подправить. Вам надо или обойти инициализацию в самом методе new(), если есть параметры, или перенести весь код из new() в другой метод
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
11.11.2020, 14:03 | #7 |
Участник
|
Цитата:
Сообщение от Владимир Максимов
Т.е. прямо в методе new() сделайте код обхода, чтобы класс все-таки был инициализирован. Пусть и с явно "кривыми" данными. А "кривизну" проверяйте уже в рабочих методах. Ну, или просто перенесите инициализацию тех объектов, которые создаются в методе new() в первый исполняемый метод класса, чтобы в new() ничего не было
Если же в метод new() у класса передаются какие-то параметры, то можно опираться на тот факт, что в обслуживающих функциях эти параметры никогда не передаются при инициализации класса. Соответственно, делать обход по факту отсутствия переданных параметров X++: public void new() { super(#CustDocument_CustTable); } Где #define.CustDocument_CustTable('CustDocument.CustTable') Вообщем я правильно понял что лучше будет в таком случае просто убрать super ? Просто как быть если возникает предупреждение что в методе не вызывается родительский метод? |
|
11.11.2020, 17:56 | #8 |
Участник
|
Нет. Просто закомментировать нельзя. Вы же сломаете штатную работу с этим классом. Хотя, если они нигде и никак не используется, то, конечно, можно комментировать
Вам надо посмотреть, что там в родительском классе в методе new() происходит. И перенос сделать у родителя. Ну и по всей иерархии, если данный класс - это не один наследник у этого родителя Цель модификации заключается в том, чтобы, в идеале, в методе new() вообще ничего не было. В крайнем случае, инициализировались бы какие-то константы. Главное, убрать из new() инициализацию других объектов, которые, собственно, и привели к ошибке
__________________
- Может, я как-то неправильно живу?! - Отчего же? Правильно. Только зря... |
|
|
|