/// <summary> /// Invoked by a Cache instance on updating, using properties from the PollNode such as connection strings, etc. /// </summary> /// <typeparam name="T">Type of item in the cache</typeparam> /// <param name="description">Description of the operation, used purely for profiling</param> /// <param name="getData">The operation used to actually get data, e.g. <code>using (var conn = GetConnectionAsync()) { return getFromConnection(conn); }</code></param> /// <param name="logExceptions">Whether to log any exceptions to the log</param> /// <param name="addExceptionData">Optionally add exception data, e.g. <code>e => e.AddLoggedData("Server", Name)</code></param> /// <returns>A cache update action, used when creating a <see cref="Cache"/>.</returns> protected Action <Cache <T> > UpdateCacheItem <T>(string description, Func <Task <T> > getData, bool logExceptions = false, // TODO: Settings Action <Exception> addExceptionData = null) where T : class { return(async cache => { if (OpserverProfileProvider.EnablePollerProfiling) { cache.Profiler = OpserverProfileProvider.CreateContextProfiler("Poll: " + description, cache.UniqueId); } using (MiniProfiler.Current.Step(description)) { CacheItemFetching?.Invoke(this, EventArgs.Empty); try { using (MiniProfiler.Current.Step("Data Fetch")) { cache.Data = await getData(); } cache.LastSuccess = cache.LastPoll = DateTime.UtcNow; cache.ErrorMessage = ""; PollFailsInaRow = 0; } catch (Exception e) { var deserializationException = e as DeserializationException; if (deserializationException != null) { e.AddLoggedData("Snippet-After", deserializationException.SnippetAfterError) .AddLoggedData("Position", deserializationException.Position.ToString()) .AddLoggedData("Ended-Unexpectedly", deserializationException.EndedUnexpectedly.ToString()); } if (logExceptions) { addExceptionData?.Invoke(e); Current.LogException(e); } cache.LastPoll = DateTime.UtcNow; PollFailsInaRow++; cache.ErrorMessage = "Unable to fetch from " + NodeType + ": " + e.Message; #if DEBUG cache.ErrorMessage += " @ " + e.StackTrace; #endif if (e.InnerException != null) { cache.ErrorMessage += "\n" + e.InnerException.Message; } } CacheItemFetched?.Invoke(this, EventArgs.Empty); CachedMonitorStatus = null; } if (OpserverProfileProvider.EnablePollerProfiling) { OpserverProfileProvider.StopContextProfiler(); } }); }
/// <summary> /// Invoked by a Cache instance on updating, using properties from the PollNode such as connection strings, etc. /// </summary> /// <typeparam name="T">Type of item in the cache</typeparam> /// <param name="description">Description of the operation, used purely for profiling</param> /// <param name="getData">The operation used to actually get data, e.g. <code>using (var conn = GetConnectionAsync()) { return getFromConnection(conn); }</code></param> /// <param name="logExceptions">Whether to log any exceptions to the log</param> /// <param name="addExceptionData">Optionally add exception data, e.g. <code>e => e.AddLoggedData("Server", Name)</code></param> /// <param name="timeoutMs">The timeout in milliseconds for this poll to complete before aborting.</param> /// <param name="afterPoll">An optional action to run after polling has completed successfully</param> /// <returns>A cache update action, used when creating a <see cref="Cache"/>.</returns> protected Func <Cache <T>, Task> UpdateCacheItem <T>(string description, Func <Task <T> > getData, bool logExceptions = false, // TODO: Settings Action <Exception> addExceptionData = null, int?timeoutMs = null, Action <Cache <T> > afterPoll = null) where T : class { return(async cache => { cache.PollStatus = "UpdateCacheItem"; if (OpserverProfileProvider.EnablePollerProfiling) { cache.Profiler = OpserverProfileProvider.CreateContextProfiler("Poll: " + description, cache.UniqueId, store: false); } using (MiniProfiler.Current.Step(description)) { CacheItemFetching?.Invoke(this, EventArgs.Empty); try { cache.PollStatus = "Fetching"; using (MiniProfiler.Current.Step("Data Fetch")) { var fetch = getData(); if (timeoutMs.HasValue) { if (await Task.WhenAny(fetch, Task.Delay(timeoutMs.Value)) == fetch) { // Re-await for throws. cache.SetData(await fetch.ConfigureAwait(false)); } else { throw new TimeoutException($"Fetch timed out after {timeoutMs.ToString()} ms."); } } else { cache.SetData(await fetch.ConfigureAwait(false)); } } cache.PollStatus = "Fetch Complete"; cache.SetSuccess(); PollFailsInaRow = 0; afterPoll?.Invoke(cache); } catch (Exception e) { if (logExceptions) { addExceptionData?.Invoke(e); Current.LogException(e); } PollFailsInaRow++; var errorMessage = "Unable to fetch from " + NodeType + ": " + e.Message; #if DEBUG errorMessage += " @ " + e.StackTrace; #endif if (e.InnerException != null) { errorMessage += "\n" + e.InnerException.Message; } cache.PollStatus = "Fetch Failed"; cache.SetFail(errorMessage); } CacheItemFetched?.Invoke(this, EventArgs.Empty); CachedMonitorStatus = null; LastFetch = cache; } if (OpserverProfileProvider.EnablePollerProfiling) { OpserverProfileProvider.StopContextProfiler(); } cache.PollStatus = "UpdateCacheItem Complete"; }); }