Транзакция в бухгалтерии что это

Транзакции «1С». Что это такое, и зачем они нужны?

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Известно ли вам, за что отвечают транзакции «1С»? В одной из наших статей мы уже рассказывали о таких понятиях, как блокировки и взаимоблокировки. Важно отметить, что эти явления возможны именно благодаря транзакциям «1С».

Что такое транзакция в «1С»?

Транзакция – это последовательность действий, переводящая базу данных из одного целостного состояния в другое целостное состояние. Примером транзакции может служить перевод денежных средств с одной банковской карты на другую.

Транзакции могут быть выполнены до конца или не выполнены вообще. Варианта «наполовину выполненная транзакция» не существует. В СУБД транзакции фиксируются методом COMMIT.

Какими свойствами должна обладать транзакция?

ACID (Atomicity, Consistency, Isolation, Durability) – самый распространенный набор требований к транзакциям «1С».

Обязательные свойства любой транзакции:

Как работать с транзакциями «1С»?

Создавать транзакции можно двумя способами:

Вложенные транзакции не поддерживаются в «1С». Если вы несколько раз открываете транзакцию, она «сливается» в одну. При фиксации или отмене эти действия производятся со всеми транзакциями, активируемыми ранее.

При низкой производительности «1С» правильно созданные транзакции помогут работать быстрее. Когда транзакция проводится без ошибок, то в системе не возникает блокировок и взаимоблокировок. Вы не увидите сообщений об ошибке, и отмены проведения документа не произойдет.

Если у вас возникли вопросы по созданию транзакций и оптимизации «1С», обратитесь за консультацией к специалистам «ГЭНДАЛЬФ».

Источник

Что такое трансакция

Трансакция – операции с денежными средствами: перевод, вывод или зачисление на счет. То есть любой процесс, связанный с использованием банковским счетом. Слово происходит от английского transaction – «дело, сделка, соглашение».

Существует несколько видов операций, которые принято называть трансакциями.

— Банковская трансакция – перевод средств с одного счета на другой. Например, в тарифных сборниках банков стоимость операций по переводу денег может быть указана в процентах от суммы либо как цена одной трансакции, то есть твердая сумма за операцию.

— Бухгалтерская трансакция – также перевод средств между счетами, но в рамках одного предприятия. При этом необходимо учитывать, что одна трансакция – это не обязательно всего одна проводка, так как некоторые операции требуют нескольких записей.

— Трансакция на рынке ценных бумаг – это сделка с теми или иными активами. Количество покупаемых или продаваемых акций, облигаций или производных финансовых инструментов за одну трансакцию может быть любым.

Также следует выделить свойства трансакции, такие как:

— Неразрывность. Трансакция — неделимая единица работы, она выполняется целиком или действие не происходит. За обеспечение неразрывности отвечает СУБД (система управлениями баз данных).

— Согласованность. Каждая сделка должна обеспечить перевод информации из одного слаженного состояния в другое.

— Изолированность. Трансакции выполняются независимо друг от друга. То есть промежуточные итоги незавершенной операции не должны быть доступными для новой сделки.

— Устойчивость. Результат успешной трансакции сохраняется в базе данных постоянно и не должен теряться при случайных сбоях.

Кроме того, термин «трансакция» широко применим при операциях с кредитными картами. Причем принято различать онлайн-трансакции, когда происходит немедленная связь с процессинговым центром, и офлайн-трансакции, при которых этого не происходит.

Источник

ACID. Что под капотом у транзакции

От корректного функционирования базы данных (БД) может зависеть не только скорость, но и надежность приложения. Для глубокого погружения в задачи специалисту, как правило, нужно освоить работу с транзакциями – об этом и пойдет речь ниже. Рассмотрим виды и свойства транзакций, а также постараемся понять, как устроен этот механизм. Надеемся, что статья может быть полезна начинающим разработчикам и всем, кто хочет лучше разобраться в теме.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

От автора: однажды у меня спросили, что такое транзакция. Я попробовал рассказать простыми словами, но у меня не получилось, хотя я часто использовал это понятие. Поэтому прежде, чем говорить о свойствах транзакций, постараемся дать определение, для начала своими словами.

Что такое транзакция (transaction)?

Транзакция — это некий набор связанных операций с базой данных.

