/// <summary> /// Checks for catch up subscriptions. /// </summary> /// <returns></returns> private async Task CheckForCatchUpSubscriptions() { try { Logger.LogInformation("Checking for CatchUp subscriptions"); // Get a handle to the repository INewConfigurationRepository configRepository = this.RepositoryResolver(); // Get the event store server id from the config Guid eventStoreServerId = this.ServiceSettings.Value.EventStoreServerId; // Get the subscription CatchupSubscriptionConfiguration catchUpSubscriptionGroup = await configRepository.GetNextCatchupSubscriptionConfiguration(eventStoreServerId, CancellationToken.None); if (catchUpSubscriptionGroup != null) { Logger.LogInformation($"About connect to stream for catch up {catchUpSubscriptionGroup.StreamName}"); Logger.LogInformation($"CatchUp retrieved with Id {catchUpSubscriptionGroup.SubscriptionId}"); // Create a new subscription ISubscription subscription = this.SubscriptionResolver(); // Start this subscription await subscription.StartSubscription(catchUpSubscriptionGroup.SubscriptionId, catchUpSubscriptionGroup.StreamName, catchUpSubscriptionGroup.Name, catchUpSubscriptionGroup.Position, catchUpSubscriptionGroup.EndPointUri, subscriptionType : SubscriptionType.CatchUp); } } catch (Exception ex) { Logger.LogError(ex); } }
/// <summary> /// Processes the cache event. /// </summary> /// <param name="cacheEventType">Type of the cache event.</param> /// <param name="subscriptionConfiguration">The subscription group.</param> /// <returns></returns> private async Task ProcessCacheEvent(CacheEventType cacheEventType, SubscriptionConfiguration subscriptionConfiguration) { if (cacheEventType == CacheEventType.Added) { // Create a new subscription ISubscription subscription = this.SubscriptionResolver(); // Start this subscription await subscription.StartSubscription(subscriptionConfiguration.SubscriptionId, subscriptionConfiguration.StreamName, subscriptionConfiguration.GroupName); // Add this to the cached subscription list this.SubscriptionList.Add(subscription); } else if (cacheEventType == CacheEventType.Removed) { // Find the item that has been removed based on the event argument ISubscription subscription = this.SubscriptionList.Single(x => x.SubscriptionId == subscriptionConfiguration.SubscriptionId); // Stop the subscription await subscription.StopSubscription(); // Now remove the item this.SubscriptionList.Remove(subscription); } else if (cacheEventType == CacheEventType.Updated) { // Find the item that has been updated based on the event argument ISubscription subscription = this.SubscriptionList.SingleOrDefault(x => x.SubscriptionId == subscriptionConfiguration.SubscriptionId); if (subscription != null) { // Stop the current subscription await subscription.StopSubscription(); // Re-Create the subscription // Start this subscription await subscription.StartSubscription(subscriptionConfiguration.SubscriptionId, subscriptionConfiguration.StreamName, subscriptionConfiguration.GroupName, subscriptionConfiguration.StreamPositionToRestartFrom); // Update the repository position INewConfigurationRepository configRepository = this.RepositoryResolver(); await configRepository.ResetSubscriptionStreamPosition(subscriptionConfiguration.SubscriptionId, CancellationToken.None); } } }
/// <summary> /// Lives the process started. /// </summary> /// <param name="catchUpSubscriptionId">The catch up subscription identifier.</param> private async void LiveProcessStarted(Guid catchUpSubscriptionId) { Logger.LogInformation($"LiveProcessStarted for catchUpSubscriptionId {catchUpSubscriptionId}"); // Get the config repository INewConfigurationRepository repository = this.ConfigurationRepositoryResolver(); try { // Remove this catchup subscription await repository.DeleteCatchUpSubscription(catchUpSubscriptionId, CancellationToken.None); await this.StopSubscription(); } catch (Exception e) { Exception ex = new Exception($"Failed to delete CatchUpSubscription {catchUpSubscriptionId}", e); Logger.LogError(ex); } }
/// <summary> /// Events the appeared. /// </summary> /// <param name="subscription">The subscription.</param> /// <returns></returns> /// <exception cref="NotFoundException">No Endpoint Uri found for Subscription Id {subscription.SubscriptionGroupId}</exception> /// <exception cref="Exception"></exception> /// <exception cref="InvalidOperationException"></exception> /// <exception cref="TimeoutException">Error processing Event Id {subscription.EventId}. Attempted to Send to Uri: {endpoint}, request Timed out</exception> private async Task <Boolean> EventAppeared(SubscriptionDataTransferObject subscription) { Boolean result = false; String endpoint = null; try { // Lookup the endpoint that we need to post this event to // Get the config repository INewConfigurationRepository repository = this.ConfigurationRepositoryResolver(); // Get the endpoint from the repository if (this.Type == SubscriptionType.Persistent) { SubscriptionConfiguration configuration = await repository.GetSubscriptionConfiguration(Guid.Parse(subscription.SubscriptionGroupId), CancellationToken.None); endpoint = configuration.EndPointUri; } else if (this.Type == SubscriptionType.CatchUp) { CatchupSubscriptionConfiguration configuration = await repository.GetCatchupSubscriptionConfiguration(Guid.Parse(subscription.SubscriptionGroupId), CancellationToken.None); endpoint = configuration.EndPointUri; } // Check we have found an endpoint if (string.IsNullOrEmpty(endpoint)) { throw new NotFoundException($"No Endpoint Uri found for Subscription Id {subscription.SubscriptionGroupId}"); } // Send the request to the endpoint using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, endpoint)) { request.Content = new StringContent(subscription.SerialisedData, Encoding.UTF8, "application/json"); // Set the http timeout from the config value CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(100000); HttpResponseMessage response = await this.HttpClient.SendAsync(request, cts.Token); if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.InternalServerError) { // Get the message contents String responseBody = await response.Content.ReadAsStringAsync(); // Construct an error message String errorMessage = $"Failed Posting to Subscription Group Endpoint [{response.StatusCode}]. Response [{responseBody}]"; throw new Exception(errorMessage); } if (!response.IsSuccessStatusCode && response.StatusCode == HttpStatusCode.InternalServerError) { String errorMessage = "Failed Posting to Subscription Group Endpoint - Internal Server Error"; throw new InvalidOperationException(errorMessage); } } result = true; } catch (TaskCanceledException tcex) { // This transaction has timed out throw new TimeoutException($"Error processing Event Id {subscription.EventId}. Attempted to Send to Uri: {endpoint}, request Timed out"); } catch (NotFoundException nex) { Logger.LogError(nex); } catch (Exception ex) { String errorMessage = $"Error processing Event Id {subscription.EventId}. Attempted to Send to Uri: {endpoint}"; Exception exception = new Exception(errorMessage, ex); Logger.LogError(exception); } return(result); }
/// <summary> /// Initializes a new instance of the <see cref="SubscriptionCache" /> class. /// </summary> /// <param name="configurationRepository">The configuration repository.</param> /// <param name="serviceSettings">The service settings.</param> public SubscriptionCache(INewConfigurationRepository configurationRepository, IOptions <ServiceSettings> serviceSettings) { this.ConfigurationRepository = configurationRepository; this.ServiceSettings = serviceSettings; this.SubscriptionConfigurations = new Dictionary <Guid, SubscriptionConfiguration>(); }