public async Task <SubscriptionDescription> Create(string topicPath, string subscriptionName, SubscriptionMetadataInternal metadata, string sqlFilter, INamespaceManagerInternal namespaceManager, string forwardTo = null) { var subscriptionDescription = new SubscriptionDescription(topicPath, subscriptionName) { EnableBatchedOperations = subscriptionSettings.EnableBatchedOperations, AutoDeleteOnIdle = subscriptionSettings.AutoDeleteOnIdle, DefaultMessageTimeToLive = subscriptionSettings.DefaultMessageTimeToLive, EnableDeadLetteringOnFilterEvaluationExceptions = subscriptionSettings.EnableDeadLetteringOnFilterEvaluationExceptions, EnableDeadLetteringOnMessageExpiration = subscriptionSettings.EnableDeadLetteringOnMessageExpiration, LockDuration = subscriptionSettings.LockDuration, MaxDeliveryCount = DefaultMaxDeliveryCountForNoImmediateRetries }; if (subscriptionSettings.ForwardDeadLetteredMessagesToCondition(SubscriptionClient.FormatSubscriptionPath(topicPath, subscriptionName))) { subscriptionDescription.ForwardDeadLetteredMessagesTo = subscriptionSettings.ForwardDeadLetteredMessagesTo; } subscriptionSettings.DescriptionCustomizer(subscriptionDescription); 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 => TaskEx.CompletedTrue, (updateTopicPath, previousValue) => TaskEx.CompletedTrue).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); }
public async Task <SubscriptionDescription> Create(string topicPath, string subscriptionName, SubscriptionMetadataInternal metadata, string sqlFilter, INamespaceManagerInternal namespaceManager, string forwardTo) { if (!(metadata is ForwardingTopologySubscriptionMetadata meta)) { throw new InvalidOperationException($"Cannot create subscription `{subscriptionName}` for topic `{topicPath}` without namespace information required."); } var subscriptionDescription = new SubscriptionDescription(topicPath, subscriptionName) { EnableBatchedOperations = subscriptionSettings.EnableBatchedOperations, AutoDeleteOnIdle = subscriptionSettings.AutoDeleteOnIdle, DefaultMessageTimeToLive = subscriptionSettings.DefaultMessageTimeToLive, EnableDeadLetteringOnFilterEvaluationExceptions = subscriptionSettings.EnableDeadLetteringOnFilterEvaluationExceptions, EnableDeadLetteringOnMessageExpiration = subscriptionSettings.EnableDeadLetteringOnMessageExpiration, LockDuration = subscriptionSettings.LockDuration, MaxDeliveryCount = DefaultMaxDeliveryCountForNoImmediateRetries }; if (subscriptionSettings.ForwardDeadLetteredMessagesToCondition(SubscriptionClient.FormatSubscriptionPath(topicPath, subscriptionName))) { subscriptionDescription.ForwardDeadLetteredMessagesTo = subscriptionSettings.ForwardDeadLetteredMessagesTo; } subscriptionSettings.DescriptionCustomizer(subscriptionDescription); subscriptionDescription.ForwardTo = forwardTo; subscriptionDescription.UserMetadata = metadata.Description; try { var exists = await ExistsAsync(topicPath, subscriptionName, metadata.Description, namespaceManager).ConfigureAwait(false); if (!exists) { var ruleDescription = new RuleDescription { Filter = new SqlFilter(sqlFilter), Name = metadata.SubscriptionNameBasedOnEventWithNamespace }; try { 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 => TaskEx.CompletedTrue, (updateTopicPath, previousValue) => TaskEx.CompletedTrue).ConfigureAwait(false); } 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."); exists = true; } } if (exists) { 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 (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); }