В первом приближении это действительно так. Однако, пока определение неполное. Не хватает самого главного, а именно — этот набор операций должен представлять единую логическую систему с данными.

Например, давайте представим такую ситуацию: у каждого человека есть карта, с помощью которой он может совершать определенные действия, будь то онлайн-покупка, перевод денежных средств с карты на карту, оплата счетов и т.д. Какие операции происходят в базе данных при совершении перевода денежных средств с одного лицевого счета на другой? В этой ситуации необходимо выполнить два запроса к базе данных:

С первого лицевого счета происходит списание N-ой суммы денежных средств.

На второй лицевой счет идет зачисление этой же суммы.

В данном случае эти две операции связаны и составляют единую логическую систему работы с данными. Теперь можно дать полное определение транзакции.

Транзакция — это набор последовательных операций с базой данных, соединенных в одну логическую единицу.

Виды транзакций

Транзакции делят на два вида:

Неявные транзакции, которые предусмотрены на уровне базы данных. Например, БД задает отдельную инструкцию INSERT, UPDATE или DELETE как единицу транзакции.

Явные транзакции — их начало и конец явно обозначаются такими инструкциями, как BEGIN TRANSACTION, COMMIT или ROLLBACK.

В ORM Laravel при использовании фасада DB есть возможность явно указать транзакцию с помощью конструкции DB::transaction(). Если необходимо больше гибкости, можно обратиться к конструкциям DB::beginTransaction(), DB::rollBack(), DB::commit().

Свойства транзакции

Выделяют так называемые «магические» свойства транзакции, которые описываются аббревиатурой «ACID». Каждая буква аббревиатуры означает одно из свойств, о которых мы поговорим ниже.

Atomicity или атомарность (A)

Вернемся к предыдущему примеру с переводом денежных средств между двумя лицевыми счетами. Мы установили, что эти 2 операции, которые взаимодействуют с базой данных, являются операциями транзакции. А какие проблемы могут возникнуть, если мы просто выполним эти операции последовательно, отправив два запроса к БД?

Первый запрос выполнится успешно. С первого лицевого счета будет списана N-ая сумма денежных средств.

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

В этой ситуации речь идет о проблеме потери данных. В целях снижения этого риска транзакции обладают таким свойством, как атомарность (atomicity), неделимость: либо будут выполнены все действия транзакции, либо никакие.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Consistency или согласованность (C)

Согласованность означает, что если до выполнения транзакции данные в БД находятся в неком состоянии «good state»*, то они будут в этом же состоянии и после выполнения транзакции.

*Иными словами, выполняется некий набор условий. Примеры: в таблице countries не должно быть двух строк с названием страны «Российская Федерация»; возраст человека не может быть больше 150 лет.

На самом деле ни одна база данных не может гарантировать свойство согласованности. А всё потому, что поддержание консистентности — это прерогатива приложения, а не БД. База данных лишь предоставляет инструменты для выполнения данного свойства транзакции, например, уникальные ключи, внешние ключи и т.д.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Isolation или изоляция (I)

Переходим к самому интересному свойству — изоляции. Представим ситуацию, когда в определенный момент времени с системой работают несколько пользователей. Естественно, операции транзакции в БД выполняются параллельно, чтобы ускорить работу системы. Но у параллельной работы транзакций есть свои подводные камни:

Если операции транзакции взаимодействуют с разным набором непересекающихся данных, все работает корректно.

Но что будет, если две и более операций транзакции в один момент времени начнут работать с одним и тем же набором данных? Возникнет явление, называемое race condition (состояние гонки).

Выделяют несколько эффектов, связанных с этим явлением.

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

Представим, что у клиента банка есть счет, на котором находится 1000 денежных единиц. Транзакции А и В считывают данное значение из БД. Транзакция А должна увеличить данную сумму на 100 денежных единиц, а транзакция В — на 200. Транзакция А увеличивает сумму денежных единиц на счёте на 100 (итого 1100) и записывает значение в БД, транзакция В увеличивает сумму на 200 денежных единиц и записывает в БД (итого 1200). В результате на счете должно оказаться 1300, а по факту имеем 1200 денежных единиц.

Эффект грязного чтения возникает, когда транзакция считывает данные, которые еще не были зафиксированы.

