Esempio n. 1
0
        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);
        }
Esempio n. 3
0
        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);
        }