private void ProcessSingleStageTransactionRequest(TransactionRequest transactionRequest, IClient client) { var typesToPut = transactionRequest.ItemsToPut.Select(i => i.FullTypeName).Distinct().ToList(); var typesToDelete = transactionRequest.ItemsToDelete.Select(i => i.FullTypeName).Distinct().ToList(); var types = typesToPut.Union(typesToDelete).Distinct(); try { foreach (var type in types) { if (!DataStores[type].Lock.TryEnterWriteLock(Constants.DelayForLock)) { throw new CacheException("Failed to acquire write locks for single-stage transaction", ExceptionType.FailedToAcquireLock); } } Dbg.Trace($"S: fallback to single stage for transaction {transactionRequest.TransactionId}"); // check the conditions (in case of conditional update) int index = 0; foreach (var condition in transactionRequest.Conditions) { if (!condition.IsEmpty()) { var ds = DataStores[condition.TypeName]; ds.CheckCondition(transactionRequest.ItemsToPut[index].PrimaryKey, condition); } index++; } PersistenceEngine?.NewTransaction(new MixedTransaction { ItemsToDelete = transactionRequest.ItemsToDelete, ItemsToPut = transactionRequest.ItemsToPut } ); // update the data in memory var dataRequests = transactionRequest.SplitByType(); foreach (var dataRequest in dataRequests) { if (!DataStores.ContainsKey(dataRequest.FullTypeName)) { throw new NotSupportedException($"The type {dataRequest.FullTypeName} is not registered"); } DataStores[dataRequest.FullTypeName].ProcessRequest(dataRequest, client, null); } client.SendResponse(new NullResponse()); Dbg.Trace($"S: end writing delayed transaction {transactionRequest.TransactionId}"); } catch (CacheException e) { client.SendResponse(new ExceptionResponse(e, e.ExceptionType)); } catch (Exception e) { Dbg.Trace($"error writing delayed transaction:{e.Message}"); client.SendResponse(new ExceptionResponse(e)); // failed to write a durable transaction so stop here } finally { // release acquired locks foreach (var type in types) { if (DataStores[type].Lock.IsWriteLockHeld) { DataStores[type].Lock.ExitWriteLock(); } } } }
private void ProcessTransactionRequest(TransactionRequest transactionRequest, IClient client) { // First try to acquire a write lock on all concerned data stores var typesToPut = transactionRequest.ItemsToPut.Select(i => i.FullTypeName).Distinct().ToList(); var typesToDelete = transactionRequest.ItemsToDelete.Select(i => i.FullTypeName).Distinct().ToList(); var types = typesToPut.Union(typesToDelete).Distinct().ToList(); if (types.Any(t => !DataStores.ContainsKey(t))) { throw new NotSupportedException("Type not registered"); } // do not work too hard if it's single stage if (transactionRequest.IsSingleStage) { ProcessSingleStageTransactionRequest(transactionRequest, client); return; } if (!AcquireWriteLock(client, types, transactionRequest.TransactionId)) { return; } Dbg.Trace($"S: lock acquired by all clients for transaction {transactionRequest.TransactionId}"); // Second register a durable delayed transaction. It can be cancelled later try { // check the conditions (in case of conditional update) int index = 0; foreach (var condition in transactionRequest.Conditions) { if (!condition.IsEmpty()) { var ds = DataStores[condition.TypeName]; ds.CheckCondition(transactionRequest.ItemsToPut[index].PrimaryKey, condition); } index++; } Dbg.Trace($"S: begin writing delayed transaction {transactionRequest.TransactionId}"); PersistenceEngine?.NewTransaction(new MixedTransaction { ItemsToDelete = transactionRequest.ItemsToDelete, ItemsToPut = transactionRequest.ItemsToPut }, true ); client.SendResponse(new ReadyResponse()); Dbg.Trace($"S: end writing delayed transaction {transactionRequest.TransactionId}"); } catch (CacheException e) { Dbg.Trace($"error in first stage:{e.Message} server {ShardIndex}"); client.SendResponse(new ExceptionResponse(e, e.ExceptionType)); // failed to write a durable transaction so stop here // unlock foreach (var type in types) { if (DataStores[type].Lock.IsWriteLockHeld) { DataStores[type].Lock.ExitWriteLock(); } } return; } catch (Exception e) { Dbg.Trace($"error in first stage:{e.Message} server {ShardIndex}"); client.SendResponse(new ExceptionResponse(e)); // failed to write a durable transaction so stop here // unlock foreach (var type in types) { if (DataStores[type].Lock.IsWriteLockHeld) { DataStores[type].Lock.ExitWriteLock(); } } return; } try { Dbg.Trace($"S: begin waiting for client go {transactionRequest.TransactionId}"); var answer = client.ShouldContinue(); Dbg.Trace($"S: end waiting for client go answer = {answer}"); if (answer.HasValue) // the client has answered { if (answer.Value) { // update the data in memory var dataRequests = transactionRequest.SplitByType(); foreach (var dataRequest in dataRequests) { if (!DataStores.ContainsKey(dataRequest.FullTypeName)) { throw new NotSupportedException( $"The type {dataRequest.FullTypeName} is not registered"); } DataStores[dataRequest.FullTypeName].ProcessRequest(dataRequest, client, null); } ServerLog.LogInfo( $"S: two stage transaction committed successfully on server {ShardIndex} {transactionRequest.TransactionId}"); } else { ServerLog.LogWarning( $"S: two stage transaction cancelled by client on server {ShardIndex} {transactionRequest.TransactionId}"); // cancel the delayed transaction PersistenceEngine?.CancelDelayedTransaction(); } } else // the client failed to answer in a reasonable delay (which is less than the delay to commit a delayed transaction ) { PersistenceEngine?.CancelDelayedTransaction(); } } catch (Exception e) { ServerLog.LogInfo($"error in the second stage of a transaction:{e.Message}"); } // unlock foreach (var type in types) { if (DataStores[type].Lock.IsWriteLockHeld) { DataStores[type].Lock.ExitWriteLock(); } } }