Представим, что транзакция А переводит все деньги клиента на другой счет, но не фиксирует изменения. Транзакция В считывает изменения счёта А, получает 0 денежных единиц на счете и отказывает клиенту в выдаче наличных. Транзакция А прерывается и отменяет перевод между счетами.

Эффект неповторяемого чтения возникает, когда транзакция считывает дважды одну и ту же строку, но каждый раз получает разные результаты.

Например, по правилу согласованности клиент банка не может иметь отрицательный баланс на счёте. Транзакция А хочет уменьшить баланс счета клиента на 200 денежных единиц. Она проверяет текущее значение суммы на счёте — 500 денежных единиц. В это время транзакция В уменьшает сумму на счёте до 0 и фиксирует изменения. Если бы транзакция А повторно проверила сумму, то получила бы 0 денежных единиц, но на основе первоначальных данных она уже приняла решение уменьшить значение, и счет уходит в минус.

Эффект чтения фантомов возникает, когда набор данных соответствует условиям поиска, но изначально не отображается.

Например, правило согласованности запрещает иметь клиенту более 3 лицевых счетов. Для открытия нового счета транзакция А проверяет все счета клиента банка и в результате получает 2 счета. В этот момент транзакция B открывает еще один счет клиенту и фиксирует изменения (3 счета). Если бы транзакция А повторно проверила количество лицевых счетов клиента, то их оказалось бы 3, и по правилу согласованности открытие нового счета было бы невозможно.

Решение

Для устранения данных эффектов на уровне баз данных предусмотрены уровни изоляции, или transaction isolation levels, которые так или иначе реализованы во многих СУБД. Для примера рассмотрим движок InnoDB в СУБД MySQL:

Read uncommitted – это уровень изоляции, при котором каждая транзакция видит незафиксированные изменения другой транзакции. Справляется с эффектом потерянного обновления, но остаются остальные проблемы: эффекты грязного чтения, неповторяемого чтения, чтения фантомов.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Все запросы SELECT считывают данные в неблокирующей манере.

Блокирующее чтение (SELECT … FOR UPDATE, LOCK IN SHARE MODE), UPDATE и DELETE блокирует искомые индексные строки. Таким образом, возможна вставка данных в промежутки между индексами. Промежутки блокируются только при проверках на дублирующиеся и внешние ключи.

Read committed — это уровень изоляции, при котором параллельно исполняющиеся транзакции видят только зафиксированные изменения других транзакций. Справляется с эффектами потерянного обновления и грязного чтения, остаются эффекты неповторяемого чтения и чтения фантомов.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Согласованное чтение не накладывает блокировок, однако считывает данные из свежего снэпшота. В остальном ведёт себя так же, как и read uncommitted.

Repeatable read или snapshot isolation — это уровень изоляции, при котором транзакция не видит изменения данных, прочитанные ей ранее, однако способна прочитать новые данные, соответствующие условию поиска. Справляется с эффектами потерянного обновления, грязного чтения, неповторяемого чтения, остается эффект чтения фантомов.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Согласованное чтение не накладывает блокировок и считывает данные из снэпшота, который создается при первом чтении в транзакции. Таким образом, одинаковые запросы вернут одинаковый результат.

Блокировка для блокирующего чтения будет зависеть от типа условия:

если условие с диапазоном, например, WHERE (id > 7), то блокируется весь диапазон;

если уникальное, например, WHERE (id = 7), то блокируется одна индексная запись.

Кстати, в InnoDB именно уровень repeatable read используется по умолчанию.

Serializable — это уровень изоляции, при котором каждая транзакция выполняется так, как будто параллельных транзакций не существует. Справляется со всеми перечисленными выше эффектами.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Аналогично repeatable read, но есть интересный момент. Если выключен autocommit (а при явном старте транзакции START TRANSACTION он выключен по умолчанию), то все запросы SELECT превращаются в запросы SELECT … LOCK IN SHARE MODE.

SELECT … LOCK IN SHARE MODE – блокирует считываемые строки на запись.

SELECT … FOR UPDATE – блокирует считываемые строки на чтение.

Теперь, когда разобрались со всеми подводными камнями, сформулируем определение изоляции.

Изоляция — это свойство транзакции, которое позволяет скрывать изменения, внесенные одной операцией транзакции при возникновении явления race condition.

Durability или долговечность (D)

Долговечность означает, что если транзакция выполнена, и даже если в следующий момент произойдет сбой в системе, результат сохранится.

