Как исправить ошибку «В данной транзакции уже происходили ошибки!» при обмене мобильного приложения 1С с сервером

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработка мобильных приложений на платформе 1С:Предприятие имеет множество нюансов, особенно когда речь заходит об интеграции и обмене данными с серверной базой через Web-сервисы (HTTP или SOAP). Часто разработчики сталкиваются с ситуацией, когда на локальном компьютере или в эмуляторе обмен проходит идеально, но при подключении реального мобильного устройства система начинает выдавать непонятные сбои.

Одной из самых коварных является ошибка: «В данной транзакции уже происходили ошибки!». Она сопровождается загадочным поведением отладчика, когда простейшие строки кода, такие как Запрос.Выполнить() или даже обычный поиск элемента НайтиПоКоду(), внезапно вызывают аварийное завершение сеанса СУБД. В этой статье мы подробно разберем природу этого явления, выясним, почему оно проявляется на реальных мобильных устройствах, и рассмотрим пошаговое руководство по локализации и исправлению этой проблемы.

1. Анатомия ошибки: что такое «сломанная транзакция»?

Для того чтобы понять, почему падает наш запрос, проанализируем поведение СУБД и платформы 1С при работе с транзакциями. Ошибка «В данной транзакции уже происходили ошибки!» указывает на состояние, которое в системном программировании называется broken transaction (или установка флага setRollbackOnly):

  1. Любое обращение к базе данных в рамках веб-сервиса (особенно при записи документов или справочников) неявно или явно открывает транзакцию.
  2. Если внутри этой активной транзакции происходит абсолютно любое исключение на уровне СУБД (например, неуспешная запись объекта, дублирование уникального индекса, нарушение прав доступа RLS или блокировка базы данных), СУБД помечает текущую транзакцию как испорченную.
  3. Даже если мы предусмотрительно обернули проблемный участок кода в конструкцию Попытка — Исключение — КонецПопытки, перехватили ошибку и попытались продолжить выполнение алгоритма, транзакция уже считается «мертвой». Её невозможно зафиксировать, её можно только откатить.
  4. При любой последующей попытке обратиться к СУБД в рамках этого же серверного вызова (выполнить запрос, получить представление ссылки, найти объект по коду) платформа 1С мгновенно прерывает работу программы и выбрасывает исключение: «В данной транзакции уже происходили ошибки!».

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

2. Почему в эмуляторе все работает, а на реальном устройстве — нет?

Рассмотрим причины, по которым разработчик видит успешный обмен на локальном компьютере, но сталкивается со сбоем при работе с реального мобильного телефона или ТСД (терминала сбора данных):

3. Главная архитектурная ошибка: передача ссылок через Web-сервисы

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

Мобильная СУБД (например, SQLite на Android/iOS) и серверная СУБД (MS SQL, PostgreSQL) — это изолированные базы данных. Идентификатор UUID одного и того же подразделения в них может не совпадать. Когда веб-сервис пытается десериализовать объект XDTO и преобразовать его в СправочникСсылка на сервере, он пытается сопоставить UUID. Если сопоставление завершается сбоем или объект не найден, СУБД генерирует внутреннюю ошибку, которая тут же помечает транзакцию как сломанную еще до того, как управление передается первой строчке нашего тела метода веб-сервиса!

Правильное архитектурное решение: передавать параметры обмена исключительно в виде простых типов данных (строка, число, булево). Например, передавать уникальный идентификатор в виде строки (GUID) или код подразделения.

Рассмотрим, как правильно реализовать получение ссылки на стороне сервера по переданному коду:


