Проблема связанных SQLNCLI серверов. «Никакая транзакция не активна

Независимо от выбранного варианта работы (файловый или клиент-серверный) система «1С:Предприятие» обеспечивает работу с информацией, хранящейся в базе данных, с использованием механизма транзакций.

Транзакция - это неделимая с точки зрения воздействия на базу данных последовательность операций манипулирования данными. Она выполняется по принципу «все или ничего» и переводит базу данных из одного целостного состояния в другое целостное состояние. Если по каким-либо причинам одно из действий транзакции невыполнимо или произошло какое-либо нарушение работы системы, база данных возвращается в то состояние, которое было до начала транзакции (происходит откат транзакции).

Система «1С:Предприятие» осуществляет неявный вызов транзакций при выполнении любых действий, связанных с модификацией информации, хранящейся в базе данных. Например, все обработчики событий, расположенные в модулях объектов и наборов записей, связанные с модификацией данных базы данных, вызываются в транзакции. В транзакции выполняется также чтение объектов следующих типов: ПланОбменаОбъект, ДокументОбъект, СправочникОбъект, ПланВидовХарактеристикОбъект, ПланВидовРасчетаОбъект, ПланСчетовОбъект, БизнесПроцессОбъект, ЗадачаОбъект, ПоследовательностьНаборЗаписей, РегистрСведенийНаборЗаписей, РегистрНакопленияНаборЗаписей, РегистрБухгалтерииНаборЗаписей, РегистрРасчетаНаборЗаписей, ПерерасчетНаборЗаписей . При этом в режиме управляемых блокировок выполняется установка разделяемой блокировки по значению регистратора для наборов записей и по значениям отбора для набора записей независимого регистра сведений.

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

Использование явного вызова транзакций

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

● при окончании выполнения встроенного языка (обработчик события, внешнее соединение, automation-сервер);

● при передаче управления с сервера на клиента.

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

Попытка

НачатьТранзакцию();

// Последовательность операторов

ЗафиксироватьТранзакцию();

Исключение

ОтменитьТранзакцию();

КонецПопытки;

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

● невосстановимые,

● восстановимые.

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

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

Попытка … Исключение … КонецПопытки.

Вложенный вызов транзакций

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

НачатьТранзакцию();

НачатьТранзакцию();

ЗафиксироватьТранзакцию();

// Вложенный вызов транзакции

НачатьТранзакцию();

ЗафиксироватьТранзакцию();

ЗафиксироватьТранзакцию();

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

ВНИМАНИЕ! Система «1С:Предприятие» не поддерживает вложенных транзакций. Это означает, что всегда действует только транзакция самого верхнего уровня.

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

Влияние транзакций на работу программных объектов

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

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

В этом правиле есть исключения. В силу значительной прикладной специфики программных объектов системы «1С:Предприятие» в некоторых случаях откат изменений, выполненных в базе данных, все же может влиять на значения свойств соответствующих программных объектов. Это происходит в следующих случаях:

● при отмене транзакции признак проведения документа восстанавливает значение, которое было до начала транзакции;

● если объект был создан и записан в транзакции, то при откате транзакции очищается значение ссылки;

● если объект создавался вне транзакции и при записи его в транзакции использовался код/номер, сгенерированный автоматически, то при отмене транзакции код/номер очищается.

3

Я пытаюсь выполнить хранимую процедуру и просто вставить ее результаты во временную таблицу, и я получаю следующее сообщение:

Операция не может быть выполнена, поскольку поставщик OLE DB «SQLNCLI «для связанного сервера« MyServerName »не удалось начать распределенную транзакцию. Поставщик OLE DB «SQLNCLI» для связанного сервера «MyServerName» возвратил сообщение «Никакая транзакция не активна».

Мой запрос выглядит следующим образом:

INSERT INTO #TABLE EXEC MyServerName.MyDatabase.dbo.MyStoredProcedure Param1, Param2, Param3

Точное число колонки, имена, проблема не является результатом.

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

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

Помогите, пожалуйста? :)

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

  • 3 ответа
  • Сортировка:

    Активность

3

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

Сегодня нам пришлось принудительно перезагрузить компьютер на нашем сервере разработки из-за неисправности без разрыва, и когда мы загрузили сервер, угадайте, что? Оно работает!

Так что для записи я изменил определенную конфигурацию MSDTC, добавил ее как связанный сервер и разрешил RPC IN и OUT, а также изменил конфигурацию RPC для «НЕТ АУТЕНТИФИКАЦИИ ТРЕБУЕТСЯ» или что-то в этом роде.

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