Если вы пользуетесь облачными хранилищами, такими как Amazon S3, то могли заметить, что разные тарифы обещают вам разное количество девяток durability. В контексте облака durability означает сохранность ваших данных и то, как они реплицируются. Чем больше копий ваших данных в разных точках мира, тем выше вероятность их не потерять из-за наводнения, землетрясения или нашествия инопланетян. В контексте «ACID» это обычно означает, что после фиксирования данные записываются в постоянное хранилище.

Вывод

Как мы рассмотрели выше, ошибки при проведении транзакций могут приводить к нежелательным последствиям в работе с системой. В статье мы осветили возможные риски при проведении транзакции и то, как ее “магические свойства” помогают справиться с каждой отдельной проблемой. Надеемся, что этот материал был вам полезен, и ждем ваших комментариев.

Источник

Вы не умеете работать с транзакциями

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Заголовок вышел броским, но накипело. Сразу скажу, что речь пойдет об 1С. Дорогие 1С-ники, вы не умеете работать с транзакциями и не понимаете что такое исключения. К такому выводу я пришел, просматривая большое количество кода на 1С, рождаемого в дебрях отечественного энтерпрайза. В типовых конфигурациях с этим все достаточно хорошо, но ужасающее количество заказного кода написано некомпетентно с точки зрения работы с базой данных. Вы когда-нибудь видели у себя ошибку «В данной транзакции уже происходили ошибки»? Если да — то заголовок статьи относится и к вам. Давайте под катом разберемся, наконец, что такое транзакции и как правильно с ними обращаться, работая с 1С.

Почему надо бить тревогу

Для начала, давайте разберемся, что же такое представляет собой ошибка «В данной транзакции уже происходили ошибки». Это, на самом деле, предельно простая штука: вы пытаетесь работать с базой данных внутри уже откаченной (отмененной) транзакции. Например, где-то был вызван метод ОтменитьТранзакцию, а вы пытаетесь ее зафиксировать.

Почему это плохо? Потому что данная ошибка ничего не говорит вам о том, где на самом деле случилась проблема. Когда в саппорт от пользователя приходит скриншот с таким текстом, а в особенности для серверного кода, с которым интерактивно не работает человек — это… Хотел написать «критичная ошибка», но подумал, что это buzzword, на который уже никто не обращает внимания…. Это задница. Это ошибка программирования. Это не случайный сбой. Это косяк, который надо немедленно переделывать. Потому что, когда у вас фоновые процессы сервера встанут ночью и компания начнет стремительно терять деньги, то «В данной транзакции уже происходили ошибки» это последнее, что вы захотите увидеть в диагностических логах.

Есть, конечно, вероятность, что технологический журнал сервера (он ведь у вас включен в продакшене, да?) как-то поможет диагностировать проблему, но я сейчас навскидку не могу придумать вариант — как именно в нем найти реальную причину указанной ошибки. А реальная причина одна — программист Вася получил исключение внутри транзакции и решил, что один раз — не карабас «подумаешь, ошибка, пойдем дальше».

Что такое транзакции в 1С

Неловко писать про азбучные истины, но, видимо, немножго придется. Транзакции в 1С — это то же самое, что транзакции в СУБД. Это не какие-то особенные «1С-ные» транзакции, это и есть транзакции в СУБД. Согласно общей идее транзакций, они могут либо выполниться целиком, либо не выполниться совсем. Все изменения в таблицах базы данных, выполненные внутри транзакции, могут быть разом отменены, как будто ничего не было.

Далее, нужно понимать, что в 1С не поддерживаются вложенные транзакции. Собственно говоря, они не поддерживаются не «в 1С», а вообще не поддерживаются. По-крайней мере, теми СУБД, с которыми умеет работать 1С. Вложенных транзакций, например, нет в MS SQL и Postgres. Каждый «вложенный» вызов НачатьТранзакцию просто увеличивает счетчик транзакций, а каждый вызов «ЗафиксироватьТранзакцию» — уменьшает этот счетчик. Данное поведение описано в множестве книжек и статей, но выводы из этого поведения, видимо, разобраны недостаточно. Строго говоря, в SQL есть т.н. SAVEPOINT, но 1С их не использует, да и вещь это достаточно специфичная.

