// Temp: all async when views can be in MVC Core public static LightweightCache <T> Get(PollNode owner, string key, Func <T> getData, TimeSpan duration, TimeSpan staleDuration) { using (MiniProfiler.Current.Step("LightweightCache: " + key)) { // Let GetSet handle the overlap and locking, for now. That way it's store dependent. return(owner.MemCache.GetSet <LightweightCache <T> >(key, (_, __) => { var tc = new LightweightCache <T>() { Key = key }; try { tc.Data = getData(); } catch (Exception e) { tc.Error = e; e.Log(); } tc.LastFetch = DateTime.UtcNow; return tc; }, duration, staleDuration)); } }
public NodeData(PollNode node, bool includeData = false) { Name = node.UniqueKey; Type = node.NodeType; LastPolled = node.LastPoll; LastPollDurationMS = node.LastPollDuration.TotalMilliseconds; Caches = node.DataPollers.Select(c => new CacheData(c, includeData)); }
public bool TryRemove(PollNode node) { if (node == null || !node.AddedToGlobalPollers) { return(false); } lock (_addLock) { return(AllPollNodes.Remove(node)); } }
/// <summary> /// Adds a node to the global polling list ONLY IF IT IS NEW /// If a node with the same unique key was already added, it will not be added again /// </summary> /// <param name="node">The node to add to the global polling list</param> /// <returns>Whether the node was added</returns> public bool TryAdd(PollNode node) { lock (_addLock) { var success = AllPollNodes.Add(node); if (success) { if (node is IIssuesProvider iProvider) { IssueProviders.Add(iProvider); } if (node is INodeRoleProvider nrProvider) { NodeRoleProviders.Add(nrProvider); } } return(success); } }
public static NodeData GetNode(PollNode node, bool includeData = false) => new NodeData(node, includeData);
/// <summary> /// Creates a cache poller /// </summary> /// <typeparam name="T">Type of item in the cache</typeparam> /// <param name="owner">The PollNode owner of this Cache</param> /// <param name="description">Description of the operation, used purely for profiling</param> /// <param name="cacheDuration">The length of time to cache data for</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="timeoutMs">The timeout in milliseconds for this poll to complete before aborting.</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="afterPoll">An optional action to run after polling has completed successfully</param> /// <param name="memberName"></param> /// <param name="sourceFilePath"></param> /// <param name="sourceLineNumber"></param> /// <returns>A cache update action, used when creating a <see cref="Cache"/>.</returns> public Cache(PollNode owner, string description, TimeSpan cacheDuration, Func <Task <T> > getData, int?timeoutMs = null, bool?logExceptions = null, Action <Exception> addExceptionData = null, Action <Cache <T> > afterPoll = null, [CallerMemberName] string memberName = "", [CallerFilePath] string sourceFilePath = "", [CallerLineNumber] int sourceLineNumber = 0) : base(owner, cacheDuration, memberName, sourceFilePath, sourceLineNumber) { MiniProfilerDescription = "Poll: " + description; // concatenate once // TODO: Settings via owner logExceptions ??= LogExceptions; _updateFunc = async() => { var success = true; PollStatus = "UpdateCacheItem"; if (EnableProfiling) { Profiler = _profilerOptions.StartProfiler(MiniProfilerDescription); Profiler.Id = UniqueId; } using (MiniProfiler.Current.Step(description)) { try { PollStatus = "Fetching"; using (MiniProfiler.Current.Step("Data Fetch")) { var task = getData(); if (timeoutMs.HasValue) { if (await Task.WhenAny(task, Task.Delay(timeoutMs.Value)) == task) { // Re-await for throws. Data = await task; } else { // This means the .WhenAny returned the timeout first...boom. throw new TimeoutException($"Fetch timed out after {timeoutMs} ms."); } } else { Data = await task; } } PollStatus = "Fetch Complete"; SetSuccess(); afterPoll?.Invoke(this); } catch (Exception e) { success = false; if (logExceptions.Value) { addExceptionData?.Invoke(e); e.Log(); } var errorMessage = StringBuilderCache.Get() .Append("Unable to fetch from ") .Append(owner.NodeType) .Append(": ") .Append(e.Message); #if DEBUG errorMessage.Append(" @ ").Append(e.StackTrace); #endif if (e.InnerException != null) { errorMessage.AppendLine().Append(e.InnerException.Message); } PollStatus = "Fetch Failed"; SetFail(e, errorMessage.ToStringRecycle()); } owner.PollComplete(this, success); } if (EnableProfiling) { Profiler.Stop(); } PollStatus = "UpdateCacheItem Complete"; return(Data); }; }