Показать сообщение отдельно
Старый 06.11.2011, 19:41   #8  
Logger is offline
Logger
Участник
Лучший по профессии 2015
Лучший по профессии 2014
 
3,873 / 3123 (112) ++++++++++
Регистрация: 12.10.2004
Адрес: Москва
Записей в блоге: 2
Вот пример джоба :
X++:
// GRD_R4453_InventSumInventTrans_pkoz, Разъехались остатки в InventSum и проводки в InventTrans., pkoz, 04.11.2011
static void Job791_15(Args _args)
{
    #define.recIdTrans(5637151827)
    InventTrans     InventTrans;
    InventSum       InventSum;
    ;
    try
    {
        ttsBegin;
        try
        {
            InventTrans = InventTrans::findRecId(#recIdTrans, true);
            InventTrans.Qty += 1.0;
            InventTrans.Update();
            breakpoint;
        }
        catch
        {
            info("поймали внутрений catch");
        }
        ttsCommit;
    }
    catch
    {
        info("поймали внешний catch");
    }

    info(strFMT("InventTrans.RowCount() = %1, InventTrans.Qty = %2, InventSum : %3 ",
        InventTrans.RowCount(),
        InventTrans.Qty,
        con2str(Global::buf2Con(InventSum::find(InventTrans.ItemId, InventTrans.inventDimId)), ";  ")
        ));

}
Использовать так :
1. Проставляем в джобе recID существующей проводки InventTrans
2. Открываем 2 клиента аксапты.
3. Запускаем джоб на 1-м клиенте, ждем когда он выпадет в отладчик на точке останова.
4. Запускаем тот же джоб на 2-м клиенте. Он повиснет.
5. Жмем F5 в отладчике на 1-м клиенте.

В итоге после выполнения джоба на обоих клиентах, получаем что InventTrans.Qty увеличилось на 1, а соответствующая колонка в InventSum увеличилась на 2.

Причина такого поведения, в том что мы просто поставили обработку catch в которую попадают исключения
Exception::UpdateConflict
и
Exception:uplicateKeyException
которые в свою очередь не откатывают транзакцию.

Поэтому чтобы избегать в модификациях подобных ошибок, нельзя ставить обработку catch без указания вида исключения.
Ну или по крайней мере перед ней всегда делать обработку вышеупомянутых типов исключений.

Для удобства, я завел макрос Catch_DangerousException, который можно везде вставлять в блок обработки.
Текст макроса
X++:
catch (Exception::UpdateConflict)
{
    if (appl.ttsLevel() == 0)
    {
        if (xSession::currentRetryCount() >= 5)
        {
            throw Exception::UpdateConflictNotRecovered;
        }
        else
        {
            retry;
        }
    }
    else
    {
        throw Exception::UpdateConflict;
    }
}

catch (Exception::DuplicateKeyException)
{
    // info(strfmt("@SYS123267", ));

    if (appl.ttsLevel() != 0)
    {
        throw Exception::DuplicateKeyExceptionNotRecovered;
    }
}
Упомянутый в заголовке темы пример следует переписать так :

X++:
            ...
            ttsBegin;
            try
            {
                // здесь расположен код обновляющий InventTrans - например комплектация или резервирование
            }
            catch(Exception::Error)
            {
                // обработка                    
            }

            #catch_DangerousException

            catch 
            {   // сюда попадает обработка конфликта обновления записи
                // ничего не делаем
            }
            ttsCommit;
            ...
За это сообщение автора поблагодарили: Vadik (25), Ruff (10), kashperuk (20).