예제 #1
0
        public async Task <long> CreateOrRestoreAsync(IPartitionErrorHandler errorHandler, long firstInputQueuePosition)
        {
            EventTraceContext.Clear();

            this.ErrorHandler = errorHandler;
            this.TraceHelper.TraceProgress("Starting partition");

            await MaxConcurrentStarts.WaitAsync();

            // create or restore partition state from last snapshot
            try
            {
                // create the state
                this.State = ((IStorageProvider)this.host).CreatePartitionState();

                // initialize timer for this partition
                this.PendingTimers = new BatchTimer <PartitionEvent>(this.ErrorHandler.Token, this.TimersFired);

                // goes to storage to create or restore the partition state
                this.TraceHelper.TraceProgress("Loading partition state");
                var inputQueuePosition = await this.State.CreateOrRestoreAsync(this, this.ErrorHandler, firstInputQueuePosition).ConfigureAwait(false);

                // start processing the timers
                this.PendingTimers.Start($"Timer{this.PartitionId:D2}");

                // start processing the worker queues
                this.State.StartProcessing();

                this.TraceHelper.TraceProgress($"Started partition, nextInputQueuePosition={inputQueuePosition}");
                return(inputQueuePosition);
            }
            catch (Exception e) when(!Utils.IsFatal(e))
            {
                this.ErrorHandler.HandleError(nameof(CreateOrRestoreAsync), "Could not start partition", e, true, false);
                throw;
            }
            finally
            {
                MaxConcurrentStarts.Release();
            }
        }
        async Task Work()
        {
            this.Tracer?.Invoke("started");

            EventTraceContext.Clear();
            int?previousBatch = null;

            while (!this.cancellationToken.IsCancellationRequested)
            {
                int?nextBatch = this.GetNextBatch();

                if (previousBatch.HasValue)
                {
                    this.WorkLoopCompleted(previousBatch.Value, this.stopwatch.Elapsed.TotalMilliseconds, nextBatch);
                }

                if (!nextBatch.HasValue)
                {
                    this.Tracer?.Invoke("no work, starting to shut down");

                    // no work found. Announce that we are planning to shut down.
                    // Using an interlocked exchange to enforce store-load fence.
                    int read = Interlocked.Exchange(ref this.state, SHUTTINGDOWN);

                    Debug.Assert(read == RUNNING); // because that's what its set to before Work is called

                    // but recheck so we don't miss work that was just added
                    nextBatch = this.GetNextBatch();

                    if (!nextBatch.HasValue)
                    {
                        // still no work. Try to transition to idle but revert if state has been changed.
                        read = Interlocked.CompareExchange(ref this.state, IDLE, SHUTTINGDOWN);

                        if (read == SHUTTINGDOWN)
                        {
                            // shut down is complete
                            this.Tracer?.Invoke("shut down");
                            return;
                        }
                        else
                        {
                            // shutdown was reverted by Notify()
                            Debug.Assert(read == RUNNING);
                            this.Tracer?.Invoke("was told to continue");
                            continue;
                        }
                    }
                    else
                    {
                        // we found more work. So we do not shutdown but go back to running.
                        this.Tracer?.Invoke("found more work after all");
                        this.state = RUNNING;
                    }
                }

                this.Tracer?.Invoke("processing batch");
                this.stopwatch.Restart();

                try
                {
                    // recheck for cancellation right before doing work
                    this.cancellationToken.ThrowIfCancellationRequested();

                    // do the work, calling the virtual method
                    await this.Process(this.batch).ConfigureAwait(false);

                    this.Tracer?.Invoke("notifying waiters");

                    // notify anyone who is waiting for completion
                    foreach (var w in this.waiters)
                    {
                        w.TrySetResult(true);
                    }
                }
                catch (Exception e)
                {
                    foreach (var w in this.waiters)
                    {
                        w.TrySetException(e);
                    }
                }

                this.Tracer?.Invoke("done processing batch");
                this.stopwatch.Stop();
                previousBatch = this.batch.Count;
            }
        }