public async Task <SubscriptionDescription> Create(string topicPath, string subscriptionName, SubscriptionMetadata metadata, string sqlFilter, INamespaceManager namespaceManager, string forwardTo) { var meta = metadata as ForwardingTopologySubscriptionMetadata; if (meta == null) { throw new InvalidOperationException($"Cannot create subscription `{subscriptionName}` for topic `{topicPath}` without namespace information required."); } var subscriptionDescription = subscriptionDescriptionFactory(topicPath, subscriptionName, settings); subscriptionDescription.ForwardTo = forwardTo; subscriptionDescription.UserMetadata = metadata.Description; try { if (!await ExistsAsync(topicPath, subscriptionName, metadata.Description, namespaceManager).ConfigureAwait(false)) { var ruleDescription = new RuleDescription { Filter = new SqlFilter(sqlFilter), Name = metadata.SubscriptionNameBasedOnEventWithNamespace }; await namespaceManager.CreateSubscription(subscriptionDescription, ruleDescription).ConfigureAwait(false); logger.Info($"Subscription '{subscriptionDescription.UserMetadata}' created as '{subscriptionDescription.Name}' with rule '{ruleDescription.Name}' for event '{meta.SubscribedEventFullName}' in namespace '{namespaceManager.Address.Host}'."); var key = GenerateSubscriptionKey(namespaceManager.Address, subscriptionDescription.TopicPath, subscriptionDescription.Name); await rememberExistence.AddOrUpdate(key, keyNotFound => Task.FromResult(true), (updateTopicPath, previousValue) => Task.FromResult(true)).ConfigureAwait(false); } else { logger.Info($"Subscription '{subscriptionDescription.Name}' aka '{subscriptionDescription.UserMetadata}' already exists, skipping creation."); logger.InfoFormat("Checking if subscription '{0}' in namespace '{1}' needs to be updated.", subscriptionDescription.Name, namespaceManager.Address.Host); var existingSubscriptionDescription = await namespaceManager.GetSubscription(subscriptionDescription.TopicPath, subscriptionDescription.Name).ConfigureAwait(false); if (MembersAreNotEqual(existingSubscriptionDescription, subscriptionDescription)) { logger.Info($"Updating subscription '{subscriptionDescription.Name}' in namespace '{namespaceManager.Address.Host}' with new description."); await namespaceManager.UpdateSubscription(subscriptionDescription).ConfigureAwait(false); } // Rules can't be queried, so try to add var ruleDescription = new RuleDescription { Filter = new SqlFilter(sqlFilter), Name = metadata.SubscriptionNameBasedOnEventWithNamespace }; logger.Info($"Adding subscription rule '{ruleDescription.Name}' for event '{meta.SubscribedEventFullName}' in namespace '{namespaceManager.Address.Host}'."); try { var subscriptionClient = SubscriptionClient.CreateFromConnectionString(meta.NamespaceInfo.ConnectionString, topicPath, subscriptionName); await subscriptionClient.AddRuleAsync(ruleDescription).ConfigureAwait(false); } catch (MessagingEntityAlreadyExistsException exception) { logger.Debug($"Rule '{ruleDescription.Name}' already exists. Response from the server: '{exception.Message}'."); } } } catch (MessagingEntityAlreadyExistsException) { // the subscription already exists or another node beat us to it, which is ok logger.Info($"Subscription '{subscriptionDescription.Name}' in namespace '{namespaceManager.Address.Host}' already exists, another node probably beat us to it."); } catch (TimeoutException) { logger.Info($"Timeout occurred on subscription creation for topic '{subscriptionDescription.TopicPath}' subscription name '{subscriptionDescription.Name}' in namespace '{namespaceManager.Address.Host}' going to validate if it doesn't exist."); // there is a chance that the timeout occurred, but the topic was still created, check again if (!await ExistsAsync(subscriptionDescription.TopicPath, subscriptionDescription.Name, metadata.Description, namespaceManager, removeCacheEntry: true).ConfigureAwait(false)) { throw; } logger.Info($"Looks like subscription '{subscriptionDescription.Name}' in namespace '{namespaceManager.Address.Host}' exists anyway."); } catch (MessagingException ex) { var loggedMessage = $"{(ex.IsTransient ? "Transient" : "Non transient")} {ex.GetType().Name} occurred on subscription '{subscriptionDescription.Name}' creation for topic '{subscriptionDescription.TopicPath}' in namespace '{namespaceManager.Address.Host}'."; if (!ex.IsTransient) { logger.Fatal(loggedMessage, ex); throw; } logger.Info(loggedMessage, ex); } return(subscriptionDescription); }
public async Task <SubscriptionDescription> Create(string topicPath, string subscriptionName, SubscriptionMetadata metadata, string sqlFilter, INamespaceManager namespaceManager, string forwardTo = null) { var subscriptionDescription = subscriptionDescriptionFactory(topicPath, subscriptionName, settings); if (!string.IsNullOrWhiteSpace(forwardTo)) { subscriptionDescription.ForwardTo = forwardTo; } subscriptionDescription.UserMetadata = metadata.Description; try { if (!await ExistsAsync(topicPath, subscriptionName, metadata.Description, namespaceManager).ConfigureAwait(false)) { await namespaceManager.CreateSubscription(subscriptionDescription, sqlFilter).ConfigureAwait(false); logger.Info($"Subscription '{subscriptionDescription.UserMetadata}' in namespace '{namespaceManager.Address.Host}' created as '{subscriptionDescription.Name}'."); var key = GenerateSubscriptionKey(namespaceManager.Address, subscriptionDescription.TopicPath, subscriptionDescription.Name); await rememberExistence.AddOrUpdate(key, keyNotFound => Task.FromResult(true), (updateTopicPath, previousValue) => Task.FromResult(true)).ConfigureAwait(false); } else { logger.Info($"Subscription '{subscriptionDescription.Name}' in namespace '{namespaceManager.Address.Host}' aka '{subscriptionDescription.UserMetadata}' already exists, skipping creation."); logger.InfoFormat("Checking if subscription '{0}' in namespace '{1}' needs to be updated.", subscriptionDescription.Name, namespaceManager.Address.Host); var existingSubscriptionDescription = await namespaceManager.GetSubscription(subscriptionDescription.TopicPath, subscriptionDescription.Name).ConfigureAwait(false); if (MembersAreNotEqual(existingSubscriptionDescription, subscriptionDescription)) { OverrideImmutableMembers(existingSubscriptionDescription, subscriptionDescription); logger.InfoFormat("Updating subscription '{0}' in namespace '{1}' with new description.", subscriptionDescription.Name, namespaceManager.Address.Host); await namespaceManager.UpdateSubscription(subscriptionDescription).ConfigureAwait(false); } } } catch (MessagingEntityAlreadyExistsException) { // the subscription already exists or another node beat us to it, which is ok logger.InfoFormat("Subscription '{0}' in namespace '{1}' already exists, another node probably beat us to it.", subscriptionDescription.Name, namespaceManager.Address.Host); } catch (TimeoutException) { logger.InfoFormat("Timeout occurred on subscription creation for topic '{0}' subscription name '{1}' in namespace '{2}' going to validate if it doesn't exist.", subscriptionDescription.TopicPath, subscriptionDescription.Name, namespaceManager.Address.Host); // there is a chance that the timeout occurred, but the topic was still created, check again if (!await ExistsAsync(subscriptionDescription.TopicPath, subscriptionDescription.Name, metadata.Description, namespaceManager, removeCacheEntry: true).ConfigureAwait(false)) { throw; } logger.InfoFormat("Looks like subscription '{0}' in namespace '{1}' exists anyway.", subscriptionDescription.Name, namespaceManager.Address.Host); } catch (MessagingException ex) { var loggedMessage = $"{(ex.IsTransient ? "Transient" : "Non transient")} {ex.GetType().Name} occurred on subscription '{subscriptionDescription.Name}' creation for topic '{subscriptionDescription.TopicPath}' in namespace '{namespaceManager.Address.Host}'."; if (!ex.IsTransient) { logger.Fatal(loggedMessage, ex); throw; } logger.Info(loggedMessage, ex); } return(subscriptionDescription); }