public async Task <ActionResponseBag> ParseStreamAsync(Stream stream) { string text = null; ActionResponseBag actions = new ActionResponseBag(); try { using (StreamReader streamReader = new StreamReader(stream)) { text = await streamReader.ReadToEndAsync().ConfigureAwait(false); } } catch { return(actions); } if (!string.IsNullOrEmpty(text)) { try { actions = JsonConvert.DeserializeObject <ActionResponseBag>(text); } catch (Exception ex) { throw new TargetedNotificationsException(ex.Message, ex); } } return(actions); }
public override Task <GroupedRemoteSettings> Start() { Interlocked.Exchange(ref startTask, Task.Run(async delegate { GroupedRemoteSettings result = null; queryIteration++; ActionResponseBag actionResponseBag = await GetTargetedNotificationActionsAsync().ConfigureAwait(false); if (actionResponseBag != null) { ProcessActionResponseBag(actionResponseBag); result = await ProcessRemoteSettingsFromTargetedNotificationsAsync().ConfigureAwait(false); } StartAgainAfter(serviceQueryLoopTimeSpan); return(result); })); return(startTask); }
protected override async Task <ActionResponseBag> GetTargetedNotificationActionsAsync() { IEnumerable <Task <IEnumerable <ActionResponse> > > tasks = directories.SelectMany((IDirectoryReader d) => d.ReadAllFiles()).Select(async delegate(DirectoryReaderContext x) { try { return(await localTestParser.ParseStreamAsync(x)); } catch (TargetedNotificationsException exception) { logger.LogError("Error parsing test file: " + x.DirectoryName + "-" + x.FileName, exception); } return(Enumerable.Empty <ActionResponse>()); }); ActionResponseBag actionResponseBag = new ActionResponseBag(); ActionResponseBag actionResponseBag2 = actionResponseBag; actionResponseBag2.Actions = (await Task.WhenAll(tasks)).SelectMany((IEnumerable <ActionResponse> x) => x); return(actionResponseBag); }
protected override async Task <ActionResponseBag> GetTargetedNotificationActionsAsync() { IEnumerable <string> previouslyCachedRuleIds = useCache ? notificationAndCourtesyCache.GetAllCachedRuleIds(cacheTimeoutMs) : Enumerable.Empty <string>(); Stream stream = await SendTargetedNotificationsRequestAsync(previouslyCachedRuleIds).ConfigureAwait(false); if (stream != null) { try { ActionResponseBag actionResponseBag = await notificationsParser.ParseStreamAsync(stream).ConfigureAwait(false); if (useCache) { notificationAndCourtesyCache.MergeNewResponse(actionResponseBag, previouslyCachedRuleIds, cacheTimeoutMs); } return(actionResponseBag); } catch (TargetedNotificationsException exception) { string eventName = "VS/Core/TargetedNotifications/ApiResponseParseFailure"; Dictionary <string, object> additionalProperties = new Dictionary <string, object> { { "VS.Core.TargetedNotifications.ApiResponseMs", apiTimer?.ElapsedMilliseconds }, { "VS.Core.TargetedNotifications.Iteration", queryIteration } }; targetedNotificationsTelemetry.PostCriticalFault(eventName, "Failed to parse TN API response", exception, additionalProperties); } } return(null); }
private void ProcessActionResponseBag(ActionResponseBag response) { logger.LogInfo($"Received {response.Actions.Count()} actions from {Name}"); List <string> list; try { actionsAndCategoriesLock.Wait(); list = tnActions.Values.SelectMany((Dictionary <string, ActionResponse> x) => x.Keys).ToList(); foreach (ActionResponse action in response.Actions) { if (!tnActions.TryGetValue(action.ActionPath, out Dictionary <string, ActionResponse> value)) { value = new Dictionary <string, ActionResponse>(StringComparer.OrdinalIgnoreCase); tnActions[action.ActionPath] = value; } if (action.RuleId != null) { value[action.RuleId] = action; } else { logger.LogInfo("Skipping action that has no RuleId " + action.ActionPath + " from " + Name); } } foreach (ActionCategory category in response.Categories) { ActionCategories[category.CategoryId] = category; } } finally { actionsAndCategoriesLock.Release(); } lock (subscriptionLockObject) { try { foreach (string key in tnSubscriptionCallbacks.Keys) { foreach (Type key2 in tnSubscriptionCallbacks[key].Keys) { MethodInfo methodInfo = typeof(TargetedNotificationsProviderBase).GetMethod("SubscribeActionsInternal", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(key2); foreach (object item in tnSubscriptionCallbacks[key][key2]) { methodInfo.Invoke(this, new object[3] { key, item, list }); } } } } catch (Exception exception) { string eventName = "VS/Core/TargetedNotifications/ReSubscribeFailure"; targetedNotificationsTelemetry.PostCriticalFault(eventName, "failure", exception); } } string eventName2 = "VS/Core/TargetedNotifications/ActionsReceived"; IEnumerable <ActionResponse> source = response.Actions ?? Enumerable.Empty <ActionResponse>(); Dictionary <string, IEnumerable <string> > val = (from x in source group x by x.ActionPath).ToDictionary((IGrouping <string, ActionResponse> x) => x.Key, (IGrouping <string, ActionResponse> x) => x.Select((ActionResponse y) => y.RuleId)); List <string> list2 = ActionCategories.Keys.ToList(); Dictionary <string, object> additionalProperties = new Dictionary <string, object> { { "VS.Core.TargetedNotifications.ProviderName", Name }, { "VS.Core.TargetedNotifications.Actions", new TelemetryComplexProperty(val) }, { "VS.Core.TargetedNotifications.ActionCount", source.Count() }, { "VS.Core.TargetedNotifications.Categories", new TelemetryComplexProperty(list2) }, { "VS.Core.TargetedNotifications.CategoryCount", list2.Count }, { "VS.Core.TargetedNotifications.ApiResponseMs", apiTimer?.ElapsedMilliseconds }, { "VS.Core.TargetedNotifications.Iteration", queryIteration } }; targetedNotificationsTelemetry.PostSuccessfulOperation(eventName2, additionalProperties); }
/// <summary> /// Called when a new ActionResponseBag is received from the /// service. This merges that new data into the existing cache /// </summary> /// <param name="newResponse">An ActionResponseBag received from the Azure API</param> /// <param name="previouslyCachedRuleIds">Set of rule IDs that were previously read from the cache and should not be written back</param> /// <param name="timeoutMs">Maximum time to wait, in milliseconds, for the cache lock. Leave null for infinite.</param> public void MergeNewResponse(ActionResponseBag newResponse, IEnumerable <string> previouslyCachedRuleIds, int?timeoutMs = null) { if (Storage.Lock(timeoutMs)) { Stopwatch stopwatch = Stopwatch.StartNew(); try { DateTime utcNow = DateTime.UtcNow; bool flag = false; CachedTargetedNotifications localCacheCopy = Storage.GetLocalCacheCopy(); List <string> list = new List <string>(); List <string> list2 = new List <string>(); foreach (ActionResponse action in newResponse.Actions) { if (action.SendAlways) { if (localCacheCopy.Actions.Remove(action.RuleId)) { flag = true; } } else if (!previouslyCachedRuleIds.Contains(action.RuleId)) { localCacheCopy.Actions[action.RuleId] = new CachedActionResponseTime { CachedTime = (localCacheCopy.Actions.ContainsKey(action.RuleId) ? localCacheCopy.Actions[action.RuleId].CachedTime : utcNow), MaxWaitTimeSpan = ((action.MaxWaitTimeSpan == null) ? defaultMaxWaitTimeSpan : TimeSpan.Parse(action.MaxWaitTimeSpan)) }; list.Add(action.RuleId); flag = true; } } string[] array = localCacheCopy.Actions.Keys.ToArray(); foreach (string text in array) { CachedActionResponseTime cachedActionResponseTime = localCacheCopy.Actions[text]; if (utcNow >= cachedActionResponseTime.CachedTime.Add(cachedActionResponseTime.MaxWaitTimeSpan)) { localCacheCopy.Actions.Remove(text); list2.Add(text); flag = true; } } foreach (ActionCategory category in newResponse.Categories) { if (localCacheCopy.Categories.ContainsKey(category.CategoryId)) { TimeSpan timeSpan = TimeSpan.Parse(category.WaitTimeSpan); if (localCacheCopy.Categories[category.CategoryId].WaitTimeSpan != timeSpan) { localCacheCopy.Categories[category.CategoryId].WaitTimeSpan = timeSpan; flag = true; } } } array = localCacheCopy.Categories.Keys.ToArray(); foreach (string key in array) { CachedActionCategoryTime cachedActionCategoryTime = localCacheCopy.Categories[key]; if (utcNow >= cachedActionCategoryTime.LastSent.Add(cachedActionCategoryTime.WaitTimeSpan)) { localCacheCopy.Categories.Remove(key); flag = true; } } if (flag) { Storage.SetLocalCache(localCacheCopy); } if (list.Count > 0) { responseUsesCachedRules = true; } stopwatch.Stop(); if (list.Count > 0 || list2.Count > 0 || localCacheCopy.Actions.Count > 0 || localCacheCopy.Categories.Count > 0) { string eventName = "VS/Core/TargetedNotifications/CacheMerged"; Dictionary <string, object> additionalProperties = new Dictionary <string, object> { { "VS.Core.TargetedNotifications.AddedActions", new TelemetryComplexProperty(list) }, { "VS.Core.TargetedNotifications.AddedActionsCount", list.Count }, { "VS.Core.TargetedNotifications.ExpiredActions", new TelemetryComplexProperty(list2) }, { "VS.Core.TargetedNotifications.ExpiredActionsCount", list2.Count }, { "VS.Core.TargetedNotifications.CachedActions", new TelemetryComplexProperty(localCacheCopy.Actions.Keys.ToList()) }, { "VS.Core.TargetedNotifications.CachedActionsCount", localCacheCopy.Actions.Count }, { "VS.Core.TargetedNotifications.CachedCategories", new TelemetryComplexProperty(localCacheCopy.Categories.Keys.ToList()) }, { "VS.Core.TargetedNotifications.CachedCategoriesCount", localCacheCopy.Categories.Count }, { "VS.Core.TargetedNotifications.DurationMs", stopwatch.ElapsedMilliseconds } }; telemetry.PostSuccessfulOperation(eventName, additionalProperties); } } catch (Exception exception) { stopwatch.Stop(); string eventName2 = "VS/Core/TargetedNotifications/CacheMergeFailure"; string[] array2 = (from a in newResponse.Actions where !a.SendAlways select a.RuleId).Except(previouslyCachedRuleIds).ToArray(); Dictionary <string, object> additionalProperties2 = new Dictionary <string, object> { { "VS.Core.TargetedNotifications.SendOnceActions", new TelemetryComplexProperty(array2) }, { "VS.Core.TargetedNotifications.SendOnceActionsCount", array2.Length }, { "VS.Core.TargetedNotifications.DurationMs", stopwatch.ElapsedMilliseconds } }; telemetry.PostCriticalFault(eventName2, "Failed to merge new response with cache", exception, additionalProperties2); } finally { Storage.Unlock(); } } else { string eventName3 = "VS/Core/TargetedNotifications/CacheLockTimeout"; Dictionary <string, object> additionalProperties3 = new Dictionary <string, object> { { "VS.Core.TargetedNotifications.Operation", "MergeNewResponse" }, { "VS.Core.TargetedNotifications.TimeoutMs", timeoutMs } }; telemetry.PostDiagnosticFault(eventName3, "Timeout acquiring cache lock", null, additionalProperties3); } }