/// <inheritdoc/> public async Task <bool> TryPersist(ScopeId scopeId, IFilterDefinition filterDefinition, CancellationToken cancellationToken) { var tryGetStream = await _streamDefinitions.TryGet(scopeId, filterDefinition.TargetStream, cancellationToken).ConfigureAwait(false); if (!tryGetStream.Success) { return(false); } var newStreamDefinition = new StreamDefinition(filterDefinition); await _streamDefinitions.Persist(scopeId, newStreamDefinition, cancellationToken).ConfigureAwait(false); return(true); }
async Task ValidateFilter <TFilterDefinition>( ScopeId scopeId, TFilterDefinition filterDefinition, Func <IFilterProcessor <TFilterDefinition> > getFilterProcessor, CancellationToken cancellationToken) where TFilterDefinition : IFilterDefinition { _logger.Debug("Validating Filter '{Filter}'", filterDefinition.TargetStream); var filterValidationResults = await _filterForAllTenants.Validate(getFilterProcessor, cancellationToken).ConfigureAwait(false); if (filterValidationResults.Any(_ => !_.Value.Succeeded)) { var firstFailedValidation = filterValidationResults.Select(_ => _.Value).First(_ => !_.Succeeded); _logger.Warning("Filter validation failed. {Reason}", filterDefinition.TargetStream, firstFailedValidation.FailureReason); throw new FilterValidationFailed(filterDefinition.TargetStream, firstFailedValidation.FailureReason); } var filteredStreamDefinition = new StreamDefinition(filterDefinition); _logger.Debug("Persisting definition for Stream '{Stream}'", filteredStreamDefinition.StreamId); await _streamDefinitions.Persist(scopeId, filteredStreamDefinition, cancellationToken).ConfigureAwait(false); }
/// <inheritdoc/> public override async Task Connect( IAsyncStreamReader <EventHandlerClientToRuntimeMessage> runtimeStream, IServerStreamWriter <EventHandlerRuntimeToClientMessage> clientStream, ServerCallContext context) { _logger.Debug("Connecting Event Handler"); using var cts = CancellationTokenSource.CreateLinkedTokenSource(_hostApplicationLifetime.ApplicationStopping, context.CancellationToken); var cancellationToken = cts.Token; var dispatcher = _reverseCallDispatchers.GetFor <EventHandlerClientToRuntimeMessage, EventHandlerRuntimeToClientMessage, EventHandlerRegistrationRequest, EventHandlerRegistrationResponse, HandleEventRequest, EventHandlerResponse>( runtimeStream, clientStream, context, _ => _.RegistrationRequest, (serverMessage, registrationResponse) => serverMessage.RegistrationResponse = registrationResponse, (serverMessage, request) => serverMessage.HandleRequest = request, _ => _.HandleResult, _ => _.CallContext, (request, context) => request.CallContext = context, _ => _.CallContext, (message, ping) => message.Ping = ping, message => message.Pong); _logger.Trace("Waiting for connection arguments..."); if (!await dispatcher.ReceiveArguments(cancellationToken).ConfigureAwait(false)) { const string message = "Event Handlers connection arguments were not received"; _logger.Warning(message); var failure = new Failure(EventHandlersFailures.NoEventHandlerRegistrationReceived, message); await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false); return; } _logger.Trace("Received connection arguments"); var arguments = dispatcher.Arguments; var executionContext = arguments.CallContext.ExecutionContext.ToExecutionContext(); _logger.Trace("Setting execution context{NewLine}{ExecutionContext}", System.Environment.NewLine, executionContext); _executionContextManager.CurrentFor(executionContext); var sourceStream = StreamId.EventLog; _logger.Trace("Received Source Stream '{SourceStream}'", sourceStream); var eventHandlerId = arguments.EventHandlerId.To <EventProcessorId>(); _logger.Trace("Received Event Handler '{EventHandler}'", eventHandlerId); StreamId targetStream = eventHandlerId.Value; var scopeId = arguments.ScopeId.To <ScopeId>(); _logger.Trace("Received Scope '{Scope}'", scopeId); var types = arguments.Types_.Select(_ => _.Id.To <ArtifactId>()); _logger.Trace("Received Types: [{Types}]'", string.Join(", ", types.Select(_ => $"'{_}'"))); var partitioned = arguments.Partitioned; _logger.Trace("Event Handler '{EventHandler}' {PartitionedString}", eventHandlerId, partitioned ? "is partitioned" : "is not partitioned"); if (targetStream.IsNonWriteable) { _logger.Warning("Cannot register Event Handler '{EventHandler}' because it is an invalid Stream Id", eventHandlerId); var failure = new Failure( EventHandlersFailures.CannotRegisterEventHandlerOnNonWriteableStream, $"Cannot register Event Handler: '{eventHandlerId}' because it is an invalid Stream Id"); await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false); return; } _logger.Debug("Connecting Event Handler '{EventHandlerId}'", eventHandlerId); var filterDefinition = new TypeFilterWithEventSourcePartitionDefinition(sourceStream, targetStream, types, partitioned); var filteredStreamDefinition = new StreamDefinition(filterDefinition); Func <IFilterProcessor <TypeFilterWithEventSourcePartitionDefinition> > getFilterProcessor = () => new TypeFilterWithEventSourcePartition( scopeId, filterDefinition, _getEventsToStreamsWriter(), _loggerManager.CreateLogger <TypeFilterWithEventSourcePartition>()); var tryRegisterFilterStreamProcessor = TryRegisterFilterStreamProcessor <TypeFilterWithEventSourcePartitionDefinition>( scopeId, eventHandlerId, getFilterProcessor, cancellationToken); if (!tryRegisterFilterStreamProcessor.Success) { if (tryRegisterFilterStreamProcessor.HasException) { var exception = tryRegisterFilterStreamProcessor.Exception; _logger.Warning(exception, "An error occurred while registering Event Handler '{EventHandlerId}'", eventHandlerId); ExceptionDispatchInfo.Capture(exception).Throw(); } else { _logger.Debug("Failed to register Event Handler '{EventHandlerId}'. Filter already registered", eventHandlerId); var failure = new Failure( FiltersFailures.FailedToRegisterFilter, $"Failed to register Event Handler: {eventHandlerId}. Filter already registered."); await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false); return; } } using var filterStreamProcessor = tryRegisterFilterStreamProcessor.Result; var tryRegisterEventProcessorStreamProcessor = TryRegisterEventProcessorStreamProcessor( scopeId, eventHandlerId, filteredStreamDefinition, () => new EventProcessor( scopeId, eventHandlerId, dispatcher, _loggerManager.CreateLogger <EventProcessor>()), cancellationToken); if (!tryRegisterEventProcessorStreamProcessor.Success) { if (tryRegisterEventProcessorStreamProcessor.HasException) { var exception = tryRegisterEventProcessorStreamProcessor.Exception; _logger.Warning(exception, "An error occurred while registering Event Handler' {EventHandlerId}", eventHandlerId); ExceptionDispatchInfo.Capture(exception).Throw(); } else { _logger.Warning("Failed to register Event Handler: {eventHandlerId}. Event Processor already registered on Source Stream '{SourceStreamId}'", eventHandlerId, eventHandlerId); var failure = new Failure( FiltersFailures.FailedToRegisterFilter, $"Failed to register Event Handler: {eventHandlerId}. Event Processor already registered on Source Stream: '{eventHandlerId}'"); await WriteFailedRegistrationResponse(dispatcher, failure, cancellationToken).ConfigureAwait(false); return; } } using var eventProcessorStreamProcessor = tryRegisterEventProcessorStreamProcessor.Result; var tryStartEventHandler = await TryStartEventHandler( dispatcher, filterStreamProcessor, eventProcessorStreamProcessor, scopeId, filterDefinition, getFilterProcessor, cancellationToken).ConfigureAwait(false); if (!tryStartEventHandler.Success) { cts.Cancel(); if (tryStartEventHandler.HasException) { var exception = tryStartEventHandler.Exception; _logger.Debug(exception, "An error occurred while starting Event Handler '{EventHandlerId}' in Scope {ScopeId}", eventHandlerId, scopeId); ExceptionDispatchInfo.Capture(exception).Throw(); } else { _logger.Warning("Could not start Event Handler '{EventHandlerId}' in Scope: {ScopeId}", eventHandlerId, scopeId); return; } } var tasks = tryStartEventHandler.Result; try { await Task.WhenAny(tasks).ConfigureAwait(false); if (TryGetException(tasks, out var ex)) { _logger.Warning(ex, "An error occurred while running Event Handler '{EventHandlerId}' in Scope: '{ScopeId}'", eventHandlerId, scopeId); ExceptionDispatchInfo.Capture(ex).Throw(); } } finally { cts.Cancel(); await Task.WhenAll(tasks).ConfigureAwait(false); _logger.Debug("Event Handler: '{EventHandler}' in Scope: '{ScopeId}' disconnected", eventHandlerId, scopeId); } }
async Task <FilterValidationResult> PerformValidation(IFilterProcessor <IFilterDefinition> filter, StreamPosition lastUnprocessedEvent, CancellationToken cancellationToken) { try { var streamDefinition = new StreamDefinition(filter.Definition); var oldStreamEventsFetcher = await _eventFetchers.GetRangeFetcherFor( filter.Scope, streamDefinition, cancellationToken).ConfigureAwait(false); var eventLogFetcher = await _eventFetchers.GetRangeFetcherFor( filter.Scope, new EventLogStreamDefinition(), cancellationToken).ConfigureAwait(false); var oldStream = oldStreamEventsFetcher.FetchRange( new StreamPositionRange(StreamPosition.Start, ulong.MaxValue), cancellationToken); var eventLogStream = eventLogFetcher.FetchRange(new StreamPositionRange(StreamPosition.Start, lastUnprocessedEvent), cancellationToken); await using var oldStreamEnumerator = oldStream.GetAsyncEnumerator(cancellationToken); var newStreamPosition = 0; await foreach (var eventFromEventLog in eventLogStream.WithCancellation(cancellationToken)) { var filteringResult = await filter.Filter(eventFromEventLog.Event, PartitionId.None, filter.Identifier, eventFromEventLog.Event.ExecutionContext, cancellationToken).ConfigureAwait(false); if (filteringResult is FailedFiltering failedResult) { return(FilterValidationResult.Failed(failedResult.FailureReason)); } if (!filteringResult.IsIncluded) { continue; } if (!await oldStreamEnumerator.MoveNextAsync()) { return(FilterValidationResult.Failed("The number of events included in the new stream generated from the filter does not match the old stream.")); } var oldStreamEvent = oldStreamEnumerator.Current; var filteredEvent = new StreamEvent( eventFromEventLog.Event, new StreamPosition((ulong)newStreamPosition++), filter.Definition.TargetStream, filteringResult.Partition, streamDefinition.Partitioned); if (EventsAreNotEqual(filter.Definition, filteredEvent, oldStreamEvent, out var failedValidation)) { return(failedValidation); } } if (await oldStreamEnumerator.MoveNextAsync()) { return(FilterValidationResult.Failed($"The number of events included in the new stream generated from the filter does not match the old stream.")); } return(FilterValidationResult.Succeeded()); } catch (Exception exception) { return(FilterValidationResult.Failed(exception.Message)); } }