// На сервере принимаем параметр как Строку (Код подразделения)
Функция ВыгрузитьСотрудников(КодПодразделения) Экспорт

    // 1. Ищем подразделение по коду
    ПодразделениеСсылка = Справочники.Подразделения.НайтиПоКоду(КодПодразделения);
    
    Если ПодразделениеСсылка.Пустая() Тогда
        ВызватьИсключение "Указанное подразделение с кодом " + КодПодразделения + " не найдено на сервере!";
    КонецЕсли;

    // 2. Формируем запрос
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |	Сотрудники.Ссылка КАК Ссылка,
        |	Сотрудники.Код КАК Код,
        |	Сотрудники.Наименование КАК Наименование
        |ИЗ
        |	Справочник.Сотрудники КАК Сотрудники
        |ГДЕ
        |	Сотрудники.Подразделение В ИЕРАРХИИ(&Подразделение)";
        
    Запрос.УстановитьПараметр("Подразделение", ПодразделениеСсылка);
    
    Попытка
        РезультатЗапроса = Запрос.Выполнить().Выгрузить();
    Исключение
        ЗаписатьЛогОшибки(ОписаниеОшибки());
        ВызватьИсключение "Ошибка выполнения запроса сотрудников: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
    КонецПопытки;

    Возврат СериализоватьТаблицуВXDTO(РезультатЗапроса);

КонецФункции

4. Пошаговое руководство по поиску истинной причины сбоя

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

Шаг 1. Анализ Технологического журнала 1С (Самый надежный метод)

Поскольку обычный Журнал регистрации 1С часто не фиксирует внутренние ошибки СУБД, завернутые в блоки Попытка...Исключение, лучшим решением является настройка Технологического журнала (ТЖ) на сервере 1С:Предприятия. Нам необходимо отследить системное событие SDBL с фильтром по функции установки отката транзакции.

Создайте текстовый файл с именем logcfg.xml и сохраните его в каталоге конфигурации сервера (например, C:\Program Files\1cv8\conf\). Наполните его следующим XML-кодом:


<?xml version="1.0" encoding="UTF-8"?>
<config xmlns="http://v8.1c.ru/v8/tech-log">
    <log location="C:\v8logs\db_errors" history="24">
        <event>
            <eq property="name" value="SDBL"/>
            <eq property="Func" value="setRollbackOnly"/>
        </event>
        <property name="all"/>
    </log>
</config>

Запустите процесс обмена на мобильном устройстве до воспроизведения ошибки. После этого откройте сформированный лог-файл в папке C:\v8logs\db_errors. В логе вы увидите точный стек вызовов функций 1С и SQL-запрос, выполнение которого привело к падению транзакции. Это укажет на конкретную строку кода, ломающую транзакцию выше по стеку.

Шаг 2. Исключение ссылочной зависимости в веб-сервисе

Убедитесь, что ни один метод вашего Web-сервиса не принимает и не возвращает типы данных, отличные от стандартных (строки, числа, даты, булево). Если вам необходимо передать ссылку, передавайте строковое представление её уникального идентификатора (UUID):


// Код на стороне мобильного приложения для передачи UUID подразделения:
СтрокаUUID = Строка(СсылкаНаПодразделение.УникальныйИдентификатор());
// Передаем СтрокаUUID в параметры метода веб-сервиса

На стороне сервера восстанавливаем ссылку из строки:


UUID = Новый УникальныйИдентификатор(ПереданнаяСтрокаUUID);
ПодразделениеДляОтбора = Справочники.Подразделения.ПолучитьСсылку(UUID);

Шаг 3. Проверка подписок на события «ПередЗаписью» и «ПриЗаписи»

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

Шаг 4. Разделение транзакционных пространств обмена

Не выполняйте прием данных от мобильного устройства и выгрузку данных обратно в рамках одного неразрывного вызова веб-сервиса. Разделите процесс обмена на два независимых этапа (два HTTP-запроса):

  1. Первый запрос: Мобильное устройство отправляет свои данные (документы, изменения). Сервер принимает их, записывает, завершает вызов веб-сервиса и фиксирует транзакцию.
  2. Второй запрос: Мобильное устройство запрашивает актуальные справочники. Сервер открывает абсолютно новую чистую транзакцию и без проблем подготавливает пакет данных для мобильного клиента.

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

← На главную