Распределенные транзакции в WCF: WCF и CLR хранимые процедуры. Часть 1.

Тема организации распределенных транзакций между различными составляющими системы затрагивается довольно часто. В серии последующих постов постараюсь рассказать о различных способах формирования транзакций для повышения интероперабельности системы. Поддержка в WCF спецификаций WS-* (в том числе WS-AtomicTransaction и WS-Coordination) в значительной степени облегчает поставленную задачу.

Первый пост затронет создание транзакций между CRL хранимой процедурой в SQL Server 2008 и Oracle 11g. Допустим, в организации над одним модулем системы работают несколько отделов. Отдел2 разрабатывает backend часть системы и предоставляет набор веб-сервисов, для вызова соответствующих операций. А отедл1 занимается frontend составляющей и должен использовать определенную комбинацию операций, опубликованных сервисов, для выполнения поставленной задачи (необходимо, чтобы при неуспешном выполнении какого-либо вызова сервиса откатывалась вся задача целиком). Пример подобной архитектуры приведен на рисунке:

Распределенные транзакции с участием WCF. Схема 1

Подобную схему достаточно просто реализовать:

  • Транзакция инициализируется в хранимой процедуре proc1:

--корневая транзакция

begin DISTRIBUTED transaction

<код процедуры>

в случае успешного выполнения commit transaction

в случае ошибок rollback transaction

  • Далее в рамках этой транзакции вызывается CLR процедура на том же сервере:

EXEC <CLR хранимую процедуру>

  • CLR процедура обращается к WCF сервису или к нескольким WCF сервисам (в этот момент локальная транзакция становится распределенной):  

[Microsoft.SqlServer.Server.SqlProcedure]

public static void CallWS()

{

EndpointAddress address = new EndpointAddress("<адрес>");

WSHttpBinding binding = new WSHttpBinding();

binding.TransactionFlow = true;

try

{

//client proxy для WCF сервиса

Service1Client client = new Service1Client(binding, address);

client.Operation1(5);

}

catch (System.Exception ex)

{

//откат корневой транзакции

Transaction.Current.Rollback();

}

}

WCF использует wshttpbinding, обладающий поддержкой транзакций:

[ServiceContract(SessionMode = SessionMode.Required)]

public interface IService1

{

[OperationContract]

[TransactionFlow(TransactionFlowOption.Mandatory)]

string Operation1(int value);

[OperationContract]

[TransactionFlow(TransactionFlowOption.Mandatory)]

string Operation2(int value);

}

public class Service1 : IService1

{

#region IService1 Members

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]

public string Operation1 (int value) {}

{

<код метода: обращение к Oracle>

OperationContext.Current.SetTransactionComplete();

}

[OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = false)]

public string Operation2 (int value)

{

<код метода: обращение к Oracle>

OperationContext.Current.SetTransactionComplete();

}

#endregion

}

в конфигурационном файле сервиса так же включена поддержка «входящей» транзакции (атрибут transaction flow):

<bindings>

<wsHttpBinding>

<binding name="wsHttBinding1" transactionFlow="true" >

</binding>

</wsHttpBinding>

</bindings>

<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttBinding1" contract="IService1">

 

подробнее о  вызове WCF сервиса из CLR процедур см. "Call a WCF Service from SQLCLR"

  • из методов WCF сервиса идет обращение к Oracle:

    для организации участия Oracle в распределенной транзакции необходимо сконфигурировать MSDTC, см. «How To Configure DTC to Support Oracle Transactions» и «Understanding XA Transactions».

    Конфигурация MSDTC для поддержки XA транзакций

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

В следующий раз расскажу об использовании SQL Server Broker'а для выполнения похожей задачи.