Здесь и далее, специально для Воинов Истинной Веры, считающих, что код должен писаться только на английском, под спойлерами будет приведен аналог кода в англоязычном синтаксисе 1С.

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

Вы же наверняка пишете такой код, да? Приведенный пример кода содержит ошибки. Как минимум, три. Знаете какие? Про первую я скажу сразу, она связана с объектными блокировками и не имеет отношения непосредственно к транзакциям. Про вторую — чуть позже. Третья ошибка — это deadlock, который возникнет при параллельном исполнении этого кода, но это тема для отдельной статьи, ее рассматривать сейчас не будем, дабы не усложнять код. Ключевое слово для гугления: deadlock управляемые блокировки.

Обратите внимание, простой ведь код. Такого в ваших 1С-системах просто вагон. И он содержит сразу, как минимум, 3 ошибки. Задумайтесь на досуге, сколько ошибок есть в более сложных сценариях работы с транзакциями, написанных вашими программистами 1С 🙂

Объектные блокировки

Суть проблемы в том, что в указанном примере кода изменяется объект базы данных, но в другом сеансе может сидеть интерактивный пользователь (или соседний фоновый поток), который тоже будет менять этот объект. Здесь один из вас может получить ошибку «запись была изменена или удалена». Если это произойдет в интерактивном сеансе, то пользователь почешет репу, ругнется и попробует переоткрыть форму. Если это произойдет в фоновом потоке, то вам придется искать это в логах. А журнал регистрации, как вы знаете, медленный, а ELK-стек для журналов 1С у нас в отрасли настраивают единицы… (мы, к слову, входим в число тех, кто настраивает и другим помогает настраивать :))

Короче говоря, это досадная ошибка и лучше, чтобы ее не было. Поэтому, в стандартах разработки четко написано, что перед изменением объектов необходимо ставить на них объектную блокировку методом «ОбъектСправочника.Заблокировать()«. Тогда параллельный сеанс (который тоже должен так поступить) не сможет начать операцию изменения и получит ожидаемый, управляемый отказ.

А теперь про транзакции

С первой ошибкой разобрались, давайте перейдем ко второй.

Если не предусмотреть проверку исключения в этом методе, то исключение (например, весьма вероятное на методе «Записать()») выбросит вас из данного метода без завершения транзакции. Исключение из метода «Записать» может быть выброшено по самым разным причинам, например, сработают какие-то прикладные проверки в бизнес-логике, или возникнет упомянутая выше объектная блокировка. Так или иначе, вторая ошибка гласит: код, начавший транзакцию, не несет ответственность за ее завершение.

Транзакция в бухгалтерии что это. Смотреть фото Транзакция в бухгалтерии что это. Смотреть картинку Транзакция в бухгалтерии что это. Картинка про Транзакция в бухгалтерии что это. Фото Транзакция в бухгалтерии что это

Именно так я бы назвал эту проблему. В нашем статическом анализаторе кода 1С на базе SonarQube мы даже отдельно встроили такую диагностику. Сейчас я работаю над ее развитием, и фантазия программистов 1С, чей код попадает ко мне на анализ, порой приводит меня в шок и трепет…

Почему? Потому что выброшенное наверх исключение внутри транзакции в 90% случаев не даст эту транзакцию зафиксировать и приведет к ошибке. Следует понимать, что 1С автоматически откатывает незавершенную транзакцию только после возвращения из скриптового кода на уровень кода платформы. До тех пор, пока вы находитесь на уровне кода 1С, транзакция остается активной.

Поднимемся на уровень выше по стеку вызовов:

Смотрите, что получается. Наш проблемный метод вызывается откуда-то извне, выше по стеку. На уровне этого метода разработчик понятия не имеет — будут ли какие-то транзакции внутри метода ОченьПолезныйИВажныйКод или их не будет. А если будут — то будут ли они все завершены… Мы же все тут за мир и инкапсуляцию, верно? Автор метода «ВажныйКод» не должен думать про то, что именно происходит внутри вызываемого им метода. Того самого, в котором некорректно обрабатывается транзакция. В итоге, попытка поработать с базой данных после выброса исключения изнутри транзакции, с высокой вероятностью приведет к тому, что «В данной транзакции бла-бла…»

Размазывание транзакций по методам

