public void ExecuteTransaction(IList <CachedObject> itemsToPut, IList <OrQuery> conditions, IList <CachedObject> itemsToDelete = null) { var locksOk = false; var iteration = 0; while (!locksOk) { var delay = ThreadLocalRandom.Instance.Next(10 * iteration); TransactionStatistics.Retries(iteration + 1); Dbg.Trace( $"C: delay = {delay} for iteration {iteration} single stage transaction connector {GetHashCode()}"); if (delay > 0) { Thread.Sleep(delay); } if (itemsToPut == null) { throw new ArgumentNullException(nameof(itemsToPut)); } var request = new TransactionRequest(itemsToPut, conditions, itemsToDelete) { IsSingleStage = true }; TransactionStatistics.ExecutedAsSingleStage(); var response = Channel.SendRequest(request); if (response is NullResponse) { locksOk = true; } else if (response is ExceptionResponse exResponse) { if (exResponse.ExceptionType != ExceptionType.FailedToAcquireLock) { throw new CacheException(exResponse.Message, exResponse.ExceptionType); } } } TransactionStatistics.NewTransactionCompleted(); }
private void SendRequestsAndWaitForLock <TRequest>(List <CacheClient> clients, ConcurrentDictionary <int, TRequest> requestByServer, ConcurrentDictionary <int, Session> sessions, string transactionId, ConcurrentDictionary <int, bool> serverStatus) where TRequest : Request { var locksOk = false; var iteration = 0; while (!locksOk) { try { var delay = ThreadLocalRandom.Instance.Next(10 * iteration); TransactionStatistics.Retries(iteration + 1); Dbg.Trace( $"C: delay = {delay} for iteration {iteration} transaction {transactionId} connector {GetHashCode()}"); if (delay > 0) { Thread.Sleep(delay); } // send transaction requests Parallel.ForEach(clients, client => { var request = requestByServer[client.ShardIndex]; try { var session = client.Channel.BeginSession(); sessions[client.ShardIndex] = session; Dbg.Trace( $"C: Sending transaction request to server {client.ShardIndex} transaction {transactionId} connector {GetHashCode()}"); client.Channel.PushRequest(session, request); } catch (Exception e) { // here if communication exception serverStatus[client.ShardIndex] = false; Dbg.Trace($"C: Exception while sending request to server {client.ShardIndex}:{e.Message}"); } }); // wait for servers to acquire lock Parallel.ForEach(clients, client => { try { var session = sessions[client.ShardIndex]; var answer = client.Channel.GetResponse(session); if (answer is ReadyResponse) { serverStatus[client.ShardIndex] = true; } else { serverStatus[client.ShardIndex] = false; } } catch (Exception e) { // here if communication exception serverStatus[client.ShardIndex] = false; Dbg.Trace($"C: Exception while sending request to server {client.ShardIndex}:{e.Message}"); } }); } catch (AggregateException e) { // this code should never be reached throw new CacheException( $"Error in the first stage of a two stage transaction:{e.InnerExceptions.First()}"); } locksOk = serverStatus.Values.All(s => s); Dbg.Trace(!locksOk ? $"C: Failed to acquire lock for transaction {transactionId}. retrying " : $"C: Lock acquired for all servers: transaction {transactionId} "); if (locksOk == false) { Parallel.ForEach(clients, client => { if (serverStatus[client.ShardIndex]) { var session = sessions[client.ShardIndex]; client.Channel.PushRequest(session, new ContinueRequest { Rollback = true }); } }); } else { Parallel.ForEach(clients, client => { var session = sessions[client.ShardIndex]; client.Channel.PushRequest(session, new ContinueRequest { Rollback = false }); }); } iteration++; TransactionStatistics.NewAttemptToLock(); } }