private void UpdateScannerState() { DateTime now = Time.UtcNow; if (_scanTraits.Type == ScanType.Official) { if (now < _scanTraits.EventStart) { _scannerState = MatchScannerState.OfficialBaseline; } else if (now < _scanTraits.EventEnd) { _scannerState = MatchScannerState.OfficialInProgress; } else { _scannerState = MatchScannerState.OfficialFinishing; } } }
private async Task RunScannerAsync() { Log.TraceInformation("Starting up the scanner loop"); bool scanIsTotallyCompleted = false; if (_scanTraits.Type == ScanType.Manual) { _scannerState = MatchScannerState.ManualScan; } else if (_scanTraits.Type == ScanType.TestPass) { _scannerState = MatchScannerState.TestScan; } try { TimeSpan? tilNextUpdate = TimeSpan.Zero; while (tilNextUpdate.HasValue) { AbortIfTestScanRunsLong(); UpdateScannerState(); // Sleep until there's more work to do or until the scan is canceled. Log.TraceInformation("Sleeping for {0}", tilNextUpdate); await UpdateAppState("Sleeping for " + tilNextUpdate.ToString()).ConfigureAwait(false); if (_cancelScanEvent.WaitOne(tilNextUpdate.Value)) { Log.TraceInformation("Breaking out of scan loop because the event was signaled"); break; } await _participationHandle.HeartbeatAsync().ConfigureAwait(false); // Update the next partition. The return value tells us whether or not there's // any more work to do. var refreshResult = await RefreshNextPartitionAsync().ConfigureAwait(false); tilNextUpdate = refreshResult.TimeTilNextUpdate; if (!tilNextUpdate.HasValue) { Log.TraceInformation("Breaking out of scan loop because a next update time was not returned."); break; } if (tilNextUpdate > _scannerHeartbeatPeriod) { tilNextUpdate = _scannerHeartbeatPeriod; } TimeSpan timeTilEnd = _scanTraits.EventEnd - Time.UtcNow; if ((tilNextUpdate > timeTilEnd) && (timeTilEnd > TimeSpan.Zero)) { tilNextUpdate = _scanTraits.EventEnd - Time.UtcNow; } else if (timeTilEnd <= TimeSpan.Zero) { tilNextUpdate = TimeSpan.Zero; } } // We broke out of the loop. Determine the reason and report the result. scanIsTotallyCompleted = await WrapUpScanAsync().ConfigureAwait(false); } catch(Exception Ex) { // Make a best effort to report the exception data. Log.TraceEvent(TraceEventType.Critical, 0, "Scanner for event {0} encountered an exception and will terminate: {1}", this.Traits.EventId, Ex.ToString()); string data = null; if (Ex is System.AggregateException) { if (Ex.InnerException is System.Data.Entity.Validation.DbEntityValidationException) { StringBuilder sb = new StringBuilder(); var dbException = Ex.InnerException as System.Data.Entity.Validation.DbEntityValidationException; foreach (var entry in dbException.EntityValidationErrors) { sb.AppendFormat("Entity Validation error: {0}", entry.Entry.ToString()); foreach (var subentry in entry.ValidationErrors) { sb.AppendFormat("Db validation error for property {0}: {1}", subentry.PropertyName, subentry.ErrorMessage); } } data = sb.ToString(); Log.TraceEvent(TraceEventType.Critical, 0, "Validation errors: {0}", sb.ToString()); } } _participationHandle.AddInfoAsync(InfoLevel.Error, Ex.ToString(), data).Wait(); _participationHandle.LeaveAsync(ParticipantState.Faulted).Wait(); throw; } finally { // At this point, we've exited the scan loop. It was either because the scan is done // or because we were canceled. We may have already called out to our subscribers, but // if we didn't, make sure it happens now. if (!_scanEnded) { Log.TraceInformation("Notifying subscribers that scan has ended, totallyComplete={0}, _canceled={1}", scanIsTotallyCompleted, _cancelScan); _monitor.EndScan(this, scanIsTotallyCompleted, !_cancelScan); } if (OnWorkerFinished != null) { // This is intentionally run asynchronously and not awaited. Task.Run(() => { OnWorkerFinished(scanIsTotallyCompleted); }); } } }