Ejemplo n.º 1
0
 public StorageBatch(StorageBatch <TState> previous)
 {
     MetaData         = previous.MetaData;
     confirmUpTo      = previous.confirmUpTo;
     cancelAbove      = previous.cancelAbove;
     cancelAboveStart = cancelAbove;
 }
Ejemplo n.º 2
0
        /// <summary>
        /// called on activation, and when recovering from storage conflicts or other exceptions.
        /// </summary>
        private async Task Restore()
        {
            // start the load
            var loadtask = this.storage.Load();

            // abort active transactions, without waking up waiters just yet
            AbortExecutingTransactions("due to restore");

            // abort all entries in the commit queue
            foreach (var entry in commitQueue.Elements)
            {
                NotifyOfAbort(entry, problemFlag);
            }
            commitQueue.Clear();

            var loadresponse = await loadtask;

            storageBatch = new StorageBatch <TState>(loadresponse);

            stableState          = loadresponse.CommittedState;
            stableSequenceNumber = loadresponse.CommittedSequenceId;

            if (logger.IsEnabled(LogLevel.Debug))
            {
                logger.Debug($"Load v{stableSequenceNumber} {loadresponse.PendingStates.Count}p {storageBatch.MetaData.CommitRecords.Count}c");
            }

            // ensure clock is consistent with loaded state
            MergeClock(storageBatch.MetaData.TimeStamp);

            // resume prepared transactions (not TM)
            foreach (var pr in loadresponse.PendingStates.OrderBy(ps => ps.TimeStamp))
            {
                if (pr.SequenceId > stableSequenceNumber && pr.TransactionManager != null)
                {
                    if (logger.IsEnabled(LogLevel.Debug))
                    {
                        logger.Debug($"recover two-phase-commit {pr.TransactionId}");
                    }

                    var tm = (pr.TransactionManager == null) ? null :
                             (ITransactionParticipant)JsonConvert.DeserializeObject <ITransactionParticipant>(pr.TransactionManager, MetaData.SerializerSettings);

                    commitQueue.Add(new TransactionRecord <TState>()
                    {
                        Role                        = CommitRole.RemoteCommit,
                        TransactionId               = Guid.Parse(pr.TransactionId),
                        Timestamp                   = pr.TimeStamp,
                        State                       = pr.State,
                        TransactionManager          = tm,
                        PrepareIsPersisted          = true,
                        LastSent                    = default(DateTime),
                        ConfirmationResponsePromise = null
                    });
                }
            }

            // resume committed transactions (on TM)
            foreach (var kvp in storageBatch.MetaData.CommitRecords)
            {
                if (logger.IsEnabled(LogLevel.Debug))
                {
                    logger.Debug($"recover commit confirmation {kvp.Key}");
                }

                confirmationTasks.Add(kvp.Key, new TransactionRecord <TState>()
                {
                    Role              = CommitRole.LocalCommit,
                    TransactionId     = kvp.Key,
                    Timestamp         = kvp.Value.Timestamp,
                    WriteParticipants = kvp.Value.WriteParticipants
                });
            }

            // clear the problem flag
            problemFlag = TransactionalStatus.Ok;

            // check for work
            confirmationWorker.Notify();
            storageWorker.Notify();
            lockWorker.Notify();
        }
Ejemplo n.º 3
0
        private async Task StorageWork()
        {
            try
            {
                if (problemFlag != TransactionalStatus.Ok)
                {
                    logger.Debug($"restoring state after status={problemFlag}");

                    await Restore();
                }
                else
                {
                    // count committable entries at the bottom of the commit queue
                    int committableEntries = 0;
                    while (committableEntries < commitQueue.Count && commitQueue[committableEntries].ReadyToCommit)
                    {
                        committableEntries++;
                    }

                    // process all committable entries, assembling a storage batch
                    if (committableEntries > 0)
                    {
                        // process all committable entries, adding storage events to the storage batch
                        CollectEventsForBatch(committableEntries);

                        if (logger.IsEnabled(LogLevel.Debug))
                        {
                            var r = commitQueue.Count > committableEntries ? commitQueue[committableEntries].ToString() : "";
                            logger.Debug($"batchcommit={committableEntries} leave={commitQueue.Count - committableEntries} {r}");
                        }
                    }
                    else
                    {
                        // send or re-send messages and detect timeouts
                        CheckProgressOfCommitQueue();
                    }

                    // store the current storage batch, if it is not empty
                    if (storageBatch.BatchSize > 0)
                    {
                        var thisBatch = storageBatch;
                        var nextBatch = new StorageBatch <TState>(thisBatch);

                        // get the next batch in place so it can be filled while we store the old one
                        storageBatch = nextBatch;

                        nextBatch.ETag = await thisBatch.Store(storage);

                        if (committableEntries > 0)
                        {
                            // update stable state
                            var lastCommittedEntry = commitQueue[committableEntries - 1];
                            stableState          = lastCommittedEntry.State;
                            stableSequenceNumber = lastCommittedEntry.SequenceNumber;

                            // remove committed entries from commit queue
                            commitQueue.RemoveFromFront(committableEntries);
                        }

                        // run follow-up actions
                        thisBatch.RunFollowUpActions();

                        storageWorker.Notify(); // re-check for work
                    }
                }
                return;
            }
            catch (InconsistentStateException e)
            {
                logger.Warn(888, $"reload from storage triggered by e-tag mismatch {e}");

                problemFlag = TransactionalStatus.StorageConflict;
            }
            catch (Exception e)
            {
                logger.Warn(888, $"exception in storageWorker", e);

                problemFlag = TransactionalStatus.UnknownException;
            }

            // after exceptions, we try again, but with limits
            if (++failCounter < 10)
            {
                await Task.Delay(100);

                // this restarts the worker, which sees the problem flag and recovers.
                storageWorker.Notify();
            }
            else
            {
                // bail out
                logger.Warn(999, $"storageWorker is bailing out");
            }
        }