private async Task DoLoad() { this.logger.Debug("DoLoad"); // load inital state TransactionalStorageLoadResponse <TState> loadResponse = await this.storage.Load(StateName); this.eTag = loadResponse.ETag; this.metadata = Metadata.FromString(loadResponse.Metadata); this.version = this.metadata.HighestVersion; this.highestReadTransactionId = this.metadata.HighestReadTransactionId; this.commitedState = loadResponse.CommittedState; foreach (PendingTransactionState <TState> pendingState in loadResponse.PendingStates) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Rebuilding log from storage for {0}", pendingState.SequenceId); } this.log[pendingState.SequenceId] = new LogRecord <TState> { NewVal = pendingState.State, Version = (TransactionalResourceVersion.TryParse(pendingState.TransactionId, out TransactionalResourceVersion version)) ? version : default(TransactionalResourceVersion) }; } UpdateActiveState(); }
/// <summary> /// Find the appropriate version of the state to serve for this transaction. /// We enforce reads in transaction id order, hence we find the version written by the highest /// transaction less than or equal to this one /// </summary> private bool TryGetVersion(long transactionId, out TState readState, out TransactionalResourceVersion readVersion) { readState = this.value; readVersion = this.version; bool versionAvailable = this.version.TransactionId <= transactionId; LogRecord <TState> logRecord = null; foreach (KeyValuePair <long, LogRecord <TState> > kvp in this.log) { if (kvp.Key > transactionId) { break; } logRecord = kvp.Value; } if (logRecord == null) { return(versionAvailable); } readState = logRecord.NewVal; readVersion = logRecord.Version; return(true); }
/// <summary> /// Undo writes to restore state to pre transaction value. /// </summary> private void Rollback(long transactionId) { List <KeyValuePair <long, LogRecord <TState> > > records = this.log.SkipWhile(kvp => kvp.Key < transactionId).ToList(); foreach (KeyValuePair <long, LogRecord <TState> > kvp in records) { if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Removing transaction {0} in rollback", kvp.Key); } this.log.Remove(kvp.Key); this.transactionCopy.Remove(kvp.Key); } if (this.log.Count > 0) { LogRecord <TState> lastLogRecord = this.log.Values.Last(); if (this.logger.IsEnabled(LogLevel.Debug) && this.version != lastLogRecord.Version) { this.logger.Debug("Rolling back from {0} to {1}", this.version, lastLogRecord.Version); } this.version = lastLogRecord.Version; this.value = lastLogRecord.NewVal; } else { if (this.logger.IsEnabled(LogLevel.Debug) && this.version != this.metadata.StableVersion) { this.logger.Debug("Rolling back to stable version, from {0} to {1}", this.version, this.metadata.StableVersion); } this.version = this.metadata.StableVersion; this.value = this.commitedState; } }
public void RecordRead(ITransactionalResource transactionalResource, TransactionalResourceVersion readVersion, long stableVersion) { if (readVersion.TransactionId == TransactionId) { // Just reading our own write here. // Sanity check to see if there's a lost write. int resourceWriteNumber; if (WriteSet.TryGetValue(transactionalResource, out resourceWriteNumber) && resourceWriteNumber > readVersion.WriteNumber) { // Context has record of more writes than we have, some writes must be lost. throw new OrleansTransactionAbortedException(TransactionId, "Lost Write"); } } else { TransactionalResourceVersion resourceReadVersion; if (ReadSet.TryGetValue(transactionalResource, out resourceReadVersion) && resourceReadVersion != readVersion) { // Uh-oh. Read two different versions of the grain. throw new OrleansValidationFailedException(TransactionId); } ReadSet[transactionalResource] = readVersion; if (readVersion.TransactionId != TransactionId && readVersion.TransactionId > stableVersion) { DependentTransactions.Add(readVersion.TransactionId); } } }
/// <summary> /// Transactional Write procedure. /// </summary> public void Save() { var info = TransactionContext.GetTransactionInfo(); if (this.logger.IsEnabled(LogLevel.Debug)) { this.logger.Debug("Write {0}", info); } if (info.IsReadOnly) { // For obvious reasons... throw new OrleansReadOnlyViolatedException(info.TransactionId); } Rollback(); var copiedValue = this.transactionCopy[info.TransactionId]; // // Validation // if (this.version.TransactionId > info.TransactionId || this.highestReadTransactionId >= info.TransactionId) { // Prevent cycles. Wait-die throw new OrleansTransactionWaitDieException(info.TransactionId); } TransactionalResourceVersion nextVersion = TransactionalResourceVersion.Create(info.TransactionId, this.version.TransactionId == info.TransactionId ? this.version.WriteNumber + 1 : 1); // // Update Transaction Context // info.RecordWrite(transactionalResource, this.version, this.metadata.StableVersion.TransactionId); // // Modify the State // if (!this.log.ContainsKey(info.TransactionId)) { LogRecord <TState> r = new LogRecord <TState>(); this.log[info.TransactionId] = r; } LogRecord <TState> logRecord = this.log[info.TransactionId]; logRecord.NewVal = copiedValue; logRecord.Version = nextVersion; this.value = copiedValue; this.version = nextVersion; this.transactionCopy.Remove(info.TransactionId); }
private async Task <bool> PersistCommit(long transactionId) { transactionId = Math.Max(this.highCommitTransactionId, transactionId); if (transactionId <= this.metadata.StableVersion.TransactionId) { // Transaction commit already persisted. return(true); } // find version related to this transaction LogRecord <TState> stableRecord = this.log.First(kvp => kvp.Key <= transactionId).Value; TransactionalResourceVersion stableversion = stableRecord.Version; TState stableState = stableRecord.NewVal; // Trim the logs to remove old versions. // Note that we try to keep the highest version that is below or equal to the ReadOnlyTransactionId // so that we can use it to serve read only transactions. long highestKey = transactionId; foreach (var key in this.log.Keys) { if (key > this.transactionAgent.ReadOnlyTransactionId) { break; } highestKey = key; } if (this.log.Count != 0) { List <KeyValuePair <long, LogRecord <TState> > > records = this.log.TakeWhile(kvp => kvp.Key < highestKey).ToList(); if (this.logger.IsEnabled(LogLevel.Debug)) { records.ForEach(kvp => this.logger.Debug("Removing committed transaction from log: transactionId: {1}", kvp.Key)); } records.ForEach(kvp => this.log.Remove(kvp.Key)); } Metadata newMetadata = new Metadata() { StableVersion = stableversion, HighestVersion = this.version, HighestReadTransactionId = this.highestReadTransactionId, }; this.eTag = await this.storage.Confirm(StateName, this.eTag, newMetadata.ToString(), stableversion.ToString()); this.metadata = newMetadata; this.commitedState = stableState; UpdateActiveState(); return(true); }
public async Task Commit(ITransactionInfo info) { var transactionInfo = (TransactionInfo)info; TransactionsStatisticsGroup.OnTransactionCommitRequest(); if (transactionInfo.IsReadOnly) { return; } var completion = new TaskCompletionSource <bool>(); bool canCommit = true; List <Task <bool> > prepareTasks = new List <Task <bool> >(transactionInfo.WriteSet.Count); foreach (var g in transactionInfo.WriteSet.Keys) { TransactionalResourceVersion write = TransactionalResourceVersion.Create(transactionInfo.TransactionId, transactionInfo.WriteSet[g]); TransactionalResourceVersion?read = null; if (transactionInfo.ReadSet.ContainsKey(g)) { read = transactionInfo.ReadSet[g]; transactionInfo.ReadSet.Remove(g); } prepareTasks.Add(g.Prepare(transactionInfo.TransactionId, write, read)); } foreach (var g in transactionInfo.ReadSet.Keys) { TransactionalResourceVersion read = transactionInfo.ReadSet[g]; prepareTasks.Add(g.Prepare(transactionInfo.TransactionId, null, read)); } await Task.WhenAll(prepareTasks); foreach (var t in prepareTasks) { if (!t.Result) { canCommit = false; } } if (!canCommit) { TransactionsStatisticsGroup.OnTransactionAborted(); abortedTransactions.TryAdd(transactionInfo.TransactionId, 0); throw new OrleansPrepareFailedException(transactionInfo.TransactionId); } commitCompletions.TryAdd(transactionInfo.TransactionId, completion); transactionCommitQueue.Enqueue(transactionInfo); await completion.Task; }
/// <summary> /// Read Log from persistent state interface. /// </summary> private void RevertToPersistedLog() { this.log.Clear(); foreach (KeyValuePair <long, TState> kvp in this.storage.State.Logs) { this.log[kvp.Key] = new LogRecord <TState> { NewVal = kvp.Value, Version = TransactionalResourceVersion.Create(kvp.Key, 1) }; } }
public void RecordWrite(ITransactionalResource transactionalResource, TransactionalResourceVersion latestVersion, long stableVersion) { int writeNumber; WriteSet.TryGetValue(transactionalResource, out writeNumber); WriteSet[transactionalResource] = writeNumber + 1; if (latestVersion.TransactionId != TransactionId && latestVersion.TransactionId > stableVersion) { DependentTransactions.Add(latestVersion.TransactionId); } }
private void DoRecovery() { TransactionalStateRecord <TState> storageState = this.storage.State; this.stableVersion = storageState.StableVersion; this.writeLowerBound = storageState.WriteLowerBound; this.version = storageState.Version; this.value = storageState.Value; RevertToPersistedLog(); // Rollback any known aborted transactions Restore(); }
private void UpdateActiveState() { this.logger.Debug("UpdateActiveState"); if (this.metadata.HighestVersion > this.version) { this.version = this.metadata.HighestVersion; } this.value = this.log.TryGetValue(this.version.TransactionId, out LogRecord <TState> record) ? record.NewVal : this.commitedState; this.log.Clear(); // Rollback any known aborted transactions Rollback(); }
private async Task <bool> PersistCommit(long transactionId) { transactionId = Math.Max(this.highCommitTransactionId, transactionId); if (transactionId <= this.metadata.StableVersion.TransactionId) { // Transaction commit already persisted. return(true); } // find version related to this transaction TransactionalResourceVersion stableversion = this.log.First(kvp => kvp.Key <= transactionId).Value.Version; // Trim the logs to remove old versions. // Note that we try to keep the highest version that is below or equal to the ReadOnlyTransactionId // so that we can use it to serve read only transactions. long highestKey = transactionId; foreach (var key in this.log.Keys) { if (key > this.transactionAgent.ReadOnlyTransactionId) { break; } highestKey = key; } if (this.log.Count != 0) { List <KeyValuePair <long, LogRecord <TState> > > records = this.log.TakeWhile(kvp => kvp.Key < highestKey).ToList(); records.ForEach(kvp => this.log.Remove(kvp.Key)); } this.metadata.StableVersion = stableversion; this.metadata.HighestVersion = this.version; this.metadata.HighestReadTransactionId = this.highestReadTransactionId; this.eTag = await this.storage.Confirm(StateName, this.eTag, this.metadata.ToString(), stableversion.ToString()); return(true); }
/// <summary> /// Undo writes to restore state to pre transaction value. /// </summary> private void Rollback(long transactionId) { List <KeyValuePair <long, LogRecord <TState> > > records = this.log.SkipWhile(kvp => kvp.Key < transactionId).ToList(); foreach (KeyValuePair <long, LogRecord <TState> > kvp in records) { this.log.Remove(kvp.Key); this.transactionCopy.Remove(kvp.Key); } if (this.log.Count > 0) { LogRecord <TState> lastLogRecord = this.log.Values.Last(); this.version = lastLogRecord.Version; this.value = lastLogRecord.NewVal; } else { this.version = TransactionalResourceVersion.Create(0, 0); this.value = new TState(); } }
private async Task DoRecovery() { // load inital state TransactionalStorageLoadResponse <TState> loadResponse = await this.storage.Load(StateName); this.eTag = loadResponse.ETag; this.metadata = Metadata.FromString(loadResponse.Metadata); this.highestReadTransactionId = this.metadata.HighestReadTransactionId; this.version = this.metadata.HighestVersion; this.value = loadResponse.CommittedState; this.log.Clear(); foreach (PendingTransactionState <TState> pendingState in loadResponse.PendingStates) { this.log[pendingState.SequenceId] = new LogRecord <TState> { NewVal = pendingState.State, Version = (TransactionalResourceVersion.TryParse(pendingState.TransactionId, out TransactionalResourceVersion version)) ? version : default(TransactionalResourceVersion) }; } // Rollback any known aborted transactions Restore(); }