private void LoadAllTransactions() { _transactionLog.Seek(0, SeekOrigin.Begin); var reader = new BinaryReader(_transactionLog); _lastOffset = reader.ReadInt64(); try { while (true) { var transaction = ReadTransaction(reader); if (!transaction.EndMarkerOk) { throw new FormatException( "Inconsistent transaction log. End marker not found at the end of a transaction"); } if (transaction.TransactionStatus != TransactionStatus.Processed && transaction.TransactionStatus != TransactionStatus.Canceled) { _transactionQueue.Enqueue(transaction); } } } catch (EndOfStreamException) { // ignore (end of log) } if (_transactionQueue.Count > 0) { // only the last transaction may be in "Processing" status. In this case there was a failure during transaction processing // we mark it "ToProcess". Transactions are idempotent var lastTransaction = _transactionQueue.Peek(); if (lastTransaction.TransactionStatus == TransactionStatus.Processing) { ServerLog.LogWarning("Reprocessing incomplete transaction"); lastTransaction.TransactionStatus = TransactionStatus.ToProcess; UpdateTransactionStatus(lastTransaction); } _queueNotEmpty.Set(); } }
private void LoadAllTransactions() { _transactionLog.Seek(0, SeekOrigin.Begin); var reader = new BinaryReader(_transactionLog); _lastOffset = reader.ReadInt64(); var completeTransaction = true; try { while (true) { var status = reader.ReadInt32(); completeTransaction = false; var timestamp = reader.ReadInt64(); var length = reader.ReadInt32(); var data = reader.ReadBytes(length); var offset = reader.ReadInt64(); var id = reader.ReadInt64(); var delay = reader.ReadInt32(); var endMarker = reader.ReadInt64(); if (endMarker != EndMarker) { throw new FormatException( "Inconsistent transaction log. End marker not found at the end of a transaction"); } completeTransaction = true; var time = new DateTime(timestamp); var transaction = new TransactionData { TransactionStatus = (TransactionStaus)status, TimeStamp = time, Data = data, Offset = offset, Id = id, DelayInMilliseconds = delay }; if (transaction.TransactionStatus != TransactionStaus.Processed && transaction.TransactionStatus != TransactionStaus.Canceled) { _transactionQueue.Enqueue(transaction); } } } catch (EndOfStreamException) { if (!completeTransaction) { throw new FormatException( "Inconsistent transaction log. End of file reached in the middle of a transaction"); } } if (_transactionQueue.Count > 0) { // only the last transaction may be in "Processing" status. In this case there was a failure during transaction processing // we mark it "ToProcess". Transactions are idempotent var lastTransaction = _transactionQueue.Peek(); if (lastTransaction.TransactionStatus == TransactionStaus.Processing) { ServerLog.LogWarning("Reprocessing incomplete transaction"); lastTransaction.TransactionStatus = TransactionStaus.ToProcess; UpdateTransactionStatus(lastTransaction); } _queueNotEmpty.Set(); } }
public void ProcessTransactionRequest(TransactionRequest transactionRequest, IClient client, SafeDictionary <string, DataStore> dataStores) { if (transactionRequest.TransactionId == default) { client.SendResponse(new ExceptionResponse( new CacheException( "Transaction request received without transaction id"))); return; } // First try to acquire a write lock on all concerned data stores var types = transactionRequest.AllCollections; var keys = dataStores.Keys; if (types.Any(t => !keys.Contains(t))) { throw new NotSupportedException("Type not registered"); } var transactionId = transactionRequest.TransactionId; // do not work too hard if it's single stage if (transactionRequest.IsSingleStage) { ProcessSingleStageTransactionRequest(transactionRequest, client, dataStores); return; } var lockAcquired = _lockManager.TryAcquireWriteLock(transactionId, Constants.DelayForLockInMilliseconds, types.ToArray()); if (lockAcquired) { var answer = client.ShouldContinue(); if (answer.HasValue && answer.Value) { } else { _lockManager.CloseSession(transactionId); return; } } else { client.SendResponse(new ExceptionResponse( new CacheException( $"can not acquire write lock on server for transaction {transactionRequest.TransactionId}"), ExceptionType.FailedToAcquireLock)); 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 { CheckConditions(transactionRequest, dataStores); // if we reach here the condition check has passed SaveDurableTransaction(transactionRequest, dataStores); client.SendResponse(new ReadyResponse()); Dbg.Trace($"S: end writing delayed transaction {transactionRequest.TransactionId}"); } catch (CacheException e) { Dbg.Trace($"error in first stage:{e.Message} "); client.SendResponse(new ExceptionResponse(e, e.ExceptionType)); // failed to write a durable transaction so stop here // unlock _lockManager.CloseSession(transactionRequest.TransactionId); return; } catch (Exception e) { Dbg.Trace($"error in first stage:{e.Message} "); client.SendResponse(new ExceptionResponse(e)); // failed to write a durable transaction so stop here // unlock _lockManager.CloseSession(transactionRequest.TransactionId); 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 ExecuteInMemory(transactionRequest, dataStores); ServerLog.LogInfo( $"S: two stage transaction committed successfully {transactionRequest.TransactionId}"); } else { ServerLog.LogWarning( $"S: two stage transaction cancelled by client on server {transactionRequest.TransactionId}"); // cancel the delayed transaction _transactionLog?.CancelDelayedTransaction(); } } else // the client failed to answer in a reasonable delay (which is less than the delay to commit a delayed transaction ) { _transactionLog?.CancelDelayedTransaction(); } } catch (Exception e) { ServerLog.LogInfo($"error in the second stage of a transaction:{e.Message}"); } // unlock _lockManager.CloseSession(transactionRequest.TransactionId); }