Вот тебе, бабка и безопасные вычисления! БСП

У клиента на релизе БП 3.0.165.21 перестала работать моя универсальная обработка загрузки данных.

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

База была серверная, отладка не доступна. Я грешил на ошибку базы данных, предлагал сделать тестирование и исправление. Искал решение на форумах.

Но оказалось, что в другой базе загрузка работал. Пока не обновили на этот релиз.

В журнале регистрации выглядело как классический откат транзакции:

Но я не использовал транзакции в своем коде.

Пришлось сделать файловую копию базы и разбираться.

И тут же нашлась ошибка.

Обратите внимание, как сейчас выглядит код функции ОбщегоНазначения.ВычислитьВБезопасномРежиме:

// Вычисляет переданное выражение, предварительно устанавливая безопасный режим выполнения кода
// и безопасный режим разделения данных для всех разделителей, присутствующих в составе конфигурации.
//
// Для выполняемого кода так же применяются дополнительные ограничения, запрещается:
// 
// 1) Вызов методов:
//   - Выполнить
//   - Вычислить
//   - ЗафиксироватьТранзакцию
//   - СократитьЖурналРегистрации
//   - УстановитьИспользованиеСобытияЖурналаРегистрации
//   - ЗапуститьПриложение
//
// 2) Вызов общих модулей и свойств глобального контекста:
//   - ДлительныеОперации
//   - ПользователиИнформационнойБазы
//
// Параметры:
//  Выражение - Строка - выражение на встроенном языке 1С:Предприятия.
//  Параметры - Произвольный - контекст, который требуется для вычисления выражения.
//    В тексте выражения обращение к контексту должно происходить по имени "Параметры".
//    Например, выражение "Параметры.Значение1 = Параметры.Значение2" обращается к значениям
//    "Значение1" и "Значение2" переданные в Параметры, как свойства.
//
// Возвращаемое значение:
//   Произвольный - результат вычисления выражения.
//
// Пример:
//
//  // Пример 1
//  Параметры = Новый Структура;
//  Параметры.Вставить("Значение1", 1);
//  Параметры.Вставить("Значение2", 10);
//  Результат = ОбщегоНазначения.ВычислитьВБезопасномРежиме("Параметры.Значение1 = Параметры.Значение2", Параметры);
//
//  // Пример 2
//  Результат = ОбщегоНазначения.ВычислитьВБезопасномРежиме("СтандартныеПодсистемыСервер.ВерсияБиблиотеки()");
//
Функция ВычислитьВБезопасномРежиме(Знач Выражение, Знач Параметры = Неопределено) Экспорт
	
	ПроверитьАлгоритм(Выражение);
	
	УстановитьБезопасныйРежим(Истина);
	
	Если ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		МодульРаботаВМоделиСервиса = ОбщийМодуль("РаботаВМоделиСервиса");
		МассивРазделителей = МодульРаботаВМоделиСервиса.РазделителиКонфигурации();
	Иначе
		МассивРазделителей = Новый Массив;
	КонецЕсли;
	
	Для Каждого ИмяРазделителя Из МассивРазделителей Цикл
		
		УстановитьБезопасныйРежимРазделенияДанных(ИмяРазделителя, Истина);
		
	КонецЦикла;
	
	Попытка
		Если ТранзакцияАктивна() Тогда
			Результат = Вычислить(Выражение);
		Иначе
			НачатьТранзакцию();
			Попытка
				Результат = Вычислить(Выражение);
				ОтменитьТранзакцию();
			Исключение
				ОтменитьТранзакцию();
				ВызватьИсключение;
			КонецПопытки;
		КонецЕсли;
		
		Возврат Результат;
	Исключение
		ИнформацияОбОшибке = ИнформацияОбОшибке();
		Уточнение = ОбщегоНазначенияКлиентСервер.УточнениеИсключения(ИнформацияОбОшибке);
		ВызватьИсключение(Уточнение.Текст, КатегорияОшибки.ОшибкаВнешнегоИсточникаДанных ,,, ИнформацияОбОшибке);
	КонецПопытки;
	
КонецФункции

Как видите, транзакция отменяется. Ранее такого не было, так что 1С пошла на изменение поведения функции. Зачем — непонятно. Вот код функции из предыдущих релизов:

Функция ВычислитьВБезопасномРежиме(Знач Выражение, Знач Параметры = Неопределено) Экспорт
	
	УстановитьБезопасныйРежим(Истина);
	
	Если ПодсистемаСуществует("ТехнологияСервиса.БазоваяФункциональность") Тогда
		МодульРаботаВМоделиСервиса = ОбщийМодуль("РаботаВМоделиСервиса");
		МассивРазделителей = МодульРаботаВМоделиСервиса.РазделителиКонфигурации();
	Иначе
		МассивРазделителей = Новый Массив;
	КонецЕсли;
	
	Для Каждого ИмяРазделителя Из МассивРазделителей Цикл
		
		УстановитьБезопасныйРежимРазделенияДанных(ИмяРазделителя, Истина);
		
	КонецЦикла;
	
	Возврат Вычислить(Выражение);
	
КонецФункции

Поэтому я временно отказался от этой функции, использую свою.

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

В итоге получилось, реализация записалась:

Меня удивляет, что 1С шатает из стороны в сторону и она не может определиться, как должен выглядеть код безопасного выполнения. Теперь добавила туда и транзакции, полностью исключив возможность изменения данных. При этом нарушив обратную совместимость функционала. Есть некая часть обработок для фреша, которые перестанут работать после этого обновления БСП.

Вот тебе, бабка, и Юрьев День!

Можно попробовать вызывать код в транзакции, тогда ветка ТранзакцияАктивна не сработает.

Среда: БП 3.0.165.21 БСП 3.1.10.357 Объем: 2 час.

fixin

Программирую на 1С с 1999 года. В 1С просто Гений. В 2020 году ушел из офиса на вольные хлеба фриланса. Принимаю заказы.

Читайте также:

комментариев 16

  1. Серх*й:

    Если ТранзакцияАктивна() Тогда
    Результат = Вычислить(Выражение);
    Иначе
    НачатьТранзакцию();
    Попытка
    Результат = Вычислить(Выражение);
    ОтменитьТранзакцию();
    Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
    КонецПопытки;
    КонецЕсли;

    Как-то не очень логично, зачем что-то пытаться вычислять здесь, если всё равно будет откат транзакции.. Ps Походу индусов понабрали)

  2. rzd:

    Получается обработка неуниверсальная

  3. bob32:

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

    • Обновить не получится, для фреша будут обрезанные версии без универсальности. Теперь универсальность там не возможна.

  4. Иван:

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

    Попытка
    Результат = Вычислить(Выражение);
    ЗафиксироватьТранзакцию();
    Исключение
    ОтменитьТранзакцию();
    ВызватьИсключение;
    КонецПопытки;

    • думаю, тут вообще не нужно лезть в транзакции.

    • Серх*й:

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

      А пользователи на что?)

      • мы говорим про 1С. они пишут на всю Россию, потому предварительно должны тестировать на кошечках тестах

        • Серх*й:

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

          • ну я при хорошем отношении компании к своим клиентам.
            речь не про кампанию 1С в ее нынешнем гнилом виде, конечно же.

  5. Юлия:

    Тоже столкнулась с этим изменением, задала вопрос с службу поддержки. И Вот ответ:

    Добрый день,

    Методы ВыполнитьВБезопасномРежиме и ВычислитьВБезопасномРежиме предназначены для безопасного выполнения произвольного кода, соответственно изменение данных конфигурации в них недопустимо. Информация об этом описана в комментарии к ним.
    Если ваш сценарий допускает изменение изменение данных, то вы можете открывать внешнюю транзакцию перед вызовом метода, но следует учитывать, что запись в некоторые таблицы всё равно будет запрещена. Список таких таблиц можно получить в составе подписок на события ПроверитьБезопасныйРежимПередЗаписью и ПроверитьБезопасныйРежимПередЗаписьюНабораЗаписи.

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

      • Юлия:

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

        • я использовал это для загрузки документов в универсальной загрузке из Excel, все в одном блоке ВычислитьБезопасно.
          Если один из документов не проводится, то вся внешняя транзакция откатывается.
          В общем, придется переделывать.

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

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