Beispiel #1
0
            public async ValueTask Add(TableTransactionAction operation)
            {
                if (!BatchHasKey && operation.Entity.RowKey == key.RowKey && operation.Entity.PartitionKey == key.PartitionKey)
                {
                    key      = (KeyEntity)operation.Entity;
                    keyIndex = batchOperation.Count;
                }

                batchOperation.Add(operation);

                if (batchOperation.Count == AzureTableConstants.MaxBatchSize - (BatchHasKey ? 0 : 1))
                {
                    // the key serves as a synchronizer, to prevent modification by multiple grains under edge conditions,
                    // like duplicate activations or deployments.Every batch write needs to include the key,
                    // even if the key values don't change.

                    if (!BatchHasKey)
                    {
                        keyIndex = batchOperation.Count;
                        if (string.IsNullOrEmpty(key.ETag.ToString()))
                        {
                            batchOperation.Add(new TableTransactionAction(TableTransactionActionType.Add, key));
                        }
                        else
                        {
                            batchOperation.Add(new TableTransactionAction(TableTransactionActionType.UpdateReplace, key, key.ETag));
                        }
                    }

                    await Flush().ConfigureAwait(false);
                }
            }
        public async Task <TransactionalStorageLoadResponse <TState> > Load()
        {
            try
            {
                Task <KeyEntity>           keyTask    = ReadKey();
                Task <List <StateEntity> > statesTask = ReadStates();
                this.key = await keyTask.ConfigureAwait(false);

                this.states = await statesTask.ConfigureAwait(false);

                if (string.IsNullOrEmpty(this.key.ETag))
                {
                    return(new TransactionalStorageLoadResponse <TState>());
                }
                TState commitedState = (!string.IsNullOrEmpty(this.key.CommittedTransactionId)) ? FindState(this.key.CommittedTransactionId) : new TState();
                if (commitedState == null)
                {
                    this.logger.LogCritical("Transactional state non-recoverable error.  Commited state for transaction {TransactionId} not found.", this.key.CommittedTransactionId);
                    throw new InvalidOperationException($"Transactional state non-recoverable error.  Commited state for transaction {this.key.CommittedTransactionId} not found.");
                }
                var pendingStates = states.Select(s => new PendingTransactionState <TState>(s.TransactionId, s.SequenceId, s.GetState <TState>(this.jsonSettings))).ToList();
                return(new TransactionalStorageLoadResponse <TState>(this.key.ETag, commitedState, this.key.Metadata, pendingStates));
            } catch (Exception ex)
            {
                this.logger.LogError("Transactional state load failed {Exception}.", ex);
                throw;
            }
        }
Beispiel #3
0
 public BatchOperation(ILogger logger, KeyEntity key, TableClient table)
 {
     this.batchOperation = new();
     this.logger         = logger;
     this.key            = key;
     this.table          = table;
 }
 public BatchOperation(ILogger logger, KeyEntity key, CloudTable table)
 {
     this.batchOperation = new TableBatchOperation();
     this.logger         = logger;
     this.key            = key;
     this.table          = table;
 }
Beispiel #5
0
        public async Task <TransactionalStorageLoadResponse <TState> > Load()
        {
            try
            {
                var keyTask    = ReadKey();
                var statesTask = ReadStates();
                key = await keyTask.ConfigureAwait(false);

                states = await statesTask.ConfigureAwait(false);

                if (string.IsNullOrEmpty(key.ETag.ToString()))
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.LogDebug($"{partition} Loaded v0, fresh");
                    }

                    // first time load
                    return(new TransactionalStorageLoadResponse <TState>());
                }
                else
                {
                    TState committedState;
                    if (this.key.CommittedSequenceId == 0)
                    {
                        committedState = new TState();
                    }
                    else
                    {
                        if (!FindState(this.key.CommittedSequenceId, out var pos))
                        {
                            var error = $"Storage state corrupted: no record for committed state v{this.key.CommittedSequenceId}";
                            logger.LogCritical($"{partition} {error}");
                            throw new InvalidOperationException(error);
                        }
                        committedState = states[pos].Value.GetState <TState>(this.jsonSettings);
                    }

                    var PrepareRecordsToRecover = new List <PendingTransactionState <TState> >();
                    for (int i = 0; i < states.Count; i++)
                    {
                        var kvp = states[i];

                        // pending states for already committed transactions can be ignored
                        if (kvp.Key <= key.CommittedSequenceId)
                        {
                            continue;
                        }

                        // upon recovery, local non-committed transactions are considered aborted
                        if (kvp.Value.TransactionManager == null)
                        {
                            break;
                        }

                        ParticipantId tm = JsonConvert.DeserializeObject <ParticipantId>(kvp.Value.TransactionManager, this.jsonSettings);

                        PrepareRecordsToRecover.Add(new PendingTransactionState <TState>()
                        {
                            SequenceId         = kvp.Key,
                            State              = kvp.Value.GetState <TState>(this.jsonSettings),
                            TimeStamp          = kvp.Value.TransactionTimestamp,
                            TransactionId      = kvp.Value.TransactionId,
                            TransactionManager = tm
                        });
                    }

                    // clear the state strings... no longer needed, ok to GC now
                    for (int i = 0; i < states.Count; i++)
                    {
                        var entity = states[i].Value;
                        entity.StateJson = null;
                    }

                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.LogDebug($"{partition} Loaded v{this.key.CommittedSequenceId} rows={string.Join(",", states.Select(s => s.Key.ToString("x16")))}");
                    }

                    TransactionalStateMetaData metadata = JsonConvert.DeserializeObject <TransactionalStateMetaData>(this.key.Metadata, this.jsonSettings);
                    return(new TransactionalStorageLoadResponse <TState>(this.key.ETag.ToString(), committedState, this.key.CommittedSequenceId, metadata, PrepareRecordsToRecover));
                }
            }
            catch (Exception ex)
            {
                this.logger.LogError("Transactional state load failed {Exception}.", ex);
                throw;
            }
        }