Я перезагрузил свой сервер, как... дважды, так как я изменил его, и он все еще не работает. Но как сегодня, после полного отключения и включения, это работает!

Что касается синтаксиса, я сохранил то же самое.

2

Вы пробовали использовать openquery?

Insert into table select * from openquery(myservername, "exec mydatabase.dbo.mystoredproc param1, param2, param3")

0

Из MSDN документации: (http://msdn.microsoft.com/en-us/library/ms188427(SQL.90).aspx) В SQL Server 2000 и более поздних версий, OPENQUERY не могут быть использованы для выполнения расширенные хранимые процедуры на связанном сервере. Однако расширенная хранимая процедура может быть выполнена на связанном сервере с использованием имени из четырех частей. Например: EXEC SeattleSales.master.dbo.xp_msver - Smur 28 май. 10 2010-05-28 20:52:56

1

Вы также должны проверить разрешение DNS-имен в конфигурации IP-сети.

Например, у вас есть сервер под названием server-a.mydomain.com, а другой - server-b.otherdomain.com, войдите в сервер-a и выполните «ping-server-b» (без домена).

Если он отвечает «Запрос Ping не смог найти хост-сервер-b. Пожалуйста, проверьте имя и повторите попытку». это проблема.

Перейти к панели управления> Сетевые подключения> Щелкните правой кнопкой мыши на сетевой карте> Свойства> Интернет-протокол> Свойства> Дополнительно> DNS> Добавьте этот DNS-суффикс по порядку. И здесь добавить локальный домен: mydomain.com, а затем добавить удаленный домен: otherdomain.com Нажмите OK, пока не закроется

Теперь, если вы выполните «пинг сервера б» он должен repond что-то вроде.

Не редко при работе в 1С возникает ошибка «Конфликт блокировок при выполнении транзакций: Превышено максимальное время ожидания предоставления блокировки». Суть ее кроется в том, что несколько сеансов пытаются одновременно выполнить похожие действия, затрагивая один и тот же ресурс. Сегодня мы разберемся как исправить данную ошибку

Большое количество выполняемых операций

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

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

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

Регламентные задания

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

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

«Зависшие сеансы»

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

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

Ошибки при написании конфигурации

Все типовые конфигурации разработаны квалифицированными специалистами и экспертами. Каждая система тщательно тестируется и оптимизируется для более быстрой и корректной работы в ней.

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

Большая вероятность, что конфликт блокировки возник именно из-за ошибок разработчика, если он возник после обновления программы. Для проверки можно просто «откатить» доработки, либо произвести рефакторинг кода.

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

Транзакция — логически связанная, неделимая последовательность действий. Транзакция может быть либо выполнена целиком, либо вообще не выполнена. Для фиксации транзакции в СУБД используется метод COMMIT.

Типичный пример транзакции — перевод денежных средств с одного счета на другой:

  1. начать транзакцию;
  2. прочесть количество денежных средств на счету номер 123;
  3. уменьшить баланс счета 123 на 100 рублей;
  4. сохранить баланс счёта номер 123;
  5. прочесть количество денежных средств на счету номер 321;
  6. увеличить баланс на 100 рублей;
  7. записать новое количество денежных средств на счете 321;
  8. зафиксировать транзакцию.

Получите 267 видеоуроков по 1С бесплатно:

Как мы видим, если транзакция выполнена не полностью, то она не имеет смысла.

Ключевые требования (ACID) к транзакционной СУБД

Одним из наиболее распространённых наборов требований к транзакциям и транзакционным СУБД является набор ACID (Atomicity, Consistency, Isolation, Durability). Это те свойства, которыми должна обладать любая транзакция:

  • Атомарность (Atomicity) — никакая транзакция не должна быть зафиксирована частично;
  • Согласованность (Consistency) — система находится в согласованном состоянии до начала транзакции и должна остаться в согласованном состоянии после завершения транзакции;
  • Изолированность (Isolation) — во время выполнения транзакции параллельные транзакции не должны оказывать влияние на её результат;
  • Надежность (Durability) — в случая сбоя изменения, сделанные успешно завершённой транзакцией, должны остаться сохранёнными после возвращения системы в работу.

Транзакции в 1С

Транзакции в 1С 8.3 и 8.2 создаются как автоматически, так и описываются разработчиками.

С помощью метода ТранзакцияАктивна() можно узнать, активна ли транзакция.

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

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

Если мы обратимся к технической документации или к диску ИТС, то увидим, что фирма 1С рекомендует следующий способ организации транзакции в попытке

Попытка //1. Начало транзакции. НачатьТранзакцию() ; //2. Блок операций, выполняющихся в транзакции. //3. Если все операции успешны, фиксируем транзакцию. ЗафиксироватьТранзакцию() ; Исключение //4. Если при выполнении кода возникли ошибки, отменяем транзакцию. ОтменитьТранзакцию() ; //5. При необходимости запись в журнал регистрации. //6. При необходимости вывод сообщения пользователю. КонецПопытки ;

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

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

&НаСервереБезКонтекста НачатьТранзакцию() ; //записываем новый товар Товар = Справочники. Товары. СоздатьЭлемент() ; Товар. Наименование = "Дырокол" ; Товар. Записать() ; //записываем цену НаборЗаписей = РегистрыСведений. Цена. СоздатьНаборЗаписей() ; НоваяЗапись = НаборЗаписей. Добавить() ; НоваяЗапись. Период = ТекущаяДата() ; НоваяЗапись. Товар = Товар. Ссылка; НоваяЗапись. Сумма = 100 ; НаборЗаписей. Записать() ; ЗафиксироватьТранзакцию() ; КонецПроцедуры

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

&НаСервереБезКонтекста Процедура ВыполнитьТранзакциюНаСервере() //создаем новый товар Товар = Справочники. Товары. СоздатьЭлемент() ; Товар. Наименование = "Дырокол" ; //Создаем запись с ценой НаборЗаписей = РегистрыСведений. Цена. СоздатьНаборЗаписей() ; НоваяЗапись = НаборЗаписей. Добавить() ; НоваяЗапись. Период = ТекущаяДата() ; НоваяЗапись. Сумма = 100 ; //Выполняем транзакцию в попытке Попытка НачатьТранзакцию() ; Товар. Записать() ; НоваяЗапись. Товар = Товар. Ссылка; НаборЗаписей. Записать() ; ЗафиксироватьТранзакцию() ; Исключение ОтменитьТранзакцию() ; Сообщение = Новый СообщениеПользователю; Сообщение. Текст = ; Сообщение. Сообщить() ; ЗаписьЖурналаРегистрации("Произошла ошибка при записи товара и его цены" ) ; КонецПопытки ; КонецПроцедуры

Как НЕ НАДО делать

У тех кто только начинает работать с транзакциями зачастую возникает желание сделать вот таким образом

НачатьТранзакцию() ; Попытка НачатьТранзакцию() ; //Блок операций ЗафиксироватьТранзакцию() ; Исключение ОтменитьТранзакцию() ; КонецПопытки ; Попытка НачатьТранзакцию() ; //Блок операций ЗафиксироватьТранзакцию() ; Исключение ОтменитьТранзакцию() ; КонецПопытки ; ЗафиксироватьТранзакцию() ;

Или в цикле

НачатьТранзакцию() ; Для каждого Данные Из МассивДанных Цикл Попытка НачатьТранзакцию() ; Данные. Записать() ; ЗафиксироватьТранзакцию() ; Исключение ОтменитьТранзакцию() ; КонецПопытки ; КонецЦикла ; ЗафиксироватьТранзакцию() ;

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

&НаСервереБезКонтекста Процедура ВыполнитьТранзакциюНаСервере() НачатьТранзакцию() ; Попытка НачатьТранзакцию() ; Товар = Справочники. Товары. СоздатьЭлемент() ; Товар. Наименование = "Стол" ; Товар. Записать() ; ВызватьИсключение "Ошибка записи товара." ; ЗафиксироватьТранзакцию() ; Исключение ОтменитьТранзакцию() ; Сообщение = Новый СообщениеПользователю; Сообщение. Текст = ОписаниеОшибки() Попытка НачатьТранзакцию() ; Товар = Справочники. Товары. СоздатьЭлемент() ; Товар. Наименование = "Стул" ; Товар. Записать() ; ЗафиксироватьТранзакцию() ; Исключение ОтменитьТранзакцию() ; Сообщение = Новый СообщениеПользователю; Сообщение. Текст = ОписаниеОшибки() ; Сообщение. Сообщить() ; КонецПопытки ; ЗафиксироватьТранзакцию() ; КонецПроцедуры

В результате выполнения этой процедуры увидим в окне сообщений следующее:

{ВнешняяОбработка.ТранзакцииВПопытке.Форма.Форма.Форма(20)}: Ошибка записи товара. {ВнешняяОбработка.ТранзакцииВПопытке.Форма.Форма.Форма(40)}: Ошибка при вызове метода контекста (Записать): В данной транзакции уже происходили ошибки!

Таким образом, организация вложенных транзакций в 1С абсолютно бессмысленна.

Возможные варианты

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

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

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