|
28.06.2013, 07:59 | #1 |
Участник
|
Анализатор незакрытых транзакций
У нас нередко возникала проблема с незакрытыми транзакциями (непарные ttsbegin/ttscommit). Мы пытались их устранять, искали и исправляли код с такими ошибками, но это было медленно и не очень успешно.
Для возможно более полного решения этой проблемы я написал программу для автоматического поиска подобных ошибок, с помощью которой мы выявили (а затем устранили) немалое их количество. Программа предназначена для Ax 3.0, (но, наверное, будет работать и с другими версиями Axapta, но с худшими результатами) и обрабатывает .xpo-файлы (но job-ы не обрабатываются). Принцип работы простой: считается, что все открытые в методе транзакции должны быть в нём закрыты. Вообще программа использует набор эвристик, из-за чего могут быть как ложноположительные, так и ложноотрицательные результаты. В частности, циклы обрабатываются очень приблизительно. Кроме того, макросы программа не раскрывает, а просто "угадывает", что там могло быть (синтаксически). Как пользоваться: 1. Эскпортируете объекты в какой-нибудь файл.xpo. 2. В cmd.exe или его аналоге (analyzer --- консольное приложение): > analyzer.exe этот_файл.xpo > result.txt 3. Упорно ждёте завершения обработки, затем анализируете result.txt. Если хотите попробовать на новых версиях Dynamics AX, то указывайте кодировку обрабатываемых файлов, например: > analyzer.exe aot.xpo utf-8 > aot_result.txt При сбоях в работе анализатора выдаются такие сообщения: . Parse error. Token <x> in block <y>, state <z> Данное сообщение означает, что произошёл сбой синтаксического анализатора, при этом указанный метод НЕ анализируется. Пример: public void insert() { if if(!this.ABCCalcJourID) ttscommit; \Tables\ABCCalcJour\insert, line 3: -- Parse error. Token <if> in block <CondExpr>, state <Start> . !ParseWarning: Unrecognized command (something) Данные сообщения означают, что выявлена неизвестная анализатору команда или слово, открывающее блок. Анализ при этом продолжается. Выявляемые предупреждения: . 'break' is used outside of a loop or switch! . 'continue' is used outside of a loop! -- использование break/continue вне циклов/switch-ей. . 'ttsabort' inside 'catch' is useless! -- ttsabort внутри catch бесполезен (в нашей Ax 3.0/MSSQL это так). . multiple ttsbegin/ttscommit pairs in linear block! -- несколько транзакций в линейном блоке, обычно это ошибка. Выявляемые ошибки: . 'try' is useless inside transaction!" -- try внутри транзакции бесполезен (в нашей Ax 3.0/MSSQL это так). . 'return' with incorrect ttslevel! -- выход из метода с некорректным уровнем tts. . ttslevel<0! . unpaired transaction! ------------------------------------------------- В нашем случае результаты для всего AOT такие: Время анализа: около 5 минут (313 сек). Всего выявлено ошибок и предупреждений: 335 Из них: Корректных ошибок и предупреждений: 286, ~85% Формально ошибочных, но корректных (методы 'ttsbegin' и т.п.): 30, ~9% Некорректных предупреждений: 7, ~2% Ложноположительных результатов: 10, ~3% Hack-ов (while (appl.ttsLevel()>0) ttscommit; ) : 2, менее 1% Интересно, какие результаты получатся у Вас. |
|
|
За это сообщение автора поблагодарили: Logger (10), Raven Melancholic (1), alex55 (3), imir (2). |
28.06.2013, 08:31 | #2 |
Участник
|
Это конечно круто.
Но если вам приходится прибегать к таким ухищрениям, то какое же наследство вы разгребаете |
|
28.06.2013, 09:53 | #3 |
Участник
|
Это да: "Корректных ошибок и предупреждений: 286". Из них около 40 ошибок с транзакциями в методах, которые реально используются. На этом приложении многие разрабатывали...
А Вы на своих приложениях/проектах попробовали? |
|
28.06.2013, 10:56 | #4 |
Участник
|
Такую задачу решают стандартные проверки best practics. Правда по всему приложению не за 5 минут (313 сек), а за то время, которое требуется на компиляцию.
|
|
28.06.2013, 11:11 | #5 |
Участник
|
А у Вас какая версия Axapta?
Я вот беру код типа: ------ X++: ttsbegin; if (!inventTable.AFPGroupId) { if (cry) warning(strfmt("@KCL2642", inventTable.ItemId)); return; } while select AFPGroupLines where AFPGroupLines.GroupId == inventTable.AFPGroupId { AFPUnit::createUnit(inventTable.ItemId, AFPGroupLines.InventLocationId, _combination); } ttscommit; Запускаю проверку Best Practices, и никаких предупреждений не вижу. |
|
29.06.2013, 16:53 | #6 |
Участник
|
Цитата:
X++: ttsCommit; ... ttsBegin; Просто когда-то на одной из прошлых работ я расширял BP по поводу парности ttsBegin/ttsCommit таким образом, чтобы проверялось не только количество, но и порядок и наличие между началом и подтверждение транзакции возвратов, прерываний, продолжений (в этом случае выдавал не ошибку или предупреждение, а просто сообщение, поэтому ловилось только при определенной настройке BP). За давностью лет решил, что это и есть стандарт. Ладно, вспомню что тогда делал и добавлю в наше приложение. А Вам рекомендую вписать Вашу проверку в метод checkSource класса SysBPCheckMemberFunction. В этом случае не нужно будет что-то выгружать в проекты, а контролировать все это дело в рамках текущей разработки. |
|
28.06.2013, 11:02 | #7 |
Участник
|
Цитата:
Мы настолько все не запускали. У нас другое развлечение было - диалоги в транзакциях. Ну это легко отлавливается. |
|
28.06.2013, 11:14 | #8 |
Участник
|
|
|
28.06.2013, 11:34 | #9 |
Участник
|
Возможно я слишком самонадеян.
Попробую прогнать вашим инструментом. |
|
04.07.2013, 14:37 | #10 |
Участник
|
|
|
15.07.2013, 12:44 | #11 |
Участник
|
Мощно конечно, но если возникают такие вещи в коде, напрашивается вопрос: Вы вообще рефакторинг делаете? Разбив сложный метод с транзакциями на несколько простых, можно все-таки транзакции вычленить примерно в такую форму:
X++: void complexMethod() { ttsbegin; simpleMethods(); ttscommit; }
__________________
// no comments |
|
15.07.2013, 19:41 | #12 |
Участник
|
|
|
16.07.2013, 07:37 | #13 |
Участник
|
Попробовал на АХ3 и АХ4, пишет ошибку:
invalid command name "Exportfile" while executing "Exportfile for AOT version 1.0 or later"
__________________
// no comments |
|
28.06.2013, 12:43 | #14 |
Участник
|
Да, и у нас встречалась пара - тройка таких косяков. Поскольку у нас ничего критичного(кроме неудобства) не происходило, остановились на том, что джобик с одной строчкой ttsabort; всегда есть под рукой.
__________________
-Ты в гномиков веришь? -Нет. -А они в тебя верят, смотри, не подведи их. |
|
28.06.2013, 13:23 | #15 |
Роман Долгополов (RDOL)
|
Ну по всякому бывает. Бывает что не сильно критично, а бывает что и очень даже. На моей памяти был косяк когда пакетник в трешке несколько суток импортировал из внешней системы документы и справочники. Из за вставленного кем-то посреди метода return всё это накопилось в очень длинные транзакции, "завершившиеся" только когда на оракловой базе в логах кончилось место вместе с вполне логичным откатом всего нажитого за эти дни
|
|
28.06.2013, 14:06 | #16 |
Участник
|
Цитата:
Сообщение от db
Ну по всякому бывает. Бывает что не сильно критично, а бывает что и очень даже. На моей памяти был косяк когда пакетник в трешке несколько суток импортировал из внешней системы документы и справочники. Из за вставленного кем-то посреди метода return всё это накопилось в очень длинные транзакции, "завершившиеся" только когда на оракловой базе в логах кончилось место вместе с вполне логичным откатом всего нажитого за эти дни
__________________
-Ты в гномиков веришь? -Нет. -А они в тебя верят, смотри, не подведи их. |
|
15.07.2013, 13:38 | #17 |
Участник
|
В любом приложении должен быть на первом месте такой джоб
X++: static void aaa_ttscommit(Args _args) { ; info(int2str(appl.ttsLevel())); while (appl.ttsLevel()) ttscommit; } |
|
Теги |
ttscomit, best practice |
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|