/// <summary> /// Sends data to observers for a given topic. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="useObserverAsProxy">Observable uses one observer for each cluster node as a proxy when true, /// it directly sends the message to all observers otherwise.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task NotifyObserversAsync(string topic, Message message, bool useObserverAsProxy) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; if (string.IsNullOrWhiteSpace(message?.Body)) { throw new ArgumentException($"The {nameof(message)} parameter cannot be null.", nameof(message)); } try { List <Task> taskList = new List <Task>(); if (useObserverAsProxy) { if (JsonSerializerHelper.IsJson(message.Body)) { // Create the list of observers: an observer is added to the list only at least of of its // filter predicates is satisified by the message. JObject jObject = JsonSerializerHelper.Deserialize(message.Body); IEnumerable <EntityId> observerEnumerable = (from subscriptionInfo in observerDictionary. Where(kvp => kvp.Value.Predicates.Any()). Select(observer => observer.Value) let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject)) where ok select subscriptionInfo.EntityId); // observers are grouped by NodeName taskList.AddRange( observerEnumerable. GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2))); } else { // observers are grouped by NodeName taskList.AddRange( observerDictionary. Select(kvp => kvp.Value.EntityId). GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2))); } } else { if (JsonSerializerHelper.IsJson(message.Body)) { JObject jObject = JsonSerializerHelper.Deserialize(message.Body); taskList.AddRange( (from subscriptionInfo in observerDictionary. Where(kvp => kvp.Value.Predicates.Any()). Select(observer => observer.Value) let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject)) where ok select subscriptionInfo.EntityId). Select( entityId => ProcessingHelper.NotifyObserverAsync( topic, message, entityId, id, null))); } else { taskList.AddRange( observerDictionary.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer.Value.EntityId, id, null))); } } await Task.WhenAll(taskList.ToArray()); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } }
/// <summary> /// Unregisters an entity as observable for a given topic. /// This method is called by a management service or actor. /// </summary> /// <param name="topic">The topic.</param> /// <param name="useObserverAsProxy">Observable uses one observer for each cluster node as a proxy when true, /// it directly sends the message to all observers otherwise.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task UnregisterObservableActorAsync(string topic, bool useObserverAsProxy) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; try { for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { IRegistryService registryService = ServiceProxy.Create <IRegistryService>(ConfigurationHelper.RegistryServiceUri, new ServicePartitionKey(PartitionResolver.Resolve(topic, ConfigurationHelper.RegistryServicePartitionCount))); await registryService.UnregisterObservableAsync(topic, id); break; } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } catch (AggregateException ex) { foreach (Exception innerException in ex.InnerExceptions) { ActorEventSource.Current.Error(innerException); } if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } catch (Exception ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } List <Task> taskList = new List <Task>(); try { if (useObserverAsProxy) { // observers are grouped by NodeName taskList.AddRange( observerDictionary. Select(kvp => kvp.Value.EntityId). GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.UnregisterObservableAsync(topic, tuple.Item1, id, tuple.Item2))); } else { taskList.AddRange( observerDictionary.Select( observer => ProcessingHelper.UnregisterObservableAsync(topic, observer.Value.EntityId, id, null))); } await Task.WhenAll(taskList.ToArray()); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } await this.StateManager.TryRemoveStateAsync(topic); ActorEventSource.Current.Message($"Observable successfully unregistered.\r\n[Observable]: {id}\r\n[Publication]: Topic=[{topic}]."); } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } }