Распределенные транзакции в WCF: WCF и CLR хранимые процедуры. Часть 1.
Тема организации распределенных транзакций между различными составляющими системы затрагивается довольно часто. В серии последующих постов постараюсь рассказать о различных способах формирования транзакций для повышения интероперабельности системы. Поддержка в WCF спецификаций WS-* (в том числе WS-AtomicTransaction и WS-Coordination) в значительной степени облегчает поставленную задачу.
Первый пост затронет создание транзакций между CRL хранимой процедурой в SQL Server 2008 и Oracle 11g. Допустим, в организации над одним модулем системы работают несколько отделов. Отдел2 разрабатывает backend часть системы и предоставляет набор веб-сервисов, для вызова соответствующих операций. А отедл1 занимается frontend составляющей и должен использовать определенную комбинацию операций, опубликованных сервисов, для выполнения поставленной задачи (необходимо, чтобы при неуспешном выполнении какого-либо вызова сервиса откатывалась вся задача целиком). Пример подобной архитектуры приведен на рисунке:
Подобную схему достаточно просто реализовать:
- Транзакция инициализируется в хранимой процедуре 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».
При организации подобной распределенной транзакции не стоит забывать о высокой вероятности возникновения блокировок на доступ к данным, что может привести к значительному снижению производительности системы.
В следующий раз расскажу об использовании SQL Server Broker'а для выполнения похожей задачи.