public async Task <TransactionalStorageLoadResponse <TState> > Load() { var readMetadataTask = ReadMetadata().ConfigureAwait(false); var readStatesTask = ReadStates().ConfigureAwait(false); _metadata = await readMetadataTask; _states = (await readStatesTask).OrderBy(x => x.SequenceId).ToList(); if (string.IsNullOrEmpty(_metadata.ETag)) { return(new TransactionalStorageLoadResponse <TState>()); } TState committedState; if (_metadata.CommittedSequenceId == 0) { committedState = new TState(); } else { if (!FindState(_metadata.CommittedSequenceId, out var pos)) { var error = $"Storage state corrupted: no record for committed state v{_metadata.CommittedSequenceId}"; throw new InvalidOperationException(error); } committedState = _states[pos].Value; } var prepareRecordsToRecover = _states.Where(x => x.SequenceId > _metadata.CommittedSequenceId) .TakeWhile(x => x.TransactionManager.HasValue) .Select(x => new PendingTransactionState <TState> { SequenceId = x.SequenceId, TransactionManager = x.TransactionManager.Value, State = x.Value, TimeStamp = x.Timestamp.UtcDateTime, TransactionId = x.TransactionId }) .ToArray(); // clear the state value... no longer needed, ok to GC now foreach (var state in _states) { state.ClearValue(); } var metadata = _metadata.Value; return(new TransactionalStorageLoadResponse <TState>(_metadata.ETag, committedState, _metadata.CommittedSequenceId, metadata, prepareRecordsToRecover)); }
public async Task <string> Store(string expectedETag, TransactionalStateMetaData metadata, List <PendingTransactionState <TState> > statesToPrepare, long?commitUpTo, long?abortAfter) { if (abortAfter.HasValue && _states.Any()) { while (_states.Count > 0 && _states[_states.Count - 1].SequenceId > abortAfter) { var entity = _states[_states.Count - 1]; await RemoveAbortedState(entity).ConfigureAwait(false); _states.RemoveAt(_states.Count - 1); } } if (statesToPrepare != null) { foreach (var s in statesToPrepare) { ITransactionStateEntity <TState> existingState = null; if (FindState(s.SequenceId, out var pos)) { existingState = _states[pos]; } var persistedState = await PersistState(s, commitUpTo, existingState).ConfigureAwait(false); if (existingState == null) { _states.Insert(pos, persistedState); } else { _states[pos] = persistedState; } } } Metadata = await PersistMetadata(metadata, commitUpTo ?? Metadata.CommittedSequenceId) .ConfigureAwait(false); await StoreFinalize(commitUpTo).ConfigureAwait(false); return(Metadata.ETag); }
public async Task <string> Store(string expectedETag, TransactionalStateMetaData metadata, List <PendingTransactionState <TState> > statesToPrepare, long?commitUpTo, long?abortAfter) { if (_metadata.ETag != expectedETag) { throw new ArgumentException(nameof(expectedETag), "Etag does not match"); } var stateUpdater = new StateUpdater(_dbExecuter, _stateId, _options.StateTableName, _jsonSettings); // first, clean up aborted records if (abortAfter.HasValue && _states.Count != 0) { await stateUpdater.DeleteStates(afterSequenceId : abortAfter); while (_states.Count > 0 && _states[_states.Count - 1].SequenceId > abortAfter) { _states.RemoveAt(_states.Count - 1); } } // second, persist non-obsolete prepare records var obsoleteBefore = commitUpTo.HasValue ? commitUpTo.Value : _metadata.CommittedSequenceId; if (statesToPrepare != null && statesToPrepare.Count > 0) { foreach (var s in statesToPrepare) { if (s.SequenceId >= obsoleteBefore) { var newState = new TransactionStateEntity { SequenceId = s.SequenceId, TransactionId = s.TransactionId, Timestamp = s.TimeStamp, TransactionManager = s.TransactionManager, Value = s.State }; if (FindState(s.SequenceId, out var pos)) { // overwrite with new pending state _states[pos] = newState; await stateUpdater.UpdateState(newState).ConfigureAwait(false); } else { await stateUpdater.InsertState(newState).ConfigureAwait(false); _states.Insert(pos, newState); } _logger.LogTrace($"{_stateId}.{newState.SequenceId} Update {newState.TransactionId}"); } } } await stateUpdater.EnsureInsertBufferFlushed(); // third, persist metadata and commit position _metadata = await PersistMetadata(metadata, commitUpTo ?? _metadata.CommittedSequenceId); // fourth, remove obsolete records if (_states.Count > 0 && _states[0].SequenceId < obsoleteBefore) { FindState(obsoleteBefore, out var pos); await stateUpdater.DeleteStates(beforeSequenceId : obsoleteBefore); _states.RemoveRange(0, pos); } return(_metadata.ETag); }