Второе правило «транзакционно-безопасного» кода: счетчик ссылок транзакций в начале метода и в его конце должен иметь одно и то же значение. Нельзя начинать транзакцию в одном методе и завершать ее в другом. Из этого правила, наверное, можно найти исключения, но это будет какой-то низкоуровневый код, который пишут более компетентные люди. В общем случае так писать нельзя.

Выше — неприемлемый говнокод. Нельзя писать методы так, чтобы вызывающая сторона помнила и следила за возможными (или вероятными — как знать) транзакциями внутри других методов, которые она вызывает. Это нарушение инкапсуляции и разрастание спагетти-кода, который невозможно трассировать, сохраняя рассудок.

Особенно весело вспомнить, что реальный код намного больше синтетических примеров из 3-х строчек. Выискивать начинающиеся и завершающиеся транзакции по шести уровням вложенности — это прям мотивирует на задушевные беседы с авторами.

Пытаемся исправить код

Вернемся к исходному методу и попытаемся его починить. Сразу скажу, что объектную блокировку мы чинить пока не будем, просто, чтобы не усложнять код примера.

Первый подход типичного 1С-ника

Обычно программисты 1С знают, что при записи может быть выдано исключение. А еще они боятся исключений, поэтому стараются их все перехватывать. Например, вот так:

Ну как, стало лучше, да? Ведь теперь, возможные ошибки записи обрабатываются и даже логируются. Исключения больше не возникнут при записи объекта. И в логе даже видно — на каком объекте, не поленился, вывел в сообщение ссылку вместо лаконичного «Ошибка записи справочника», как это часто любят писать вечно торопящиеся разработчики. Иными словами, налицо забота о пользователе и рост компетенций.

Однако, опытный 1С-ник здесь скажет, что нет, лучше не стало. По сути ничего не поменялось, а может даже стало и хуже. В методе «Записать()» платформа 1С сама начнет транзакцию записи, и эта транзакция будет уже вложенной по отношению к нашей. И если в момент работы с базой данных 1С свою транзакцию откатит (например, будет выдано исключение бизнес-логики), то наша транзакция верхнего уровня все равно будет помечена как «испорченная» и ее нельзя будет зафиксировать. В итоге этот код так и останется проблемным, и при попытке фиксации выдаст «уже происходили ошибки».

А теперь представьте, что речь идет не о маленьком методе, а о глубоком стеке вызовов, где в самом низу кто-то взял и «выпустил» начатую транзакцию из своего метода. Верхнеуровневые процедуры могут и понятия не иметь, что кто-то там внизу начинал транзакции. В итоге, весь код валится с невнятной ошибкой, которую расследовать невозможно в принципе.

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

Методы работы с транзакциями в 1С

Не будет лишним напомнить, что вообще 1С предоставляет нам для работы с транзакциями. Это всем известные методы:

Первые 3 метода очевидны и делают то, что написано в их названии. Последний метод — возвращает Истину, если счетчик транзакций больше нуля.

И есть интересная особенность. Методы выхода из транзакции (Зафиксировать и Отменить) выбрасывают исключения, если счетчик транзакций равен нулю. То есть, если вызвать один из них вне транзакции, то возникнет ошибка.

Как правильно пользоваться этими методами? Очень просто: надо прочитать сформулированное выше правило: код, начавший транзакцию, должен нести ответственность за ее завершение.

Как же соблюсти это правило? Давайте попробуем:

Выше мы уже поняли, что метод ДелаемЧтоТо — потенциально опасен. Он может выдать какое-то исключение, и транзакция «вылезет» наружу из нашего метода. Окей, добавим обработчик возможного исключения:

Отлично, мы поймали возникающую ошибку, но что с ней делать? Записать сообщение в лог? Ну, может быть, если код логирования ошибок должен быть именно на этом уровне и ошибку мы тут ждем. А если нет? Если мы не ожидали тут никаких ошибок? Тогда мы должны просто передать это исключение выше, пусть с ними разбирается другой слой архитектуры. Делается это оператором «ВызватьИсключение» без аргументов. В этих ваших джава-сиплюсплюсах это делается точно так же оператором throw.

Так, стоп… Если мы просто прокидываем исключение дальше, то зачем тут вообще нужна Попытка? А вот зачем: правило заставляет нас обеспечить завершение начатой нами транзакции.

Теперь, вроде бы, красиво. Однако, мы ведь помним, что не доверяем коду ДелаемЧтоТо(). Вдруг там внутри его автор не читал этой статьи, и не умеет работать с транзакциями? Вдруг он там взял, да и вызвал метод ОтменитьТранзакцию или наоборот, зафиксировал ее? Нам очень важно, чтобы обработчик исключения не породил нового исключения, иначе исходная ошибка будет потеряна и расследование проблем станет невозможным. А мы помним, что методы Зафиксировать и Отменить могут выдать исключение, если транзакция не существует. Здесь-то и пригождается метод ТранзакцияАктивна.

Финальный вариант

Наконец, мы можем написать правильный, «транзакционно-безопасный» вариант кода. Вот он:

**UPD: в комментариях предложен более безопасный вариант, когда ЗафиксироватьТранзакцию расположен внутри блока Попытка. Здесь приведен именно этот вариант, ранее Фиксация располагалась после блока Попытка-Исключение.

Постойте, но ведь не только «ОтменитьТранзакцию» может выдавать ошибки. Почему же тогда «ЗафиксироватьТранзакцию» не обернут в такое же условие с «ТранзакцияАктивна»? Опять же, по тому же самому правилу: код, начавший транзакцию, должен нести ответственность за ее завершение. Наша транзакция необязательно самая первая, она может быть вложенной. На нашем уровне абстракции мы обязаны заботиться только о нашей транзакции. Все прочие должны быть нам неинтересны. Они чужие, мы не должны нести за них ответственность. Именно НЕ ДОЛЖНЫ. Нельзя предпринимать попыток выяснения реального уровня счетчика транзакций. Это опять нарушит инкапсуляцию и приведет к «размазыванию» логики управления транзакциями. Мы проверили активность только в обработчике исключения и только для того, чтобы убедиться, что наш обработчик не породит нового исключения, «прячущего» старое.

Чек-лист рефакторинга

Давайте рассмотрим несколько наиболее распространенных ситуаций, требующих вмешательства в код.

Паттерн:

Обернуть в «безопасную» конструкцию с Попыткой, Проверкой активности и пробросом исключения.

Паттерн:

Анализ и Рефакторинг. Автор не понимал, что делает. Начинать вложенные транзакции можно безопасно. Не нужно проверять условие, нужно просто начать вложенную транзакцию. Ниже по модулю он наверняка еще там извращается с их фиксацией. Это гарантированный геморрой.

Примерно похожий вариант:

аналогично: фиксация транзакции по условию — это странно. Почему тут условие? Что, кто-то иной мог уже зафиксировать эту транзакцию? Повод для разбирательства.

Паттерн:

Паттерн:

Данная транзакция уже не завершится в случае исключения. Нет смысла продолжать цикл. Код требуется переписать, сверяясь с исходной задачей. Дополнительно обеспечить более информативное сообщение об ошибке.

В заключение

Я, как вы уже, наверное, догадались, отношусь к людям, любящим платформу 1С и разработку на ней. К платформе, разумеется, есть претензии, особенно в среде Highload, но в общем и целом, она позволяет недорого и быстро разрабатывать очень качественные корпоративные приложения. Давая из коробки и ORM, и GUI, и веб-интерфейс, и Reporting, и много чего еще. В комментариях на Хабре обычно пишут всякое высокомерное, так вот, ребята — основная проблема 1С, как экосистемы — это не платформа и не вендор. Это слишком низкий порог вхождения, который позволяет попадать в отрасль людям, не понимающим, что такое компьютер, база данных, клиент-сервер, сеть и всякое такое. 1С сделала разработку корпоративных приложений слишком легкой. Я за 20 минут могу написать на ней учетную систему для закупок/продаж с гибкими отчетами и веб-клиентом. После этого, мне несложно подумать о себе, что и на больших масштабах можно писать примерно так же. Как-то там 1С сама все внутри сделает, не знаю как, но наверное сделает. Напишу-ка я «НачатьТранзакцию()».

И знаете — самое главное, что это прекрасно. Простота разработки в 1С позволяет моментально реализовывать бизнес-идеи и встраивать их в процессы компании. Потом всегда можно отрефакторить, главное понимать как. И если вдруг вам нужна помощь в аудите вашей «медленной 1С» — обращайтесь к специалистам по оптимизации. Она совсем не медленная.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *