From 8e3baa0feca9285d860a233b6a862d790589554b Mon Sep 17 00:00:00 2001 From: Alexey Zimarev Date: Fri, 26 Sep 2025 09:25:14 +0200 Subject: [PATCH] * Remove unused declarations and parameters * Change Tuple class to a normal VO in one place (queue) * Code cleanup without changing the behaviour --- .../SpecRunner.cs | 136 +-- .../AssertEx.cs | 1 - .../Other/claims_serialization.cs | 10 +- .../TestFixtureWithProjectionCoreService.cs | 18 +- .../TestFixtureWithCoreProjection.cs | 1 - ...tureWithCoreProjectionCheckpointManager.cs | 9 +- ...FixtureWithMultiStreamCheckpointManager.cs | 4 +- .../when_prerecording_event_order.cs | 2 +- ...corded_events_after_the_last_checkpoint.cs | 2 +- ...orded_events_before_the_last_checkpoint.cs | 2 +- ...g_with_prerecorded_events_in_past_epoch.cs | 2 +- .../with_multi_stream_checkpoint_manager.cs | 2 +- ...leted_and_requesting_checkpoint_to_stop.cs | 2 +- ...ion_checkpoint_manager_has_been_created.cs | 2 +- ...eckpoint_manager_has_been_reinitialized.cs | 2 +- ...n_creating_a_default_checkpoint_manager.cs | 36 +- ..._the_core_projection_checkpoint_manager.cs | 2 +- .../when_projection_state_is_too_large.cs | 2 +- ..._the_core_projection_checkpoint_manager.cs | 2 +- ...cation_with_multi_phase_core_projection.cs | 4 +- .../when_creating_a_projection.cs | 11 - .../when_starting_a_projection.cs | 1 - ...point_requested_on_a_non_started_stream.cs | 2 +- ...cted_tag_the_started_in_recovery_stream.cs | 3 +- ...sly_written_events_at_the_same_position.cs | 3 +- ...usly_written_events_in_different_epochs.cs | 3 +- ...cted_tag_the_started_in_recovery_stream.cs | 3 +- ..._completes_before_a_timeout_in_recovery.cs | 3 +- .../when_a_read_times_out_in_recovery.cs | 3 +- .../when_checkpoint_requested.cs | 2 +- .../when_checkpoint_requested_but_disabled.cs | 2 +- ...ested_with_all_writes_already_completed.cs | 2 +- ...heckpoint_requested_with_pending_writes.cs | 2 +- .../when_creating_an_emitted_stream.cs | 16 +- .../emitted_stream/when_handling_a_timeout.cs | 3 +- ...handling_an_emit_the_not_started_stream.cs | 2 +- ..._an_emit_the_started_in_recovery_stream.cs | 3 +- ...dling_an_emit_to_the_nonexisting_stream.cs | 3 +- ..._emit_with_caused_by_and_correlation_id.cs | 3 +- ...andling_an_emit_with_committed_callback.cs | 3 +- ...cted_tag_the_started_in_recovery_stream.cs | 3 +- ...en_handling_an_emit_with_extra_metadata.cs | 3 +- ...n_handling_an_emit_with_not_ready_event.cs | 3 +- ...n_handling_an_emit_with_stream_metadata.cs | 2 +- ...ndling_an_emit_with_write_as_configured.cs | 3 +- .../when_handling_emits_in_invalid_order.cs | 3 +- ...ng_emits_with_previously_written_events.cs | 2 +- ...sly_written_events_at_the_same_position.cs | 3 +- .../when_the_stream_is_started.cs | 2 +- ..._is_started_with_already_emitted_events.cs | 2 +- .../TestFixtureWithEventFilter.cs | 2 +- .../TestFixtureWithEventReaderService.cs | 2 +- .../when_including_links.cs | 1 - .../when_not_including_links.cs | 1 - .../catching_up/index_checkpoint.cs | 1 - ...n_one_event_type_has_been_never_emitted.cs | 1 - ...ordering_happens_in_event_by_type_index.cs | 1 - .../when_handling_subscribe_requests.cs | 6 +- .../FakeReaderSubscription.cs | 7 +- .../multi_stream_reader/reordering.cs | 1 - .../multi_stream_reader/when_creating.cs | 20 +- .../when_handling_deleted_streams.cs | 3 +- ...ndling_eof_for_all_streams_and_idle_eof.cs | 3 +- .../when_handling_read_completed.cs | 3 +- ...n_handling_read_completed_and_no_stream.cs | 3 +- ...handling_read_completed_for_all_streams.cs | 3 +- ...d_for_all_streams_after_pause_requested.cs | 3 +- ...read_completed_for_all_streams_and_eofs.cs | 3 +- ...l_streams_then_pause_requested_then_eof.cs | 3 +- ...ts_and_reader_starting_after_event_zero.cs | 3 +- ...vents_and_reader_starting_at_event_zero.cs | 3 +- .../when_has_been_created.cs | 3 +- .../when_onetime_reader_handles_eof.cs | 3 +- .../when_read_completes_before_timeout.cs | 3 +- ...eam_completes_but_times_out_for_another.cs | 3 +- .../when_read_timeout_occurs.cs | 3 +- .../multi_stream_reader/when_resuming.cs | 3 +- .../reader_subscription_dispatcher.cs | 4 +- .../when_creating_projection_subscription.cs | 1 - .../TestFixtureWithProjectionSubscription.cs | 5 +- .../when_creating_projection_subscription.cs | 17 +- ...ing_events_with_content_type_validation.cs | 12 +- ...WithProjectionCoreAndManagementServices.cs | 9 +- ...StreamMultiOutputCheckpointManagerTests.cs | 6 +- .../EventReaders/Feeds/FeedReader.cs | 3 +- .../EventReaders/Feeds/FeedReaderService.cs | 14 +- .../CoreProjectionCheckpointWriterMessage.cs | 24 +- .../CoreProjectionManagementControlMessage.cs | 14 +- .../CoreProjectionManagementMessage.cs | 224 +---- .../CoreProjectionManagementMessageBase.cs | 12 +- .../CoreProjectionProcessingMessage.cs | 136 +-- .../Messages/CoreProjectionStatusMessage.cs | 164 +--- .../EventReaderSubscriptionMessageBase.cs | 227 ++--- .../EventReaders/Feeds/FeedReaderMessage.cs | 61 +- .../Messages/ICoreProjection.cs | 3 +- .../Messages/IProjectionCheckpointManager.cs | 6 +- .../Messages/IQuerySources.cs | 31 +- .../Messages/ProjectionCoreServiceMessage.cs | 59 +- .../Messages/ProjectionManagementMessage.cs | 908 +++++------------- .../Messages/ProjectionSubsystemMessage.cs | 68 +- .../Messages/QuerySourcesDefinition.cs | 48 +- .../Messages/ReaderCoreServiceMessage.cs | 16 +- .../Messages/ReaderSubscriptionManagement.cs | 69 +- .../Messages/ReaderSubscriptionMessage.cs | 175 +--- .../Messaging/PublishToWrapEnvelop.cs | 14 +- .../Messaging/UnwrapEnvelopeMessage.cs | 18 +- .../IProjectionStateSerializationTracker.cs | 2 +- .../Metrics/ProjectionTracker.cs | 3 - .../ProjectionCoreWorkersNode.cs | 31 +- .../ProjectionManagerNode.cs | 5 +- .../ProjectionWorkerNode.cs | 25 +- .../ProjectionsStandardComponents.cs | 66 +- .../ProjectionsSubsystem.cs | 95 +- .../Grpc/ProjectionManagement.Disable.cs | 2 +- .../ProjectionManagement.RestartSubsystem.cs | 2 +- .../Grpc/ProjectionManagement.Statistics.cs | 2 +- .../Services/Grpc/ProjectionManagement.cs | 11 +- .../Services/Http/ProjectionsController.cs | 333 +++---- .../ProjectionsStatisticsHttpFormatted.cs | 11 +- .../Services/IProjectionStateHandler.cs | 7 +- .../Interpreted/JintProjectionStateHandler.cs | 642 ++++++------- .../Services/Management/ManagedProjection.cs | 537 ++++------- .../ManagedProjectionStateHandler.cs | 136 +++ .../ManagedProjectionStates/AbortedState.cs | 10 - .../ManagedProjectionStates/AbortingState.cs | 22 - .../ManagedProjectionStates/CompletedState.cs | 10 - .../CreatingLoadingLoadedState.cs | 10 - .../ManagedProjectionStates/DeletingState.cs | 11 - .../ManagedProjectionStates/FaultedState.cs | 10 - .../LoadingStateState.cs | 22 - .../ManagedProjectionStateBase.cs | 38 - .../ManagedProjectionStates/PreparedState.cs | 10 - .../ManagedProjectionStates/PreparingState.cs | 26 - .../ManagedProjectionStates/RunningState.cs | 28 - .../ManagedProjectionStates/StartingState.cs | 22 - .../ManagedProjectionStates/StoppedState.cs | 10 - .../ManagedProjectionStates/StoppingState.cs | 23 - .../Services/Management/ProjectionManager.cs | 576 +++++------ .../ProjectionManagerMessageDispatcher.cs | 10 +- .../ProjectionStateHandlerFactory.cs | 18 +- .../Processing/Checkpointing/CheckpointTag.cs | 397 ++++---- .../Checkpointing/CheckpointTagExtensions.cs | 31 +- .../Checkpointing/CheckpointTagVersion.cs | 10 +- .../CoreProjectionCheckpointManager.cs | 105 +- .../CoreProjectionCheckpointReader.cs | 10 +- .../CoreProjectionCheckpointWriter.cs | 93 +- .../Checkpointing/DefaultCheckpointManager.cs | 82 +- .../ICoreProjectionCheckpointManager.cs | 3 - .../Checkpointing/PositionTagger.cs | 19 +- .../Checkpointing/PositionTracker.cs | 28 +- .../Checkpointing/ProjectionCheckpoint.cs | 52 +- .../Services/Processing/CoreProjection.cs | 80 +- .../Processing/CoreProjectionQueue.cs | 81 +- .../EmittedEvents/EmittedDataEvent.cs | 61 +- .../Emitting/EmittedEvents/EmittedEvent.cs | 61 +- .../EmittedEvents/EmittedEventEnvelope.cs | 12 +- .../EmittedEventResolutionNeeded.cs | 16 +- .../Emitting/EmittedEvents/EmittedLinkTo.cs | 48 +- .../EmittedLinkToWithRecategorization.cs | 43 +- .../EmittedEvents/ErroredEmittedEvent.cs | 8 +- .../Emitting/EmittedEvents/ExtraMetaData.cs | 10 +- .../EmittedEvents/IValidatedEmittedEvent.cs | 2 +- .../EmittedEvents/IgnoredEmittedEvent.cs | 3 +- .../EmittedEvents/ValidEmittedEvent.cs | 14 +- .../EmittedStream.WriterConfiguration.cs | 72 +- .../Processing/Emitting/EmittedStream.cs | 246 +++-- .../Emitting/EmittedStreamsDeleter.cs | 127 ++- .../Emitting/EmittedStreamsTracker.cs | 33 +- .../Emitting/EmittedStreamsWriter.cs | 10 +- .../InvalidEmittedEventSequenceException.cs | 6 +- .../Emitting/QueuedEmittedStreamsWriter.cs | 12 +- .../Processing/Emitting/ResultEventEmitter.cs | 30 +- .../EventByTypeIndexEventFilter.cs | 21 +- .../EventByTypeIndexEventReader.IndexBased.cs | 107 +-- ...ventByTypeIndexEventReader.PendingEvent.cs | 14 +- .../EventByTypeIndexEventReader.State.cs | 25 +- .../EventByTypeIndexEventReader.TfBased.cs | 31 +- .../EventByTypeIndexEventReader.cs | 32 +- .../EventByTypeIndexPositionTagger.cs | 66 +- .../Services/Processing/EventFilter.cs | 8 +- .../Processing/EventProcessedResult.cs | 77 +- .../Services/Processing/EventReader.cs | 56 +- .../Processing/EventReaderCoreService.cs | 56 +- .../Services/Processing/IEventReader.cs | 1 - .../MultiStream/MultiStreamEventFilter.cs | 23 +- .../MultiStream/MultiStreamEventReader.cs | 131 ++- ...StreamMultiOutputCheckpointManager.Item.cs | 11 +- ...MultiStreamMultiOutputCheckpointManager.cs | 54 +- .../MultiStream/MultiStreamPositionTagger.cs | 57 +- .../ByHandleStatePartitionSelector.cs | 10 +- .../ByStreamStatePartitionSelector.cs | 5 +- .../NoopStatePartitionSelector.cs | 8 +- .../Processing/Partitioning/PartitionState.cs | 66 +- .../Partitioning/PartitionStateCache.cs | 120 +-- .../PartitionStateUpdateManager.cs | 49 +- ...ventProcessingProjectionProcessingPhase.cs | 167 ++-- ...ionProcessingPhase.ProgressResultWriter.cs | 23 - ...scriptionBasedProjectionProcessingPhase.cs | 277 +++--- .../Phases/IEventProcessingPhase.cs | 15 +- .../Phases/IProgressResultWriter.cs | 8 - .../Processing/Phases/PhasePositionTagger.cs | 14 +- .../WriteQueryEofProjectionProcessingPhase.cs | 26 +- ...iteQueryResultProjectionProcessingPhase.cs | 30 +- ...ueryResultProjectionProcessingPhaseBase.cs | 50 +- .../Processing/ProjectionCoreService.cs | 80 +- .../Processing/ProjectionNamesBuilder.cs | 82 +- .../Processing/ProjectionSourceDefinition.cs | 64 +- .../Services/Processing/ProjectionVersion.cs | 31 +- .../Services/Processing/QuerySourceOptions.cs | 9 - .../RequestResponseQueueForwarder.cs | 62 +- .../Services/Processing/ResolvedEvent.cs | 108 +-- .../SingleStream/CategoryEventFilter.cs | 23 +- .../SingleStream/StreamEventFilter.cs | 15 +- .../SingleStream/StreamEventReader.cs | 54 +- .../SingleStream/StreamPositionTagger.cs | 60 +- .../Processing/SourceDefinitionBuilder.cs | 92 +- .../Processing/StagedProcessingQueue.cs | 89 +- .../ContinuousProjectionProcessingStrategy.cs | 27 +- .../DefaultProjectionProcessingStrategy.cs | 39 +- ...ReaderBasedProjectionProcessingStrategy.cs | 76 +- .../Processing/Strategies/IReaderStrategy.cs | 5 +- .../Processing/Strategies/IResultWriter.cs | 8 - .../Strategies/ProcessingStrategySelector.cs | 29 +- .../ProjectionProcessingStrategy.cs | 36 +- .../Strategies/QueryProcessingStrategy.cs | 45 +- .../Processing/Strategies/ReaderStrategy.cs | 171 ++-- .../Processing/Strategies/ResultWriter.cs | 82 +- .../EventReorderingReaderSubscription.cs | 45 +- .../Subscriptions/IReaderSubscription.cs | 3 +- .../Subscriptions/ReaderSubscription.cs | 39 +- .../Subscriptions/ReaderSubscriptionBase.cs | 91 +- .../ReaderSubscriptionOptions.cs | 50 +- .../Processing/TaggedResolvedEvent.cs | 11 +- .../HeadingEventReader.CommittedEventItem.cs | 15 +- .../HeadingEventReader.Item.cs | 8 +- ...HeadingEventReader.PartitionDeletedItem.cs | 12 +- .../TransactionFile/HeadingEventReader.cs | 68 +- .../TransactionFile/PreparePositionTagger.cs | 60 +- .../TransactionFileEventFilter.cs | 21 +- .../TransactionFileEventReader.cs | 29 +- .../TransactionFilePositionTagger.cs | 58 +- .../WorkItems/CheckpointSuggestedWorkItem.cs | 32 +- .../WorkItems/CheckpointWorkItemBase.cs | 8 +- .../WorkItems/CommittedEventWorkItem.cs | 5 +- .../Processing/WorkItems/CompletedWorkItem.cs | 11 +- .../WorkItems/GetDataWorkItemBase.cs | 5 +- .../Processing/WorkItems/GetResultWorkItem.cs | 17 +- .../Processing/WorkItems/GetStateWorkItem.cs | 39 +- .../WorkItems/NotAuthorizedWorkItem.cs | 6 +- .../WorkItems/PartitionDeletedWorkItem.cs | 3 +- .../Processing/WorkItems/ProgressWorkItem.cs | 19 +- .../Services/Processing/WorkItems/WorkItem.cs | 23 +- .../Services/ProjectionConfig.cs | 114 +-- .../Services/ReaderSubscriptionDispatcher.cs | 22 +- .../Standard/ByCorrelationId.cs | 64 +- .../Standard/CategorizeEventsByStreamPath.cs | 46 +- .../Standard/CategorizeStreamByPath.cs | 10 +- .../Standard/IndexEventsByEventType.cs | 54 +- .../Standard/IndexStreams.cs | 46 +- .../Standard/StreamCategoryExtractor.cs | 27 +- ...StreamCategoryExtractorByFirstSeparator.cs | 14 +- .../StreamCategoryExtractorByLastSeparator.cs | 14 +- .../Standard/StreamDeletedHelper.cs | 2 +- .../IntegrationTests/ReadTests.cs | 1 - 264 files changed, 4080 insertions(+), 7287 deletions(-) create mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStateHandler.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortedState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortingState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CompletedState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CreatingLoadingLoadedState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/DeletingState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/FaultedState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/LoadingStateState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/ManagedProjectionStateBase.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparedState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparingState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/RunningState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StartingState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppedState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppingState.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.ProgressResultWriter.cs delete mode 100644 src/KurrentDB.Projections.Core/Services/Processing/Phases/IProgressResultWriter.cs diff --git a/src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs b/src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs index 09c06ff1863..b06957df9c2 100644 --- a/src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs +++ b/src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs @@ -24,13 +24,7 @@ namespace KurrentDB.Projections.Core.Javascript.Tests; -public class SpecRunner { - private readonly ITestOutputHelper _output; - - public SpecRunner(ITestOutputHelper output) { - _output = output; - } - +public class SpecRunner(ITestOutputHelper output) { private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true); public static IEnumerable GetTestCases() { @@ -71,10 +65,9 @@ public static IEnumerable GetTestCases() { var initializedPartitions = new List(); if (e.TryGetProperty("initializedPartitions", out var partitions)) { - foreach (var element in partitions.EnumerateArray()) { - initializedPartitions.Add(element.GetString()!); - } + initializedPartitions.AddRange(partitions.EnumerateArray().Select(element => element.GetString()!)); } + var expectedStates = new Dictionary(); var expectedResults = new Dictionary(); int stateCount = 0; @@ -84,9 +77,10 @@ public static IEnumerable GetTestCases() { var partition = state.GetProperty("partition").GetString()!; var expectedStateNode = state.GetProperty("state"); var expectedState = expectedStateNode.ValueKind == JsonValueKind.Null ? null : expectedStateNode.GetRawText(); - if (!expectedStates.TryAdd(partition, expectedState)) throw new InvalidOperationException("Duplicate expected state"); + if (!expectedStates.TryAdd(partition, expectedState)) + throw new InvalidOperationException("Duplicate expected state"); - if (state.TryGetProperty("result", out var expectedResultNode )) { + if (state.TryGetProperty("result", out var expectedResultNode)) { var expectedResult = expectedResultNode.ValueKind == JsonValueKind.Null ? null : expectedResultNode.GetRawText(); @@ -96,19 +90,18 @@ public static IEnumerable GetTestCases() { } } - if (stateCount > 2) throw new InvalidOperationException("Cannot specify more than 2 states"); sequence.Events.Add(new InputEvent(et! - , e.GetProperty("data").GetRawText() - , e.TryGetProperty("metadata", out var metadata) ? metadata.GetRawText() : null - , initializedPartitions - , expectedStates - , expectedResults - , skip - , e.TryGetProperty("eventId", out var idElement) && idElement.TryGetGuid(out var id) ? id : Guid.NewGuid() - )); + , e.GetProperty("data").GetRawText() + , e.TryGetProperty("metadata", out var metadata) ? metadata.GetRawText() : null + , initializedPartitions + , expectedStates + , expectedResults + , skip + , e.TryGetProperty("eventId", out var idElement) && idElement.TryGetGuid(out var id) ? id : Guid.NewGuid() + )); } } @@ -154,6 +147,7 @@ public static IEnumerable GetTestCases() { foreach (var c in item.Value.EnumerateArray()) { sdb.FromCategory(c.GetString()); } + break; case "partitioned": if (item.Value.GetBoolean()) @@ -163,6 +157,7 @@ public static IEnumerable GetTestCases() { foreach (var e in item.Value.EnumerateArray()) { sdb.IncludeEvent(e.GetString()); } + break; case "allEvents": if (item.Value.GetBoolean()) { @@ -170,11 +165,13 @@ public static IEnumerable GetTestCases() { } else { sdb.NotAllEvents(); } + break; case "allStreams": if (item.Value.GetBoolean()) { sdb.FromAll(); } + break; default: throw new Exception($"unexpected property in expected config {item.Name}"); @@ -186,14 +183,12 @@ public static IEnumerable GetTestCases() { if (output.TryGetProperty("emitted", out var expectedEmittedElement)) { foreach (var element in expectedEmittedElement.EnumerateObject()) { var stream = element.Name; - foreach (var eventElement in element.Value.EnumerateArray()) { - if (eventElement.ValueKind == JsonValueKind.String) { - expectedEmittedEvents.Add( - new OutputEvent(stream, "$>", eventElement.GetString(), null)); - } - } + expectedEmittedEvents.AddRange(from eventElement in element.Value.EnumerateArray() + where eventElement.ValueKind == JsonValueKind.String + select new OutputEvent(stream, "$>", eventElement.GetString(), null)); } } + JintProjectionStateHandler runner = null; IQuerySources definition = null; @@ -213,18 +208,26 @@ public static IEnumerable GetTestCases() { yield return For($"{projection} qs.AllEvents", () => Assert.Equal(expectedDefinition.AllEvents, definition.AllEvents)); yield return For($"{projection} qs.Events", () => Assert.Equal(expectedDefinition.Events, definition.Events)); yield return For($"{projection} qs.ByStreams", () => Assert.Equal(expectedDefinition.ByStreams, definition.ByStreams)); - yield return For($"{projection} qs.ByCustomPartitions", () => Assert.Equal(expectedDefinition.ByCustomPartitions, definition.ByCustomPartitions)); + yield return For($"{projection} qs.ByCustomPartitions", + () => Assert.Equal(expectedDefinition.ByCustomPartitions, definition.ByCustomPartitions)); yield return For($"{projection} qs.DefinesStateTransform", () => Assert.Equal(expectedDefinition.DefinesStateTransform, definition.DefinesStateTransform)); yield return For($"{projection} qs.DefinesFold", () => Assert.Equal(expectedDefinition.DefinesFold, definition.DefinesFold)); - yield return For($"{projection} qs.HandlesDeletedNotifications", () => Assert.Equal(expectedDefinition.HandlesDeletedNotifications, definition.HandlesDeletedNotifications)); - yield return For($"{projection} qs.ProducesResults", () => Assert.Equal(expectedDefinition.ProducesResults, definition.ProducesResults)); + yield return For($"{projection} qs.HandlesDeletedNotifications", + () => Assert.Equal(expectedDefinition.HandlesDeletedNotifications, definition.HandlesDeletedNotifications)); + yield return For($"{projection} qs.ProducesResults", + () => Assert.Equal(expectedDefinition.ProducesResults, definition.ProducesResults)); yield return For($"{projection} qs.IsBiState", () => Assert.Equal(expectedDefinition.IsBiState, definition.IsBiState)); - yield return For($"{projection} qs.IncludeLinksOption", () => Assert.Equal(expectedDefinition.IncludeLinksOption, definition.IncludeLinksOption)); - yield return For($"{projection} qs.ResultStreamNameOption", () => Assert.Equal(expectedDefinition.ResultStreamNameOption, definition.ResultStreamNameOption)); - yield return For($"{projection} qs.PartitionResultStreamNamePatternOption", () => Assert.Equal(expectedDefinition.PartitionResultStreamNamePatternOption, - definition.PartitionResultStreamNamePatternOption)); - yield return For($"{projection} qs.ReorderEventsOption", () => Assert.Equal(expectedDefinition.ReorderEventsOption, definition.ReorderEventsOption)); - yield return For($"{projection} qs.ProcessingLagOption", () => Assert.Equal(expectedDefinition.ProcessingLagOption, definition.ProcessingLagOption)); + yield return For($"{projection} qs.IncludeLinksOption", + () => Assert.Equal(expectedDefinition.IncludeLinksOption, definition.IncludeLinksOption)); + yield return For($"{projection} qs.ResultStreamNameOption", + () => Assert.Equal(expectedDefinition.ResultStreamNameOption, definition.ResultStreamNameOption)); + yield return For($"{projection} qs.PartitionResultStreamNamePatternOption", () + => Assert.Equal(expectedDefinition.PartitionResultStreamNamePatternOption, + definition.PartitionResultStreamNamePatternOption)); + yield return For($"{projection} qs.ReorderEventsOption", + () => Assert.Equal(expectedDefinition.ReorderEventsOption, definition.ReorderEventsOption)); + yield return For($"{projection} qs.ProcessingLagOption", + () => Assert.Equal(expectedDefinition.ProcessingLagOption, definition.ProcessingLagOption)); var partitionedState = new Dictionary(); var partitionedResult = new Dictionary(); var sharedStateInitialized = false; @@ -236,6 +239,7 @@ public static IEnumerable GetTestCases() { if (!revision.TryGetValue(sequence.Stream, out _)) { revision[sequence.Stream] = 0; } + for (int j = 0; j < sequences[i].Events.Count; j++) { var logPosition = i * 100 + j; var flags = PrepareFlags.IsJson | PrepareFlags.Data; @@ -262,6 +266,7 @@ save emitted events to verify later if (@event.Metadata != null) { metadata = Utf8NoBom.GetBytes(JObject.Parse(@event.Metadata).ToString(Formatting.Indented)); } + var er = new EventRecord( revision[sequence.Stream], logPosition, Guid.NewGuid(), @event.EventId, i, j, sequence.Stream, i, DateTime.Now, flags, @event.EventType, @@ -274,13 +279,11 @@ save emitted events to verify later var expectedPartition = ""; if (expectedDefinition.DefinesFold) { if (@event.ExpectedStates.Any()) { - expectedPartition = @event.ExpectedStates - .SingleOrDefault(x => string.Empty != x.Key).Key ?? string.Empty; + expectedPartition = @event.ExpectedStates.SingleOrDefault(x => string.Empty != x.Key).Key ?? string.Empty; if (expectedPartition != string.Empty) { yield return For( $"{projection} {er.EventNumber}@{sequence.Stream} returns expected partition", - () => Assert.Equal(expectedPartition, - runner.GetStatePartition(CheckpointTag.Empty, "", e))); + () => Assert.Equal(expectedPartition, runner.GetStatePartition(CheckpointTag.Empty, "", e))); foreach (var initializedState in @event.InitializedPartitions) { yield return For( $"should not have already initialized \"{initializedState}\" at {er.EventNumber}@{sequence.Stream}", @@ -331,7 +334,8 @@ save emitted events to verify later $"process event {@event.EventType} at {er.EventNumber}@{sequence.Stream}", () => { Assert.True(runner.ProcessEvent(expectedPartition, CheckpointTag.Empty, "", e, - out var newState, out var newSharedState, out var emittedEvents), "Process event should always return true"); + out var newState, out var newSharedState, out var emittedEvents), + "Process event should always return true"); var result = runner.TransformStateToResult(); if (newSharedState != null) { partitionedState[""] = newSharedState; @@ -340,11 +344,9 @@ save emitted events to verify later partitionedState[expectedPartition] = newState; partitionedResult[expectedPartition] = result; - if (emittedEvents != null && emittedEvents.Length > 0) { + if (emittedEvents is { Length: > 0 }) { actualEmittedEvents.AddRange(emittedEvents); } - - }); if (@event.ExpectedStates.Any()) { foreach (var expectedState in @event.ExpectedStates) { @@ -353,9 +355,10 @@ save emitted events to verify later yield return For($@"state ""{expectedState.Key}"" is correct at {er.EventNumber}@{sequence.Stream}", () => { - if (string.IsNullOrEmpty(expectedState.Value)) Assert.Null(partitionedState[expectedState.Key]); + if (string.IsNullOrEmpty(expectedState.Value)) Assert.Null(partitionedState[expectedState.Key]); else { - Assert.True(partitionedState.ContainsKey(expectedState.Key), $"partition does not contain key {expectedState.Key}"); + Assert.True(partitionedState.ContainsKey(expectedState.Key), + $"partition does not contain key {expectedState.Key}"); Assert.NotNull(partitionedState[expectedState.Key]); Assert.NotEmpty(partitionedState[expectedState.Key]); var expected = Sort(expectedState.Value, "expected"); @@ -365,17 +368,19 @@ save emitted events to verify later }); } } + if (@event.ExpectedResult.Any()) { foreach (var expectedResult in @event.ExpectedResult) { - yield return For($@"result ""{expectedResult.Key}"" exists at {er.EventNumber}@{sequence.Stream}", () => Assert.Contains(expectedResult.Key, (IReadOnlyDictionary)partitionedResult)); yield return For($@"result ""{expectedResult.Key}"" is correct at {er.EventNumber}@{sequence.Stream}", () => { - if (string.IsNullOrEmpty(expectedResult.Value)) Assert.Null(partitionedResult[expectedResult.Key]); + if (string.IsNullOrEmpty(expectedResult.Value)) + Assert.Null(partitionedResult[expectedResult.Key]); else { - Assert.True(partitionedResult.ContainsKey(expectedResult.Key), $"partition does not contain key {expectedResult.Key}"); + Assert.True(partitionedResult.ContainsKey(expectedResult.Key), + $"partition does not contain key {expectedResult.Key}"); if (expectedResult.Value is null) { Assert.Null(partitionedResult[expectedResult.Key]); } else { @@ -389,17 +394,11 @@ save emitted events to verify later }); } } - - - - } } - revision[sequence.Stream] = revision[sequence.Stream] + 1; + revision[sequence.Stream] += 1; } - - } if (expectedEmittedEvents.Count == 0) { @@ -417,17 +416,21 @@ save emitted events to verify later } object[] For(string name, Action a) { - return new object[]{ new TestDefinition(Name(name),_ => { - a(); - return default; - })}; + return new object[] { + new TestDefinition(Name(name), _ => { + a(); + return default; + }) + }; } object[] WithOutput(string name, Action a) { - return new object[]{ new TestDefinition(Name(name),o => { - a(o); - return default; - })}; + return new object[] { + new TestDefinition(Name(name), o => { + a(o); + return default; + }) + }; } string Name(string name) { @@ -444,6 +447,7 @@ static JObject Sort(string json, string name) { } catch { throw; } + return new(root.Properties().OrderBy(x => x.Name)); } } @@ -451,7 +455,7 @@ static JObject Sort(string json, string name) { [Theory] [MemberData(nameof(GetTestCases))] public Task Test(TestDefinition def) { - return def.Execute(_output).AsTask(); + return def.Execute(output).AsTask(); } public class TestDefinition { @@ -492,7 +496,6 @@ class InputEvent( IReadOnlyDictionary expectedResults, bool skip, Guid eventId) { - public string EventType { get; } = eventType; public string Body { get; } = body; public string Metadata { get; } = metadata; @@ -501,7 +504,6 @@ class InputEvent( public IReadOnlyDictionary ExpectedResult { get; } = expectedResults; public bool Skip { get; } = skip; public Guid EventId { get; } = eventId; - } class OutputEvent { diff --git a/src/KurrentDB.Projections.Core.Tests/AssertEx.cs b/src/KurrentDB.Projections.Core.Tests/AssertEx.cs index 2a0e93d9bd1..d5f110f8da5 100644 --- a/src/KurrentDB.Projections.Core.Tests/AssertEx.cs +++ b/src/KurrentDB.Projections.Core.Tests/AssertEx.cs @@ -15,7 +15,6 @@ public static void AreEqual(IQuerySources expected, IQuerySources actual) { Assert.AreEqual(expected.ByStreams, actual.ByStreams, $"Expected {nameof(expected.ByStreams)} to be {expected.ByStreams} but was {actual.ByStreams}"); Assert.AreEqual(expected.DefinesFold, actual.DefinesFold, $"Expected {nameof(expected.DefinesFold)} to be {expected.DefinesFold} but was {actual.DefinesFold}"); Assert.AreEqual(expected.DefinesStateTransform, actual.DefinesStateTransform, $"Expected {nameof(expected.DefinesStateTransform)} to be {expected.DefinesStateTransform} but was {actual.DefinesStateTransform}"); - Assert.AreEqual(expected.IncludeLinksOption, actual.IncludeLinksOption, $"Expected {nameof(expected.IncludeLinksOption)} to be {expected.IncludeLinksOption} but was {actual.IncludeLinksOption}"); Assert.AreEqual(expected.IsBiState, actual.IsBiState, $"Expected {nameof(expected.IsBiState)} to be {expected.IsBiState} but was {actual.IsBiState}"); Assert.AreEqual(expected.PartitionResultStreamNamePatternOption, actual.PartitionResultStreamNamePatternOption, $"Expected {nameof(expected.PartitionResultStreamNamePatternOption)} to be {expected.PartitionResultStreamNamePatternOption} but was {actual.PartitionResultStreamNamePatternOption}"); diff --git a/src/KurrentDB.Projections.Core.Tests/Other/claims_serialization.cs b/src/KurrentDB.Projections.Core.Tests/Other/claims_serialization.cs index 16f909172db..e94865bd80c 100644 --- a/src/KurrentDB.Projections.Core.Tests/Other/claims_serialization.cs +++ b/src/KurrentDB.Projections.Core.Tests/Other/claims_serialization.cs @@ -14,10 +14,10 @@ public class claims_serialization { [Test] public void should_serialize_principal_name() { var principalName = "foo-name"; - var claimsIdentity = new ClaimsIdentity(new Claim[] { + var claimsIdentity = new ClaimsIdentity([ new(ClaimTypes.Name, principalName), - new(ClaimTypes.Role, "$admins"), - }); + new(ClaimTypes.Role, "$admins") + ]); var runas = new ProjectionManagementMessage.RunAs(new ClaimsPrincipal(claimsIdentity)); var sra = SerializedRunAs.SerializePrincipal(runas); Assert.AreEqual(principalName, sra.Name); @@ -26,12 +26,12 @@ public void should_serialize_principal_name() { [Test] public void should_only_serialize_role() { var roleClaim = new Claim(ClaimTypes.Role, "$admins"); - var claimsIdentity = new ClaimsIdentity(new[] { + var claimsIdentity = new ClaimsIdentity([ new(ClaimTypes.Name, "foo-name"), roleClaim, new("uid", "foo-uid"), new("pwd", "foo-pwd") - }); + ]); var runas = new ProjectionManagementMessage.RunAs(new ClaimsPrincipal(claimsIdentity)); var sra = SerializedRunAs.SerializePrincipal(runas); Assert.AreEqual(1, sra.Roles.Length); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/TestFixtureWithProjectionCoreService.cs b/src/KurrentDB.Projections.Core.Tests/Services/TestFixtureWithProjectionCoreService.cs index 963459ff936..468d35b729f 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/TestFixtureWithProjectionCoreService.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/TestFixtureWithProjectionCoreService.cs @@ -86,25 +86,20 @@ public void Handle(CoreProjectionProcessingMessage.PrerecordedEventsLoaded messa [SetUp] public virtual void Setup() { - _consumer = new TestHandler(); + _consumer = new(); _bus = new(); _bus.Subscribe(_consumer); ICheckpoint writerCheckpoint = new InMemoryCheckpoint(1000); var ioDispatcher = new IODispatcher(_bus, _bus, true); - _readerService = new EventReaderCoreService(_bus, ioDispatcher, 10, writerCheckpoint, - runHeadingReader: true, faultOutOfOrderProjections: true); - _subscriptionDispatcher = - new ReaderSubscriptionDispatcher(_bus); + _readerService = new(_bus, 10, writerCheckpoint, runHeadingReader: true, faultOutOfOrderProjections: true); + _subscriptionDispatcher = new(_bus); _workerId = Guid.NewGuid(); var guardBus = new GuardBusToTriggerFixingIfUsed(); var configuration = new ProjectionsStandardComponents(1, ProjectionType.All, guardBus, guardBus, guardBus, guardBus, true, 500, 250, Opts.MaxProjectionStateSizeDefault, ProjectionTrackers.NoOp); - _service = new ProjectionCoreService( - _workerId, _bus, _bus, _subscriptionDispatcher, new RealTimeProvider(), ioDispatcher, configuration); - _bus.Subscribe( - _subscriptionDispatcher.CreateSubscriber()); - _bus.Subscribe(_subscriptionDispatcher - .CreateSubscriber()); + _service = new(_workerId, _bus, _bus, _subscriptionDispatcher, new RealTimeProvider(), ioDispatcher, configuration); + _bus.Subscribe(_subscriptionDispatcher.CreateSubscriber()); + _bus.Subscribe(_subscriptionDispatcher.CreateSubscriber()); _bus.Subscribe(_subscriptionDispatcher.CreateSubscriber()); _bus.Subscribe(_subscriptionDispatcher.CreateSubscriber()); _bus.Subscribe(_subscriptionDispatcher.CreateSubscriber()); @@ -128,7 +123,6 @@ protected IReaderStrategy CreateReaderStrategy() { 0, result.Build(), new RealTimeProvider(), - stopOnEof: false, runAs: null); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/TestFixtureWithCoreProjection.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/TestFixtureWithCoreProjection.cs index 154f2741b26..23d81c685fd 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/TestFixtureWithCoreProjection.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/TestFixtureWithCoreProjection.cs @@ -78,7 +78,6 @@ protected virtual CoreProjection SystemAccounts.System, _bus, _ioDispatcher, - _subscriptionDispatcher, _timeProvider); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/TestFixtureWithCoreProjectionCheckpointManager.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/TestFixtureWithCoreProjectionCheckpointManager.cs index f63a9b15808..2d41149480b 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/TestFixtureWithCoreProjectionCheckpointManager.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/TestFixtureWithCoreProjectionCheckpointManager.cs @@ -25,8 +25,6 @@ public abstract class TestFixtureWithCoreProjectionCheckpointManager().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); _manager.Start(CheckpointTag.FromStreamPositions(0, new Dictionary { { "pa", -1 }, { "pb", -1 } }), diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_after_the_last_checkpoint.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_after_the_last_checkpoint.cs index be12e31658c..5291c083656 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_after_the_last_checkpoint.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_after_the_last_checkpoint.cs @@ -54,7 +54,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_before_the_last_checkpoint.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_before_the_last_checkpoint.cs index 3cf610fec87..4f1dd651334 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_before_the_last_checkpoint.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_before_the_last_checkpoint.cs @@ -45,7 +45,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_in_past_epoch.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_in_past_epoch.cs index 25a3eaccba2..5c36318ffe5 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_in_past_epoch.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/when_starting_with_prerecorded_events_in_past_epoch.cs @@ -59,7 +59,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/with_multi_stream_checkpoint_manager.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/with_multi_stream_checkpoint_manager.cs index 5ef141db962..be73cb88c2d 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/with_multi_stream_checkpoint_manager.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/multi_stream/with_multi_stream_checkpoint_manager.cs @@ -56,7 +56,7 @@ public void TestFixtureSetUp() { _checkpointManager = new MultiStreamMultiOutputCheckpointManager(_bus, _projectionId, _projectionVersion, SystemAccounts.System, - _ioDispatcher, _projectionConfig, _projectionName, _positionTagger, _namingBuilder, true, true, false, + _ioDispatcher, _projectionConfig, _positionTagger, _namingBuilder, true, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); When(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_checkpoint_has_been_completed_and_requesting_checkpoint_to_stop.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_checkpoint_has_been_completed_and_requesting_checkpoint_to_stop.cs index b2a8f8451a7..ecebd664713 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_checkpoint_has_been_completed_and_requesting_checkpoint_to_stop.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_checkpoint_has_been_completed_and_requesting_checkpoint_to_stop.cs @@ -30,7 +30,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); _manager.Start(CheckpointTag.FromStreamPosition(0, "stream", 10), null); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_core_projection_checkpoint_manager_has_been_created.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_core_projection_checkpoint_manager_has_been_created.cs index 781aef5afbc..6309ff9d2d3 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_core_projection_checkpoint_manager_has_been_created.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_core_projection_checkpoint_manager_has_been_created.cs @@ -48,7 +48,7 @@ public void ready_for_checkpoint_throws_invalid_operation_exception() { [Test] public void can_begin_load_state() { - _checkpointWriter.StartFrom(CheckpointTag.FromPosition(0, 0, -1), ExpectedVersion.NoStream); + _checkpointWriter.StartFrom(ExpectedVersion.NoStream); } [Test] diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_default_checkpoint_manager_has_been_reinitialized.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_default_checkpoint_manager_has_been_reinitialized.cs index a9833dadea1..074742f8fdf 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_default_checkpoint_manager_has_been_reinitialized.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_a_default_checkpoint_manager_has_been_reinitialized.cs @@ -29,7 +29,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); _manager.Start(CheckpointTag.FromStreamPosition(0, "stream", 10), null); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_creating_a_default_checkpoint_manager.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_creating_a_default_checkpoint_manager.cs index d42142f4005..13a30bf2480 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_creating_a_default_checkpoint_manager.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_creating_a_default_checkpoint_manager.cs @@ -27,18 +27,14 @@ protected override void When() { [Test] public void it_can_be_created() { _manager = new DefaultCheckpointManager( - _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, - "projection", new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, - _producesResults, _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); } [Test] public void null_publisher_throws_argument_null_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( - null, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, - "projection", new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, - _producesResults, _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + null, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } @@ -46,9 +42,8 @@ public void null_publisher_throws_argument_null_exception() { public void null_io_dispatcher_throws_argument_null_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( - _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, null, _config, "projection", - new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _producesResults, - _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, null, _config, + new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } @@ -57,9 +52,7 @@ public void null_projection_config_throws_argument_null_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, null, - "projection", - new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _producesResults, - _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } @@ -67,9 +60,8 @@ public void null_projection_config_throws_argument_null_exception() { public void null_projection_name_throws_argument_null_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( - _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, null, - new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _producesResults, - _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, + new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } @@ -77,9 +69,7 @@ public void null_projection_name_throws_argument_null_exception() { public void null_position_tagger_throws_argument_null_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( - _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, - "projection", null, _namingBuilder, _checkpointsEnabled, _producesResults, - _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, null, _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } @@ -87,9 +77,8 @@ public void null_position_tagger_throws_argument_null_exception() { public void empty_projection_checkpoint_stream_id_throws_argument_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( - _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, "", - new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _producesResults, - _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, + new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } @@ -97,9 +86,8 @@ public void empty_projection_checkpoint_stream_id_throws_argument_exception() { public void empty_projection_name_throws_argument_exception() { Assert.Throws(() => { _manager = new DefaultCheckpointManager( - _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, "", - new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _producesResults, - _definesFold, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); + _bus, _projectionCorrelationId, new ProjectionVersion(1, 0, 0), null, _ioDispatcher, _config, + new StreamPositionTagger(0, "stream"), _namingBuilder, _checkpointsEnabled, _coreProjectionCheckpointWriter, Opts.MaxProjectionStateSizeDefault); }); } } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_multiple_event_processed_received_the_core_projection_checkpoint_manager.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_multiple_event_processed_received_the_core_projection_checkpoint_manager.cs index dc1668f1bab..53215b3e8ab 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_multiple_event_processed_received_the_core_projection_checkpoint_manager.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_multiple_event_processed_received_the_core_projection_checkpoint_manager.cs @@ -29,7 +29,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); _manager.Start(CheckpointTag.FromStreamPosition(0, "stream", 10), null); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_projection_state_is_too_large.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_projection_state_is_too_large.cs index 6d82217c9ad..1c8f152b9a4 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_projection_state_is_too_large.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_projection_state_is_too_large.cs @@ -31,7 +31,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); // Initial checkpoint and state diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_starting_the_core_projection_checkpoint_manager.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_starting_the_core_projection_checkpoint_manager.cs index 0e16ce5a19b..179852d3bf2 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_starting_the_core_projection_checkpoint_manager.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/checkpoint_manager/when_starting_the_core_projection_checkpoint_manager.cs @@ -27,7 +27,7 @@ protected override void When() { _checkpointReader.BeginLoadState(); var checkpointLoaded = _consumer.HandledMessages.OfType().First(); - _checkpointWriter.StartFrom(checkpointLoaded.CheckpointTag, checkpointLoaded.CheckpointEventNumber); + _checkpointWriter.StartFrom(checkpointLoaded.CheckpointEventNumber); _manager.BeginLoadPrerecordedEvents(checkpointLoaded.CheckpointTag); _manager.Start(CheckpointTag.FromStreamPosition(0, "stream", 10), null); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/multi_phase/specification_with_multi_phase_core_projection.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/multi_phase/specification_with_multi_phase_core_projection.cs index b3c815f8fb3..3c65eaecedf 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/multi_phase/specification_with_multi_phase_core_projection.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/multi_phase/specification_with_multi_phase_core_projection.cs @@ -353,8 +353,8 @@ public IReaderSubscription CreateReaderSubscription( } public IEventReader CreatePausedEventReader( - Guid eventReaderId, IPublisher publisher, IODispatcher ioDispatcher, CheckpointTag checkpointTag, - bool stopOnEof, int? stopAfterNEvents) { + Guid eventReaderId, IPublisher publisher, CheckpointTag checkpointTag, + bool stopOnEof) { throw new NotImplementedException(); } } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_creating_a_projection.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_creating_a_projection.cs index da8e3a8cb28..59a4e5f448a 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_creating_a_projection.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_creating_a_projection.cs @@ -55,7 +55,6 @@ public void SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -82,7 +81,6 @@ public void a_negative_checkpoint_handled_interval_throws_argument_out_of_range_ SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -107,7 +105,6 @@ public void a_null_io_dispatcher__throws_argument_null_exception() { SystemAccounts.System, new FakePublisher(), null, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -132,7 +129,6 @@ public void a_null_name_throws_argument_null_excveption() { SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -157,7 +153,6 @@ public void a_null_publisher_throws_exception() { SystemAccounts.System, null, _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -182,7 +177,6 @@ public void a_null_input_queue_throws_exception() { SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -206,7 +200,6 @@ public void a_null_run_as_does_not_throw_exception() { null, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); } @@ -230,7 +223,6 @@ public void a_null_subscription_dispatcher__throws_argument_null_exception() { SystemAccounts.System, new FakePublisher(), _ioDispatcher, - null, new RealTimeProvider()); }); } @@ -255,7 +247,6 @@ public void a_null_time_provider__throws_argument_null_exception() { SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, null); }); } @@ -282,7 +273,6 @@ public void a_zero_checkpoint_handled_threshold_throws_argument_out_of_range_exc SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } @@ -307,7 +297,6 @@ public void an_empty_name_throws_argument_exception() { SystemAccounts.System, new FakePublisher(), _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); }); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_starting_a_projection.cs b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_starting_a_projection.cs index 468f770de51..8b469e870d5 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_starting_a_projection.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/core_projection/when_starting_a_projection.cs @@ -71,7 +71,6 @@ public void setup() { SystemAccounts.System, _bus, _ioDispatcher, - _subscriptionDispatcher, new RealTimeProvider()); _coreProjection.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/a_checkpoint_requested_on_a_non_started_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/a_checkpoint_requested_on_a_non_started_stream.cs index 81fceabbefa..17dabff1a6f 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/a_checkpoint_requested_on_a_non_started_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/a_checkpoint_requested_on_a_non_started_stream.cs @@ -26,7 +26,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); try { _stream.Checkpoint(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs index 98cb446d4fc..95a4ea6c8b2 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs @@ -37,8 +37,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 2, 2), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 0, -1), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_at_the_same_position.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_at_the_same_position.cs index 2e24d977f9f..2e8fd3a97d1 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_at_the_same_position.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_at_the_same_position.cs @@ -63,8 +63,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 2, 2), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 100, 50), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 100, 50), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents(CreateEventBatch()); OneWriteCompletes(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_in_different_epochs.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_in_different_epochs.cs index 35386eecf33..9f72eb3e9d1 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_in_different_epochs.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_epoch/when_handling_emits_with_previously_written_events_in_different_epochs.cs @@ -58,8 +58,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 2, 2), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 20, 10), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 20, 10), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents(CreateEventBatch()); OneWriteCompletes(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_projection/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_projection/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs index bce9ce6e261..c39b675145c 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_projection/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/another_projection/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs @@ -34,8 +34,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 2, 2), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 0, -1), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_completes_before_a_timeout_in_recovery.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_completes_before_a_timeout_in_recovery.cs index 2e31cddbacd..b9ff5e29a27 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_completes_before_a_timeout_in_recovery.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_completes_before_a_timeout_in_recovery.cs @@ -42,8 +42,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( new[] { diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_times_out_in_recovery.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_times_out_in_recovery.cs index e1506c9a29f..b77f032a36b 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_times_out_in_recovery.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_a_read_times_out_in_recovery.cs @@ -42,8 +42,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( new[] { diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested.cs index 14f37ce3013..0a3f0263cde 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested.cs @@ -25,7 +25,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); _stream.Checkpoint(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_but_disabled.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_but_disabled.cs index 1b50d0f5e2f..8c39e3bb9c7 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_but_disabled.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_but_disabled.cs @@ -25,7 +25,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler, noCheckpoints: true); _stream.Start(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_all_writes_already_completed.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_all_writes_already_completed.cs index 502060c346e..c44ac5d6896 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_all_writes_already_completed.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_all_writes_already_completed.cs @@ -38,7 +38,7 @@ public void Setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_pending_writes.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_pending_writes.cs index 5ec1e5038c1..5d085f776ab 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_pending_writes.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_checkpoint_requested_with_pending_writes.cs @@ -38,7 +38,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_creating_an_emitted_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_creating_an_emitted_stream.cs index 31825af4768..210d90f0885 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_creating_an_emitted_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_creating_an_emitted_stream.cs @@ -33,7 +33,7 @@ public void null_stream_id_throws_argument_null_exception() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _fakePublisher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, new TestCheckpointManagerMessageHandler()); }); @@ -44,7 +44,7 @@ public void null_writer_configuration_throws_argument_null_exception() { Assert.Throws(() => { new EmittedStream( null, null, new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _fakePublisher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, new TestCheckpointManagerMessageHandler()); }); @@ -58,7 +58,7 @@ public void empty_stream_id_throws_argument_exception() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _fakePublisher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, new TestCheckpointManagerMessageHandler()); }); @@ -72,7 +72,7 @@ public void null_from_throws_argument_exception() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), null, _fakePublisher, _ioDispatcher, + new TransactionFilePositionTagger(0), null, _ioDispatcher, new TestCheckpointManagerMessageHandler()); }); } @@ -85,7 +85,7 @@ public void null_publisher_throws_argument_null_exception() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), null, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, new TestCheckpointManagerMessageHandler()); }); } @@ -98,7 +98,7 @@ public void null_io_dispatcher_throws_argument_null_exception() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _fakePublisher, null, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), null, new TestCheckpointManagerMessageHandler()); }); } @@ -111,7 +111,7 @@ public void null_ready_handler_throws_argumenbt_null_exception() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _fakePublisher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, null); }); } @@ -122,7 +122,7 @@ public void it_can_be_created() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _fakePublisher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, new TestCheckpointManagerMessageHandler()); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_a_timeout.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_a_timeout.cs index c2abbd3c756..3e1c86c603e 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_a_timeout.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_a_timeout.cs @@ -54,8 +54,7 @@ public void setup() { "test_stream", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), - new ProjectionVersion(1, 2, 2), new TransactionFilePositionTagger(0), CheckpointTag.Empty, - _bus, _ioDispatcher, _readyHandler); + new ProjectionVersion(1, 2, 2), new TransactionFilePositionTagger(0), CheckpointTag.Empty, _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents(CreateEventBatch()); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_not_started_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_not_started_stream.cs index 63ec1704428..eb6ad7988f9 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_not_started_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_not_started_stream.cs @@ -35,7 +35,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.EmitEvents( new[] { diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_started_in_recovery_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_started_in_recovery_stream.cs index 496bbaa9931..da860343d24 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_started_in_recovery_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_the_started_in_recovery_stream.cs @@ -34,8 +34,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_to_the_nonexisting_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_to_the_nonexisting_stream.cs index 078951a1ea2..91285fc51fb 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_to_the_nonexisting_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_to_the_nonexisting_stream.cs @@ -36,8 +36,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_caused_by_and_correlation_id.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_caused_by_and_correlation_id.cs index 0a25c97b4d4..c53c52bc439 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_caused_by_and_correlation_id.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_caused_by_and_correlation_id.cs @@ -52,8 +52,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents(new[] { _emittedDataEvent }); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_committed_callback.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_committed_callback.cs index f9af6f90d54..2a2023c895c 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_committed_callback.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_committed_callback.cs @@ -32,8 +32,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 0, -1), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs index 58f5e5081ee..1918e452686 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_expected_tag_the_started_in_recovery_stream.cs @@ -34,8 +34,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 0, -1), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_extra_metadata.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_extra_metadata.cs index 2c0178aba13..73faa18b675 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_extra_metadata.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_extra_metadata.cs @@ -38,8 +38,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_not_ready_event.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_not_ready_event.cs index 958f4f173fc..15da95e8363 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_not_ready_event.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_not_ready_event.cs @@ -36,8 +36,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 0, -1), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_stream_metadata.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_stream_metadata.cs index c34163010c5..f1b103d2bfc 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_stream_metadata.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_stream_metadata.cs @@ -37,7 +37,7 @@ public void setup() { _stream = new EmittedStream( "test_stream", _writerConfiguration, new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( new[] { diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_write_as_configured.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_write_as_configured.cs index 618069ee1b2..33e07fe58e7 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_write_as_configured.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_an_emit_with_write_as_configured.cs @@ -37,8 +37,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), _writeAs, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_in_invalid_order.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_in_invalid_order.cs index 5b038c26b42..3f98d642f37 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_in_invalid_order.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_in_invalid_order.cs @@ -31,8 +31,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 40, 30), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 40, 30), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents( new[] { diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events.cs index 3b37d6048c4..d60fff96134 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events.cs @@ -36,7 +36,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 100, 50), _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 100, 50), _ioDispatcher, _readyHandler); _stream.Start(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events_at_the_same_position.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events_at_the_same_position.cs index a130396ed37..a35c4e2cb6e 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events_at_the_same_position.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_handling_emits_with_previously_written_events_at_the_same_position.cs @@ -57,8 +57,7 @@ public void setup() { new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, maxWriteBatchLength: 50), new ProjectionVersion(1, 0, 0), new TransactionFilePositionTagger(0), - CheckpointTag.FromPosition(0, 20, 10), - _bus, _ioDispatcher, _readyHandler); + CheckpointTag.FromPosition(0, 20, 10), _ioDispatcher, _readyHandler); _stream.Start(); _stream.EmitEvents(CreateEventBatch()); OneWriteCompletes(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started.cs index d089ee85a6e..31a0e7b5a64 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started.cs @@ -23,7 +23,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); ; _stream.Start(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started_with_already_emitted_events.cs b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started_with_already_emitted_events.cs index f2845ef423f..09cd974a125 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started_with_already_emitted_events.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/emitted_stream/when_the_stream_is_started_with_already_emitted_events.cs @@ -34,7 +34,7 @@ public void setup() { "test", new EmittedStream.WriterConfiguration(new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), null, 50), new ProjectionVersion(1, 0, 0), - new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _bus, _ioDispatcher, + new TransactionFilePositionTagger(0), CheckpointTag.FromPosition(0, 0, -1), _ioDispatcher, _readyHandler); _stream.EmitEvents( new[] { diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_filter/TestFixtureWithEventFilter.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_filter/TestFixtureWithEventFilter.cs index 184e604e141..86b5a11db29 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_filter/TestFixtureWithEventFilter.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_filter/TestFixtureWithEventFilter.cs @@ -29,7 +29,7 @@ protected virtual void When() { try { var sources = _builder.Build(); _ef = - ReaderStrategy.Create("test", 0, sources, new RealTimeProvider(), stopOnEof: false, runAs: null) + ReaderStrategy.Create("test", 0, sources, new RealTimeProvider(), runAs: null) .EventFilter; } catch (Exception ex) { _exception = ex; diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/TestFixtureWithEventReaderService.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/TestFixtureWithEventReaderService.cs index 91663704580..2596569cf56 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/TestFixtureWithEventReaderService.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/TestFixtureWithEventReaderService.cs @@ -30,7 +30,7 @@ public void Setup() { ICheckpoint writerCheckpoint = new InMemoryCheckpoint(1000); _readerService = new EventReaderCoreService( - GetInputQueue(), _ioDispatcher, 10, writerCheckpoint, runHeadingReader: GivenHeadingReaderRunning(), + GetInputQueue(), 10, writerCheckpoint, runHeadingReader: GivenHeadingReaderRunning(), faultOutOfOrderProjections: true); _subscriptionDispatcher = new ReaderSubscriptionDispatcher(GetInputQueue()); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_including_links.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_including_links.cs index fe6e1d3b0db..58f0aa709b8 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_including_links.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_including_links.cs @@ -51,7 +51,6 @@ protected override void Given() { 0, _sourceDefinition, _timeProvider, - stopOnEof: true, runAs: null); _readerSubscriptionOptions = new ReaderSubscriptionOptions( checkpointUnhandledBytesThreshold: 10000, checkpointProcessedEventsThreshold: 100, diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_not_including_links.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_not_including_links.cs index 6e19d4d5517..c0afd3b9b40 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_not_including_links.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/all_streams_with_links_event_reader/when_not_including_links.cs @@ -51,7 +51,6 @@ protected override void Given() { 0, _sourceDefinition, _timeProvider, - stopOnEof: true, runAs: null); _readerSubscriptionOptions = new ReaderSubscriptionOptions( checkpointUnhandledBytesThreshold: 10000, checkpointProcessedEventsThreshold: 100, diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/index_checkpoint.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/index_checkpoint.cs index 013e62156f5..55205b392e9 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/index_checkpoint.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/index_checkpoint.cs @@ -49,7 +49,6 @@ protected override void Given() { 0, _sourceDefinition, _timeProvider, - stopOnEof: false, runAs: null); _readerSubscriptionOptions = new ReaderSubscriptionOptions( checkpointUnhandledBytesThreshold: 10000, checkpointProcessedEventsThreshold: 100, diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_one_event_type_has_been_never_emitted.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_one_event_type_has_been_never_emitted.cs index e4afc5439d3..be042218af4 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_one_event_type_has_been_never_emitted.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_one_event_type_has_been_never_emitted.cs @@ -55,7 +55,6 @@ protected override void Given() { 0, _sourceDefinition, _timeProvider, - stopOnEof: false, runAs: null); _readerSubscriptionOptions = new ReaderSubscriptionOptions( checkpointUnhandledBytesThreshold: 10000, checkpointProcessedEventsThreshold: 100, diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_reordering_happens_in_event_by_type_index.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_reordering_happens_in_event_by_type_index.cs index 17b29c8418e..de4b6f53d66 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_reordering_happens_in_event_by_type_index.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_by_type_index_reader/catching_up/when_reordering_happens_in_event_by_type_index.cs @@ -50,7 +50,6 @@ protected override void Given() { 0, _sourceDefinition, _timeProvider, - stopOnEof: false, runAs: null); _readerSubscriptionOptions = new ReaderSubscriptionOptions( diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_reader_core_service/when_handling_subscribe_requests.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_reader_core_service/when_handling_subscribe_requests.cs index e26831495bf..1b4febba76a 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_reader_core_service/when_handling_subscribe_requests.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/event_reader_core_service/when_handling_subscribe_requests.cs @@ -93,8 +93,8 @@ public IReaderSubscription CreateReaderSubscription(IPublisher publisher, Checkp return new FakeReaderSubscription(); } - public IEventReader CreatePausedEventReader(Guid eventReaderId, IPublisher publisher, IODispatcher ioDispatcher, - CheckpointTag checkpointTag, bool stopOnEof, int? stopAfterNEvents) { + public IEventReader CreatePausedEventReader(Guid eventReaderId, IPublisher publisher, + CheckpointTag checkpointTag, bool stopOnEof) { throw new NotImplementedException(); } } @@ -130,7 +130,7 @@ public void Handle(ReaderSubscriptionMessage.ReportProgress message) { public string Tag { get; } public Guid SubscriptionId { get; } - public IEventReader CreatePausedEventReader(IPublisher publisher, IODispatcher ioDispatcher, Guid forkedEventReaderId) { + public IEventReader CreatePausedEventReader(IPublisher publisher, Guid forkedEventReaderId) { throw new ArgumentException(nameof(FakeReaderSubscriptionThatThrows)); } } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/heading_event_reader/FakeReaderSubscription.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/heading_event_reader/FakeReaderSubscription.cs index e106c4c847c..ef67ec6d219 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/heading_event_reader/FakeReaderSubscription.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/heading_event_reader/FakeReaderSubscription.cs @@ -122,8 +122,7 @@ public FakeEventReader EventReader { get { return _eventReader; } } - public IEventReader CreatePausedEventReader( - IPublisher publisher, IODispatcher ioDispatcher, Guid forkedEventReaderId) { + public IEventReader CreatePausedEventReader(IPublisher publisher, Guid forkedEventReaderId) { _eventReader = new FakeEventReader(forkedEventReaderId); return _eventReader; } @@ -159,8 +158,8 @@ public Guid EventReaderId { get { return _subscription.EventReader.EventReaderId; } } - public IEventReader CreatePausedEventReader(Guid eventReaderId, IPublisher publisher, IODispatcher ioDispatcher, - CheckpointTag checkpointTag, bool stopOnEof, int? stopAfterNEvents) { + public IEventReader CreatePausedEventReader(Guid eventReaderId, IPublisher publisher, + CheckpointTag checkpointTag, bool stopOnEof) { throw new NotImplementedException(); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/reordering.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/reordering.cs index ce0c5f73a04..5877ddd1cb3 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/reordering.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/reordering.cs @@ -44,7 +44,6 @@ protected override void Given() { 0, _sourceDefinition, _timeProvider, - stopOnEof: false, runAs: null); _readerSubscriptionOptions = new ReaderSubscriptionOptions( diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_creating.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_creating.cs index 52bfc2812f3..a16a95b5470 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_creating.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_creating.cs @@ -27,38 +27,34 @@ public void setup() { [Test] public void it_can_be_created() { - new MultiStreamEventReader( - _ioDispatcher, _bus, Guid.NewGuid(), null, 0, _abStreams, _ab12Tag, false, _timeProvider); + new MultiStreamEventReader(_bus, Guid.NewGuid(), null, 0, _abStreams, _ab12Tag, false, _timeProvider); } [Test] public void null_publisher_throws_argument_null_exception() { Assert.Throws(() => { - new MultiStreamEventReader( - _ioDispatcher, null, Guid.NewGuid(), null, 0, _abStreams, _ab12Tag, false, _timeProvider); + new MultiStreamEventReader(null, Guid.NewGuid(), null, 0, _abStreams, _ab12Tag, false, _timeProvider); }); } [Test] public void empty_event_reader_id_throws_argument_exception() { Assert.Throws(() => { - new MultiStreamEventReader( - _ioDispatcher, _bus, Guid.Empty, null, 0, _abStreams, _ab12Tag, false, _timeProvider); + new MultiStreamEventReader(_bus, Guid.Empty, null, 0, _abStreams, _ab12Tag, false, _timeProvider); }); } [Test] public void null_streams_throws_argument_null_exception() { Assert.Throws(() => { - new MultiStreamEventReader( - _ioDispatcher, _bus, Guid.NewGuid(), null, 0, null, _ab12Tag, false, _timeProvider); + new MultiStreamEventReader(_bus, Guid.NewGuid(), null, 0, null, _ab12Tag, false, _timeProvider); }); } [Test] public void null_time_provider_throws_argument_null_exception() { Assert.Throws(() => { - new MultiStreamEventReader(_ioDispatcher, _bus, Guid.NewGuid(), null, 0, _abStreams, _ab12Tag, false, + new MultiStreamEventReader(_bus, Guid.NewGuid(), null, 0, _abStreams, _ab12Tag, false, null); }); } @@ -66,16 +62,14 @@ public void null_time_provider_throws_argument_null_exception() { [Test] public void empty_streams_throws_argument_exception() { Assert.Throws(() => { - new MultiStreamEventReader( - _ioDispatcher, _bus, Guid.NewGuid(), null, 0, new string[0], _ab12Tag, false, _timeProvider); + new MultiStreamEventReader(_bus, Guid.NewGuid(), null, 0, new string[0], _ab12Tag, false, _timeProvider); }); } [Test] public void invalid_from_tag_throws_argument_exception() { Assert.Throws(() => { - new MultiStreamEventReader( - _ioDispatcher, _bus, Guid.NewGuid(), null, 0, _abStreams, + new MultiStreamEventReader(_bus, Guid.NewGuid(), null, 0, _abStreams, new Dictionary { { "a", 1 }, { "c", 2 } }, false, _timeProvider); }); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_deleted_streams.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_deleted_streams.cs index 46e8f3c41c9..239b647ed3b 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_deleted_streams.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_deleted_streams.cs @@ -37,8 +37,7 @@ protected override void Given() { public new void When() { _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _streamNames, _streamPositions, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _streamNames, _streamPositions, false, new RealTimeProvider()); _edp.Resume(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_eof_for_all_streams_and_idle_eof.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_eof_for_all_streams_and_idle_eof.cs index 7c8ebea2ee9..8a50a93fa85 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_eof_for_all_streams_and_idle_eof.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_eof_for_all_streams_and_idle_eof.cs @@ -42,8 +42,7 @@ protected override void Given() { _distibutionPointCorrelationId = Guid.NewGuid(); _fakeTimeProvider = new FakeTimeProvider(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, _fakeTimeProvider); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed.cs index d5191247633..094b1a74228 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed.cs @@ -39,8 +39,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_and_no_stream.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_and_no_stream.cs index 1c6f97568a4..6e219827d5a 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_and_no_stream.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_and_no_stream.cs @@ -39,8 +39,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams.cs index 8bbbb7a894d..86e715c6179 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams.cs @@ -40,8 +40,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_after_pause_requested.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_after_pause_requested.cs index 4023a7a4f07..ae95a47a5a2 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_after_pause_requested.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_after_pause_requested.cs @@ -39,8 +39,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_and_eofs.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_and_eofs.cs index 83e1765013b..e5b8f141081 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_and_eofs.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_and_eofs.cs @@ -41,8 +41,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_then_pause_requested_then_eof.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_then_pause_requested_then_eof.cs index 8152617352c..ebf1bcd0b9d 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_then_pause_requested_then_eof.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_read_completed_for_all_streams_then_pause_requested_then_eof.cs @@ -42,8 +42,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_after_event_zero.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_after_event_zero.cs index e2337b2391d..ddcc16599c7 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_after_event_zero.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_after_event_zero.cs @@ -38,8 +38,7 @@ protected override void Given() { public new void When() { _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _streamNames, _streamPositions, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _streamNames, _streamPositions, false, new RealTimeProvider()); _edp.Resume(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_at_event_zero.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_at_event_zero.cs index 314f1cee402..299eb71e45e 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_at_event_zero.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_handling_streams_with_deleted_events_and_reader_starting_at_event_zero.cs @@ -38,8 +38,7 @@ protected override void Given() { public new void When() { _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _streamNames, _streamPositions, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _streamNames, _streamPositions, false, new RealTimeProvider()); _edp.Resume(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_has_been_created.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_has_been_created.cs index 418004f78fa..373d1335dfd 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_has_been_created.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_has_been_created.cs @@ -28,8 +28,7 @@ public class when_has_been_created : TestFixtureWithExist _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_onetime_reader_handles_eof.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_onetime_reader_handles_eof.cs index 2932d49ffdb..501af443524 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_onetime_reader_handles_eof.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_onetime_reader_handles_eof.cs @@ -41,8 +41,7 @@ protected override void Given() { _distibutionPointCorrelationId = Guid.NewGuid(); _fakeTimeProvider = new FakeTimeProvider(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, _fakeTimeProvider, stopOnEof: true); _edp.Resume(); _firstEventId = Guid.NewGuid(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_completes_before_timeout.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_completes_before_timeout.cs index ab0fa4ca89f..e174babf366 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_completes_before_timeout.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_completes_before_timeout.cs @@ -36,8 +36,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _eventReader = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _eventReader = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _eventReader.Resume(); var correlationId = _consumer.HandledMessages.OfType() diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_for_one_stream_completes_but_times_out_for_another.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_for_one_stream_completes_but_times_out_for_another.cs index f740c53f77d..e93fcd221f0 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_for_one_stream_completes_but_times_out_for_another.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_for_one_stream_completes_but_times_out_for_another.cs @@ -35,8 +35,7 @@ protected override void Given() { _ab12Tag = new Dictionary { { "a", 1 }, { "b", 2 } }; _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _eventReader = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _eventReader = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _eventReader.Resume(); var correlationId = _consumer.HandledMessages.OfType() diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_timeout_occurs.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_timeout_occurs.cs index d60f3dbb44e..311488363c7 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_timeout_occurs.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_read_timeout_occurs.cs @@ -38,8 +38,7 @@ protected override void Given() { _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _eventReader = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _eventReader = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _eventReader.Resume(); _streamReadACorrelationId = _consumer.HandledMessages.OfType() diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_resuming.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_resuming.cs index ef2bd26c68b..c40e1154d2f 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_resuming.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/multi_stream_reader/when_resuming.cs @@ -32,8 +32,7 @@ public class when_resuming : TestFixtureWithExistingEvent _abStreams = new[] { "a", "b" }; _distibutionPointCorrelationId = Guid.NewGuid(); - _edp = new MultiStreamEventReader( - _ioDispatcher, _bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, + _edp = new MultiStreamEventReader(_bus, _distibutionPointCorrelationId, null, 0, _abStreams, _ab12Tag, false, new RealTimeProvider()); _edp.Resume(); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/reader_subscription_dispatcher.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/reader_subscription_dispatcher.cs index f6b26f62477..b90a560b397 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reader/reader_subscription_dispatcher.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reader/reader_subscription_dispatcher.cs @@ -74,7 +74,7 @@ public void should_publish_subscription_messages_to_subscribed_handler() { _readerService.Handle(new ReaderSubscriptionMessage.CommittedEventDistributed( assignedReaderMessage.ReaderId, - CreateFakeEvent(), CheckpointTag.Empty)); + CreateFakeEvent())); _queue.Process(); var committedEventReceived = _handler.HandledMessages .OfType() @@ -97,7 +97,7 @@ public void should_not_send_subscription_messages_to_cancelled_handlers() { _readerService.Handle(new ReaderSubscriptionMessage.CommittedEventDistributed( assignedReaderMessage.ReaderId, - CreateFakeEvent(), CheckpointTag.Empty)); + CreateFakeEvent())); _queue.Process(); Assert.IsEmpty(_handler.HandledMessages.OfType()); diff --git a/src/KurrentDB.Projections.Core.Tests/Services/event_reordering_projection_subscription/when_creating_projection_subscription.cs b/src/KurrentDB.Projections.Core.Tests/Services/event_reordering_projection_subscription/when_creating_projection_subscription.cs index 7cd236ce564..237d6d101a6 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/event_reordering_projection_subscription/when_creating_projection_subscription.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/event_reordering_projection_subscription/when_creating_projection_subscription.cs @@ -98,7 +98,6 @@ private IReaderStrategy CreateReaderStrategy() { 0, result.Build(), new RealTimeProvider(), - stopOnEof: false, runAs: null); } } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/TestFixtureWithProjectionSubscription.cs b/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/TestFixtureWithProjectionSubscription.cs index 472c80890fd..867056a56ee 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/TestFixtureWithProjectionSubscription.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/TestFixtureWithProjectionSubscription.cs @@ -61,9 +61,7 @@ public void setup() { } protected virtual IReaderSubscription CreateProjectionSubscription() { - return new ReaderSubscription( - "Test Subscription", - _bus, + return new ReaderSubscription(_bus, _projectionCorrelationId, _readerStrategy.PositionTagger.MakeZeroCheckpointTag(), _readerStrategy, @@ -98,7 +96,6 @@ protected virtual IReaderStrategy CreateCheckpointStrategy() { 0, sources, _timeProvider, - stopOnEof: false, runAs: config.RunAs); return readerStrategy; } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_creating_projection_subscription.cs b/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_creating_projection_subscription.cs index e72440b661f..262bee123d6 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_creating_projection_subscription.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_creating_projection_subscription.cs @@ -17,9 +17,7 @@ namespace KurrentDB.Projections.Core.Tests.Services.projection_subscription; public class when_creating_projection_subscription { [Test] public void it_can_be_created() { - new ReaderSubscription( - "Test Subscription", - new FakePublisher(), + new ReaderSubscription(new FakePublisher(), Guid.NewGuid(), CheckpointTag.FromPosition(0, 0, -1), CreateReaderStrategy(), @@ -35,9 +33,7 @@ public void it_can_be_created() { [Test] public void null_publisher_throws_argument_null_exception() { Assert.Throws(() => { - new ReaderSubscription( - "Test Subscription", - null, + new ReaderSubscription(null, Guid.NewGuid(), CheckpointTag.FromPosition(0, 0, -1), CreateReaderStrategy(), @@ -54,9 +50,7 @@ public void null_publisher_throws_argument_null_exception() { [Test] public void null_checkpoint_strategy_throws_argument_null_exception() { Assert.Throws(() => { - new ReaderSubscription( - "Test Subscription", - new FakePublisher(), + new ReaderSubscription(new FakePublisher(), Guid.NewGuid(), CheckpointTag.FromPosition(0, 0, -1), null, @@ -73,9 +67,7 @@ public void null_checkpoint_strategy_throws_argument_null_exception() { [Test] public void null_time_provider_throws_argument_null_exception() { Assert.Throws(() => { - new ReaderSubscription( - "Test Subscription", - new FakePublisher(), + new ReaderSubscription(new FakePublisher(), Guid.NewGuid(), CheckpointTag.FromPosition(0, 0, -1), CreateReaderStrategy(), @@ -98,7 +90,6 @@ private IReaderStrategy CreateReaderStrategy() { 0, result.Build(), new RealTimeProvider(), - stopOnEof: false, runAs: null); } } diff --git a/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_handling_events_with_content_type_validation.cs b/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_handling_events_with_content_type_validation.cs index adf1b6f56ba..6a19d4d7b0c 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_handling_events_with_content_type_validation.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/projection_subscription/when_handling_events_with_content_type_validation.cs @@ -29,9 +29,7 @@ protected override void When() { } protected override IReaderSubscription CreateProjectionSubscription() { - return new ReaderSubscription( - "Test Subscription", - _bus, + return new ReaderSubscription(_bus, _projectionCorrelationId, _readerStrategy.PositionTagger.MakeZeroCheckpointTag(), _readerStrategy, @@ -69,9 +67,7 @@ protected override void When() { } protected override IReaderSubscription CreateProjectionSubscription() { - return new ReaderSubscription( - "Test Subscription", - _bus, + return new ReaderSubscription(_bus, _projectionCorrelationId, _readerStrategy.PositionTagger.MakeZeroCheckpointTag(), _readerStrategy, @@ -120,9 +116,7 @@ protected override void When() { } protected override IReaderSubscription CreateProjectionSubscription() { - return new ReaderSubscription( - "Test Subscription", - _bus, + return new ReaderSubscription(_bus, _projectionCorrelationId, _readerStrategy.PositionTagger.MakeZeroCheckpointTag(), _readerStrategy, diff --git a/src/KurrentDB.Projections.Core.Tests/Services/projections_manager/TestFixtureWithProjectionCoreAndManagementServices.cs b/src/KurrentDB.Projections.Core.Tests/Services/projections_manager/TestFixtureWithProjectionCoreAndManagementServices.cs index ca44e80c9b3..16c0984d619 100644 --- a/src/KurrentDB.Projections.Core.Tests/Services/projections_manager/TestFixtureWithProjectionCoreAndManagementServices.cs +++ b/src/KurrentDB.Projections.Core.Tests/Services/projections_manager/TestFixtureWithProjectionCoreAndManagementServices.cs @@ -148,9 +148,9 @@ public void Setup() { } protected virtual Tuple[] GivenProcessingQueues() { - return new[] { + return [ Tuple.Create(_bus, GetInputQueue(), (SynchronousScheduler)null, Guid.NewGuid()) - }; + ]; } private void SetUpCoreServices( @@ -158,15 +158,14 @@ private void SetUpCoreServices( SynchronousScheduler bus, IPublisher inputQueue, SynchronousScheduler output_) { - var output = (output_ ?? inputQueue); + var output = output_ ?? inputQueue; ICheckpoint writerCheckpoint = new InMemoryCheckpoint(1000); var readerService = new EventReaderCoreService( output, - _ioDispatcher, 10, writerCheckpoint, runHeadingReader: true, faultOutOfOrderProjections: true); - _subscriptionDispatcher = new ReaderSubscriptionDispatcher(inputQueue); + _subscriptionDispatcher = new(inputQueue); bus.Subscribe( _subscriptionDispatcher.CreateSubscriber()); diff --git a/src/KurrentDB.Projections.Core.XUnit.Tests/CheckpointManagers/MultiStreamMultiOutputCheckpointManagerTests.cs b/src/KurrentDB.Projections.Core.XUnit.Tests/CheckpointManagers/MultiStreamMultiOutputCheckpointManagerTests.cs index 57020a53249..bc2cf751a74 100644 --- a/src/KurrentDB.Projections.Core.XUnit.Tests/CheckpointManagers/MultiStreamMultiOutputCheckpointManagerTests.cs +++ b/src/KurrentDB.Projections.Core.XUnit.Tests/CheckpointManagers/MultiStreamMultiOutputCheckpointManagerTests.cs @@ -47,8 +47,8 @@ public MultiStreamMultiOutputCheckpointManagerTests() { var positionTagger = new FakePositionTagger(ProjectionPhase); _sut = new MultiStreamMultiOutputCheckpointManager( - _publisher, projectionId, projectionVersion, SystemAccounts.System, _ioDispatcher, projectionConfig, ProjectionName, - positionTagger, namingBuilder, usePersistentCheckpoints: true, producesRunningResults: true, definesFold: false, + _publisher, projectionId, projectionVersion, SystemAccounts.System, _ioDispatcher, projectionConfig, + positionTagger, namingBuilder, usePersistentCheckpoints: true, _checkpointWriter, Opts.MaxProjectionStateSizeDefault); } @@ -59,7 +59,7 @@ public void when_loading_prerecorded_events(PreRecordedEventsScenario scenario) _existingStreams.HardDeleteStreams(scenario.WithHardDeletedStreams); var checkpointTag = CheckpointTag.Empty; - _checkpointWriter.StartFrom(checkpointTag, 0); + _checkpointWriter.StartFrom(0); _sut.BeginLoadPrerecordedEvents(checkpointTag); // Read the order stream to find prerecorded events diff --git a/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReader.cs b/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReader.cs index 112ea553f07..d3fe6ad33bb 100644 --- a/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReader.cs +++ b/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReader.cs @@ -32,7 +32,7 @@ private readonly private readonly int _maxEvents; private readonly Guid _requestCorrelationId; - private readonly List _batch = new List(); + private readonly List _batch = []; private readonly IEnvelope _replyEnvelope; private readonly ITimeProvider _timeProvider; @@ -73,7 +73,6 @@ public void Start() { 0, _querySource, _timeProvider, - stopOnEof: true, runAs: _user); //TODO: make reader mode explicit diff --git a/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReaderService.cs b/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReaderService.cs index 44e6a0a5eab..57b55f81311 100644 --- a/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReaderService.cs +++ b/src/KurrentDB.Projections.Core/EventReaders/Feeds/FeedReaderService.cs @@ -9,18 +9,10 @@ namespace KurrentDB.Projections.Core.EventReaders.Feeds; -public class FeedReaderService : IHandle { - private readonly ReaderSubscriptionDispatcher _subscriptionDispatcher; - - private readonly ITimeProvider _timeProvider; - - public FeedReaderService(ReaderSubscriptionDispatcher subscriptionDispatcher, ITimeProvider timeProvider) { - _subscriptionDispatcher = subscriptionDispatcher; - _timeProvider = timeProvider; - } - +public class FeedReaderService(ReaderSubscriptionDispatcher subscriptionDispatcher, ITimeProvider timeProvider) + : IHandle { public void Handle(FeedReaderMessage.ReadPage message) { - var reader = FeedReader.Create(_subscriptionDispatcher, message, _timeProvider); + var reader = FeedReader.Create(subscriptionDispatcher, message, timeProvider); reader.Start(); } } diff --git a/src/KurrentDB.Projections.Core/Messages/CoreProjectionCheckpointWriterMessage.cs b/src/KurrentDB.Projections.Core/Messages/CoreProjectionCheckpointWriterMessage.cs index 28fc90132f2..4aa0ca6c561 100644 --- a/src/KurrentDB.Projections.Core/Messages/CoreProjectionCheckpointWriterMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/CoreProjectionCheckpointWriterMessage.cs @@ -8,28 +8,12 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class CoreProjectionCheckpointWriterMessage { [DerivedMessage(ProjectionMessage.CoreProcessing)] - public sealed partial class CheckpointWritten : Message { - private readonly CheckpointTag _position; - - public CheckpointWritten(CheckpointTag position) { - _position = position; - } - - public CheckpointTag Position { - get { return _position; } - } + public sealed partial class CheckpointWritten(CheckpointTag position) : Message { + public CheckpointTag Position { get; } = position; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public sealed partial class RestartRequested : Message { - public string Reason { - get { return _reason; } - } - - private readonly string _reason; - - public RestartRequested(string reason) { - _reason = reason; - } + public sealed partial class RestartRequested(string reason) : Message { + public string Reason { get; } = reason; } } diff --git a/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementControlMessage.cs b/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementControlMessage.cs index 6d41bbfff3e..866752f4ba1 100644 --- a/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementControlMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementControlMessage.cs @@ -7,15 +7,7 @@ namespace KurrentDB.Projections.Core.Messages; [DerivedMessage] -public abstract partial class CoreProjectionManagementControlMessage : CoreProjectionManagementMessageBase { - private readonly Guid _workerId; - - public Guid WorkerId { - get { return _workerId; } - } - - public CoreProjectionManagementControlMessage(Guid projectionId, Guid workerId) - : base(projectionId) { - _workerId = workerId; - } +public abstract partial class CoreProjectionManagementControlMessage(Guid projectionId, Guid workerId) + : CoreProjectionManagementMessageBase(projectionId) { + public Guid WorkerId { get; } = workerId; } diff --git a/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessage.cs b/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessage.cs index 942be415e3d..c175adf08e0 100644 --- a/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessage.cs @@ -2,6 +2,7 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; +using KurrentDB.Common.Utils; using KurrentDB.Core.Messaging; using KurrentDB.Projections.Core.Services; using KurrentDB.Projections.Core.Services.Processing; @@ -10,202 +11,73 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class CoreProjectionManagementMessage { [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class Start : CoreProjectionManagementControlMessage { - public Start(Guid projectionId, Guid workerId) - : base(projectionId, workerId) { - } - } + public partial class Start(Guid projectionId, Guid workerId) : CoreProjectionManagementControlMessage(projectionId, workerId); [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class LoadStopped : CoreProjectionManagementControlMessage { - public LoadStopped(Guid correlationId, Guid workerId) - : base(correlationId, workerId) { - } - } + public partial class LoadStopped(Guid correlationId, Guid workerId) : CoreProjectionManagementControlMessage(correlationId, workerId); [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class Stop : CoreProjectionManagementControlMessage { - public Stop(Guid projectionId, Guid workerId) - : base(projectionId, workerId) { - } - } + public partial class Stop(Guid projectionId, Guid workerId) : CoreProjectionManagementControlMessage(projectionId, workerId); [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class Kill : CoreProjectionManagementControlMessage { - public Kill(Guid projectionId, Guid workerId) - : base(projectionId, workerId) { - } - } + public partial class Kill(Guid projectionId, Guid workerId) : CoreProjectionManagementControlMessage(projectionId, workerId); [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class GetState : CoreProjectionManagementControlMessage { - private readonly Guid _correlationId; - private readonly string _partition; - - public GetState(Guid correlationId, Guid projectionId, string partition, Guid workerId) - : base(projectionId, workerId) { - if (partition == null) - throw new ArgumentNullException("partition"); - _correlationId = correlationId; - _partition = partition; - } - - public string Partition { - get { return _partition; } - } + public partial class GetState(Guid correlationId, Guid projectionId, string partition, Guid workerId) + : CoreProjectionManagementControlMessage(projectionId, workerId) { + public string Partition { get; } = Ensure.NotNull(partition); - public Guid CorrelationId { - get { return _correlationId; } - } + public Guid CorrelationId { get; } = correlationId; } [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class GetResult : CoreProjectionManagementControlMessage { - private readonly Guid _correlationId; - private readonly string _partition; + public partial class GetResult(Guid correlationId, Guid projectionId, string partition, Guid workerId) + : CoreProjectionManagementControlMessage(projectionId, workerId) { + public string Partition { get; } = Ensure.NotNull(partition); - public GetResult(Guid correlationId, Guid projectionId, string partition, Guid workerId) - : base(projectionId, workerId) { - if (partition == null) - throw new ArgumentNullException("partition"); - _correlationId = correlationId; - _partition = partition; - } - - public string Partition { - get { return _partition; } - } - - public Guid CorrelationId { - get { return _correlationId; } - } + public Guid CorrelationId { get; } = correlationId; } [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class CreateAndPrepare : CoreProjectionManagementControlMessage { - private readonly ProjectionConfig _config; - private readonly string _handlerType; - private readonly string _query; - private readonly string _name; - private readonly ProjectionVersion _version; - private readonly bool _enableContentTypeValidation; - - public CreateAndPrepare( - Guid projectionId, - Guid workerId, - string name, - ProjectionVersion version, - ProjectionConfig config, - string handlerType, - string query, - bool enableContentTypeValidation) - : base(projectionId, workerId) { - _name = name; - _version = version; - _config = config; - _handlerType = handlerType; - _query = query; - _enableContentTypeValidation = enableContentTypeValidation; - } - - public ProjectionConfig Config { - get { return _config; } - } - - public string Name { - get { return _name; } - } - - public ProjectionVersion Version { - get { return _version; } - } - - public string HandlerType { - get { return _handlerType; } - } - - public string Query { - get { return _query; } - } - - public bool EnableContentTypeValidation { - get { return _enableContentTypeValidation; } - } + public partial class CreateAndPrepare( + Guid projectionId, + Guid workerId, + string name, + ProjectionVersion version, + ProjectionConfig config, + string handlerType, + string query, + bool enableContentTypeValidation) + : CoreProjectionManagementControlMessage(projectionId, workerId) { + public ProjectionConfig Config { get; } = config; + public string Name { get; } = name; + public ProjectionVersion Version { get; } = version; + public string HandlerType { get; } = handlerType; + public string Query { get; } = query; + public bool EnableContentTypeValidation { get; } = enableContentTypeValidation; } [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class CreatePrepared : CoreProjectionManagementControlMessage { - private readonly ProjectionConfig _config; - private readonly QuerySourcesDefinition _sourceDefinition; - private readonly string _handlerType; - private readonly string _query; - private readonly string _name; - private readonly ProjectionVersion _version; - private readonly bool _enableContentTypeValidation; - - public CreatePrepared( - Guid projectionId, - Guid workerId, - string name, - ProjectionVersion version, - ProjectionConfig config, - QuerySourcesDefinition sourceDefinition, - string handlerType, - string query, - bool enableContentTypeValidation) - : base(projectionId, workerId) { - if (name == null) - throw new ArgumentNullException("name"); - if (config == null) - throw new ArgumentNullException("config"); - if (sourceDefinition == null) - throw new ArgumentNullException("sourceDefinition"); - if (handlerType == null) - throw new ArgumentNullException("handlerType"); - if (query == null) - throw new ArgumentNullException("query"); - _name = name; - _version = version; - _config = config; - _sourceDefinition = sourceDefinition; - _handlerType = handlerType; - _query = query; - _enableContentTypeValidation = enableContentTypeValidation; - } - - public ProjectionConfig Config { - get { return _config; } - } - - public string Name { - get { return _name; } - } - - public QuerySourcesDefinition SourceDefinition { - get { return _sourceDefinition; } - } - - public ProjectionVersion Version { - get { return _version; } - } - - public string HandlerType { - get { return _handlerType; } - } - - public string Query { - get { return _query; } - } - - public bool EnableContentTypeValidation { - get { return _enableContentTypeValidation; } - } + public partial class CreatePrepared( + Guid projectionId, + Guid workerId, + string name, + ProjectionVersion version, + ProjectionConfig config, + QuerySourcesDefinition sourceDefinition, + string handlerType, + string query, + bool enableContentTypeValidation) + : CoreProjectionManagementControlMessage(projectionId, workerId) { + public ProjectionConfig Config { get; } = Ensure.NotNull(config); + public string Name { get; } = Ensure.NotNull(name); + public QuerySourcesDefinition SourceDefinition { get; } = Ensure.NotNull(sourceDefinition); + public ProjectionVersion Version { get; } = version; + public string HandlerType { get; } = Ensure.NotNull(handlerType); + public string Query { get; } = Ensure.NotNull(query); + public bool EnableContentTypeValidation { get; } = enableContentTypeValidation; } [DerivedMessage(ProjectionMessage.CoreManagement)] - public partial class Dispose : CoreProjectionManagementControlMessage { - public Dispose(Guid projectionId, Guid workerId) - : base(projectionId, workerId) { - } - } + public partial class Dispose(Guid projectionId, Guid workerId) : CoreProjectionManagementControlMessage(projectionId, workerId); } diff --git a/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessageBase.cs b/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessageBase.cs index 23a3295f044..86cc1a44f9f 100644 --- a/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessageBase.cs +++ b/src/KurrentDB.Projections.Core/Messages/CoreProjectionManagementMessageBase.cs @@ -7,14 +7,6 @@ namespace KurrentDB.Projections.Core.Messages; [DerivedMessage] -public abstract partial class CoreProjectionManagementMessageBase : Message { - private readonly Guid _projectionIdId; - - protected CoreProjectionManagementMessageBase(Guid projectionId) { - _projectionIdId = projectionId; - } - - public Guid ProjectionId { - get { return _projectionIdId; } - } +public abstract partial class CoreProjectionManagementMessageBase(Guid projectionId) : Message { + public Guid ProjectionId { get; } = projectionId; } diff --git a/src/KurrentDB.Projections.Core/Messages/CoreProjectionProcessingMessage.cs b/src/KurrentDB.Projections.Core/Messages/CoreProjectionProcessingMessage.cs index 067e159b0a8..7e01226bdad 100644 --- a/src/KurrentDB.Projections.Core/Messages/CoreProjectionProcessingMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/CoreProjectionProcessingMessage.cs @@ -9,143 +9,51 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class CoreProjectionProcessingMessage { [DerivedMessage] - public abstract partial class Message : KurrentDB.Core.Messaging.Message { - private readonly Guid _projectionId; - - protected Message(Guid projectionId) { - _projectionId = projectionId; - } - - public Guid ProjectionId { - get { return _projectionId; } - } + public abstract partial class Message(Guid projectionId) : KurrentDB.Core.Messaging.Message { + public Guid ProjectionId { get; } = projectionId; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class CheckpointLoaded : Message { - private readonly CheckpointTag _checkpointTag; - private readonly string _checkpointData; - private readonly long _checkpointEventNumber; - - public CheckpointLoaded( - Guid projectionId, CheckpointTag checkpointTag, string checkpointData, long checkpointEventNumber) - : base(projectionId) { - _checkpointTag = checkpointTag; - _checkpointData = checkpointData; - _checkpointEventNumber = checkpointEventNumber; - } - - public CheckpointTag CheckpointTag { - get { return _checkpointTag; } - } - - public string CheckpointData { - get { return _checkpointData; } - } - - public long CheckpointEventNumber { - get { return _checkpointEventNumber; } - } + public partial class CheckpointLoaded(Guid projectionId, CheckpointTag checkpointTag, string checkpointData, long checkpointEventNumber) + : Message(projectionId) { + public CheckpointTag CheckpointTag { get; } = checkpointTag; + public string CheckpointData { get; } = checkpointData; + public long CheckpointEventNumber { get; } = checkpointEventNumber; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class PrerecordedEventsLoaded : Message { - private readonly CheckpointTag _checkpointTag; - - public PrerecordedEventsLoaded(Guid projectionId, CheckpointTag checkpointTag) - : base(projectionId) { - _checkpointTag = checkpointTag; - } - - public CheckpointTag CheckpointTag { - get { return _checkpointTag; } - } + public partial class PrerecordedEventsLoaded(Guid projectionId, CheckpointTag checkpointTag) : Message(projectionId) { + public CheckpointTag CheckpointTag { get; } = checkpointTag; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class CheckpointCompleted : Message { - private readonly CheckpointTag _checkpointTag; - - public CheckpointCompleted(Guid projectionId, CheckpointTag checkpointTag) - : base(projectionId) { - _checkpointTag = checkpointTag; - } - - public CheckpointTag CheckpointTag { - get { return _checkpointTag; } - } + public partial class CheckpointCompleted(Guid projectionId, CheckpointTag checkpointTag) : Message(projectionId) { + public CheckpointTag CheckpointTag { get; } = checkpointTag; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class RestartRequested : Message { - private readonly string _reason; - - public RestartRequested(Guid projectionId, string reason) - : base(projectionId) { - _reason = reason; - } - - public string Reason { - get { return _reason; } - } + public partial class RestartRequested(Guid projectionId, string reason) : Message(projectionId) { + public string Reason { get; } = reason; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class Failed : Message { - private readonly string _reason; - - public Failed(Guid projectionId, string reason) - : base(projectionId) { - _reason = reason; - } - - public string Reason { - get { return _reason; } - } + public partial class Failed(Guid projectionId, string reason) : Message(projectionId) { + public string Reason { get; } = reason; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class ReadyForCheckpoint : KurrentDB.Core.Messaging.Message { - private readonly object _sender; - - public ReadyForCheckpoint(object sender) { - _sender = sender; - } - - public object Sender { - get { return _sender; } - } + public partial class ReadyForCheckpoint(object sender) : KurrentDB.Core.Messaging.Message { + public object Sender { get; } = sender; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class EmittedStreamAwaiting : KurrentDB.Core.Messaging.Message { - private readonly IEnvelope _envelope; - private readonly string _streamId; - - public EmittedStreamAwaiting(string streamId, IEnvelope envelope) { - _envelope = envelope; - _streamId = streamId; - } - - public string StreamId { - get { return _streamId; } - } - - public IEnvelope Envelope { - get { return _envelope; } - } + public partial class EmittedStreamAwaiting(string streamId, IEnvelope envelope) : KurrentDB.Core.Messaging.Message { + public string StreamId { get; } = streamId; + public IEnvelope Envelope { get; } = envelope; } [DerivedMessage(ProjectionMessage.CoreProcessing)] - public partial class EmittedStreamWriteCompleted : KurrentDB.Core.Messaging.Message { - private readonly string _streamId; - - public EmittedStreamWriteCompleted(string streamId) { - _streamId = streamId; - } - - public string StreamId { - get { return _streamId; } - } + public partial class EmittedStreamWriteCompleted(string streamId) : KurrentDB.Core.Messaging.Message { + public string StreamId { get; } = streamId; } } diff --git a/src/KurrentDB.Projections.Core/Messages/CoreProjectionStatusMessage.cs b/src/KurrentDB.Projections.Core/Messages/CoreProjectionStatusMessage.cs index 8733367f898..91bf93a2d8f 100644 --- a/src/KurrentDB.Projections.Core/Messages/CoreProjectionStatusMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/CoreProjectionStatusMessage.cs @@ -10,158 +10,70 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class CoreProjectionStatusMessage { - [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class CoreProjectionStatusMessageBase : CoreProjectionManagementMessageBase { - protected CoreProjectionStatusMessageBase(Guid projectionId) - : base(projectionId) { - } - } + [DerivedMessage] + public abstract partial class CoreProjectionStatusMessageBase(Guid projectionId) : CoreProjectionManagementMessageBase(projectionId); [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class Started : CoreProjectionStatusMessageBase { - public string Name { get; } - public Started(Guid projectionId, string name) - : base(projectionId) { - Name = name; - } + public partial class Started(Guid projectionId, string name) : CoreProjectionStatusMessageBase(projectionId) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class Faulted : CoreProjectionStatusMessageBase { - private readonly string _faultedReason; - - public Faulted(Guid projectionId, string faultedReason) - : base(projectionId) { - _faultedReason = faultedReason; - } - - public string FaultedReason { - get { return _faultedReason; } - } + public partial class Faulted(Guid projectionId, string faultedReason) : CoreProjectionStatusMessageBase(projectionId) { + public string FaultedReason { get; } = faultedReason; } [DerivedMessage] - public abstract partial class DataReportBase : CoreProjectionStatusMessageBase { - private readonly Guid _correlationId; - private readonly string _partition; - private readonly CheckpointTag _position; - - protected DataReportBase(Guid correlationId, Guid projectionId, string partition, CheckpointTag position) - : base(projectionId) { - _correlationId = correlationId; - _partition = partition; - _position = position; - } - - public string Partition { - get { return _partition; } - } - - public Guid CorrelationId { - get { return _correlationId; } - } - - public CheckpointTag Position { - get { return _position; } - } + public abstract partial class DataReportBase(Guid correlationId, Guid projectionId, string partition, CheckpointTag position) + : CoreProjectionStatusMessageBase(projectionId) { + public string Partition { get; } = partition; + public Guid CorrelationId { get; } = correlationId; + public CheckpointTag Position { get; } = position; } [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class StateReport : DataReportBase { - private readonly string _state; - - public StateReport( - Guid correlationId, - Guid projectionId, - string partition, - string state, - CheckpointTag position) - : base(correlationId, projectionId, partition, position) { - _state = state; - } - - public string State { - get { return _state; } - } + public partial class StateReport( + Guid correlationId, + Guid projectionId, + string partition, + string state, + CheckpointTag position) + : DataReportBase(correlationId, projectionId, partition, position) { + public string State { get; } = state; } [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class ResultReport : DataReportBase { - private readonly string _result; - - public ResultReport( - Guid correlationId, - Guid projectionId, - string partition, - string result, - CheckpointTag position) - : base(correlationId, projectionId, partition, position) { - _result = result; - } - - public string Result { - get { return _result; } - } + public partial class ResultReport( + Guid correlationId, + Guid projectionId, + string partition, + string result, + CheckpointTag position) + : DataReportBase(correlationId, projectionId, partition, position) { + public string Result { get; } = result; } [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class StatisticsReport : CoreProjectionStatusMessageBase { - private readonly ProjectionStatistics _statistics; - private readonly int _sequentialNumber; - - public StatisticsReport(Guid projectionId, ProjectionStatistics statistics, int sequentialNumber) - : base(projectionId) { - _statistics = statistics; - _sequentialNumber = sequentialNumber; - } - - public ProjectionStatistics Statistics { - get { return _statistics; } - } + public partial class StatisticsReport(Guid projectionId, ProjectionStatistics statistics, int sequentialNumber) + : CoreProjectionStatusMessageBase(projectionId) { + public ProjectionStatistics Statistics { get; } = statistics; - public int SequentialNumber { - get { return _sequentialNumber; } - } + public int SequentialNumber { get; } = sequentialNumber; } [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class Prepared : CoreProjectionStatusMessageBase { - private readonly ProjectionSourceDefinition _sourceDefinition; - - public Prepared(Guid projectionId, ProjectionSourceDefinition sourceDefinition) - : base(projectionId) { - _sourceDefinition = sourceDefinition; - } - - public ProjectionSourceDefinition SourceDefinition { - get { return _sourceDefinition; } - } + public partial class Prepared(Guid projectionId, ProjectionSourceDefinition sourceDefinition) + : CoreProjectionStatusMessageBase(projectionId) { + public ProjectionSourceDefinition SourceDefinition { get; } = sourceDefinition; } [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class Suspended : CoreProjectionStatusMessageBase { - public Suspended(Guid projectionId) - : base(projectionId) { - } - } + public partial class Suspended(Guid projectionId) : CoreProjectionStatusMessageBase(projectionId); [DerivedMessage(ProjectionMessage.CoreStatus)] - public partial class Stopped : CoreProjectionStatusMessageBase { - private readonly bool _completed; - private readonly string _name; - - public Stopped(Guid projectionId, string name, bool completed) - : base(projectionId) { - _completed = completed; - _name = name; - } - - public bool Completed { - get { return _completed; } - } + public partial class Stopped(Guid projectionId, string name, bool completed) : CoreProjectionStatusMessageBase(projectionId) { + public bool Completed { get; } = completed; - public string Name { - get { return _name; } - } + public string Name { get; } = name; } } diff --git a/src/KurrentDB.Projections.Core/Messages/EventReaderSubscriptionMessageBase.cs b/src/KurrentDB.Projections.Core/Messages/EventReaderSubscriptionMessageBase.cs index ada3e5cc47b..d87e2969aae 100644 --- a/src/KurrentDB.Projections.Core/Messages/EventReaderSubscriptionMessageBase.cs +++ b/src/KurrentDB.Projections.Core/Messages/EventReaderSubscriptionMessageBase.cs @@ -2,6 +2,7 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; +using KurrentDB.Common.Utils; using KurrentDB.Core.Messaging; using KurrentDB.Projections.Core.Services.Processing; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; @@ -16,128 +17,99 @@ public static partial class EventReaderSubscriptionMessage { /// an event at this position does not satisfy projection filter) /// [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public partial class CheckpointSuggested : EventReaderSubscriptionMessageBase { - public CheckpointSuggested( - Guid subscriptionId, CheckpointTag checkpointTag, float progress, - long subscriptionMessageSequenceNumber, object source = null) - : base(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source) { - } - } + public partial class CheckpointSuggested( + Guid subscriptionId, + CheckpointTag checkpointTag, + float progress, + long subscriptionMessageSequenceNumber, + object source = null) + : EventReaderSubscriptionMessageBase(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source); [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public partial class ProgressChanged : EventReaderSubscriptionMessageBase { - public ProgressChanged( - Guid subscriptionId, CheckpointTag checkpointTag, float progress, - long subscriptionMessageSequenceNumber, object source = null) - : base(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source) { - } - } + public partial class ProgressChanged( + Guid subscriptionId, + CheckpointTag checkpointTag, + float progress, + long subscriptionMessageSequenceNumber, + object source = null) + : EventReaderSubscriptionMessageBase(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source); [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public partial class SubscriptionStarted : EventReaderSubscriptionMessageBase { - private readonly long _startingLastCommitPosition; - - public long StartingLastCommitPosition { - get { return _startingLastCommitPosition; } - } - - public SubscriptionStarted( - Guid subscriptionId, CheckpointTag checkpointTag, long startingLastCommitPosition, - long subscriptionMessageSequenceNumber, object source = null) - : base(subscriptionId, checkpointTag, 0f, subscriptionMessageSequenceNumber, source) { - _startingLastCommitPosition = startingLastCommitPosition; - } + public partial class SubscriptionStarted( + Guid subscriptionId, + CheckpointTag checkpointTag, + long startingLastCommitPosition, + long subscriptionMessageSequenceNumber, + object source = null) + : EventReaderSubscriptionMessageBase(subscriptionId, checkpointTag, 0f, subscriptionMessageSequenceNumber, source) { + public long StartingLastCommitPosition { get; } = startingLastCommitPosition; } [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public sealed partial class NotAuthorized : EventReaderSubscriptionMessageBase { - public NotAuthorized( - Guid subscriptionId, CheckpointTag checkpointTag, float progress, - long subscriptionMessageSequenceNumber, - object source = null) - : base(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source) { - } - } + public sealed partial class NotAuthorized( + Guid subscriptionId, + CheckpointTag checkpointTag, + float progress, + long subscriptionMessageSequenceNumber, + object source = null) + : EventReaderSubscriptionMessageBase(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source); [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public sealed partial class SubscribeTimeout : EventReaderSubscriptionMessageBase { - public SubscribeTimeout(Guid subscriptionId) - : base(subscriptionId, CheckpointTag.Empty, 100.0f, -1, null) { - } - } + public sealed partial class SubscribeTimeout(Guid subscriptionId) + : EventReaderSubscriptionMessageBase(subscriptionId, CheckpointTag.Empty, 100.0f, -1, null); [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public sealed partial class Failed : EventReaderSubscriptionMessageBase { - private readonly string _reason; - - public string Reason { - get { return _reason; } - } - - public Failed(Guid subscriptionId, string reason) - : base(subscriptionId, CheckpointTag.Empty, 100.0f, -1, null) { - _reason = reason; - } + public sealed partial class Failed(Guid subscriptionId, string reason) + : EventReaderSubscriptionMessageBase(subscriptionId, CheckpointTag.Empty, 100.0f, -1, null) { + public string Reason { get; } = reason; } [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public partial class EofReached : EventReaderSubscriptionMessageBase { - public EofReached( - Guid subscriptionId, CheckpointTag checkpointTag, - long subscriptionMessageSequenceNumber, object source = null) - : base(subscriptionId, checkpointTag, 100.0f, subscriptionMessageSequenceNumber, source) { - } - } + public partial class EofReached( + Guid subscriptionId, + CheckpointTag checkpointTag, + long subscriptionMessageSequenceNumber, + object source = null) + : EventReaderSubscriptionMessageBase(subscriptionId, checkpointTag, 100.0f, subscriptionMessageSequenceNumber, source); /// - /// NOTEL the PartitionDeleted may appear out-of-order and is not guaranteed + /// NOTE: the PartitionDeleted may appear out-of-order and is not guaranteed /// to appear at the same sequence position in a recovery /// [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public partial class PartitionDeleted : EventReaderSubscriptionMessageBase { - private readonly string _partition; - - public string Partition { - get { return _partition; } - } - - public PartitionDeleted( - Guid subscriptionId, CheckpointTag checkpointTag, string partition, - long subscriptionMessageSequenceNumber, object source = null) - : base(subscriptionId, checkpointTag, 100.0f, subscriptionMessageSequenceNumber, source) { - _partition = partition; - } + public partial class PartitionDeleted( + Guid subscriptionId, + CheckpointTag checkpointTag, + string partition, + long subscriptionMessageSequenceNumber, + object source = null) + : EventReaderSubscriptionMessageBase(subscriptionId, checkpointTag, 100.0f, subscriptionMessageSequenceNumber, source) { + public string Partition { get; } = partition; } [DerivedMessage(ProjectionMessage.EventReaderSubscription)] public partial class CommittedEventReceived : EventReaderSubscriptionMessageBase { + private CommittedEventReceived( + Guid subscriptionId, CheckpointTag checkpointTag, string eventCategory, ResolvedEvent data, + float progress, long subscriptionMessageSequenceNumber, object source) + : base(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source) { + Data = Ensure.NotNull(data); + EventCategory = eventCategory; + } + public static CommittedEventReceived Sample( ResolvedEvent data, Guid subscriptionId, long subscriptionMessageSequenceNumber) { - return new CommittedEventReceived( + return new( subscriptionId, 0, null, data, 77.7f, subscriptionMessageSequenceNumber); } public static CommittedEventReceived Sample( ResolvedEvent data, CheckpointTag checkpointTag, Guid subscriptionId, long subscriptionMessageSequenceNumber) { - return new CommittedEventReceived( + return new( subscriptionId, checkpointTag, null, data, 77.7f, subscriptionMessageSequenceNumber, null); } - private readonly ResolvedEvent _data; - - private readonly string _eventCategory; - - private CommittedEventReceived( - Guid subscriptionId, CheckpointTag checkpointTag, string eventCategory, ResolvedEvent data, - float progress, long subscriptionMessageSequenceNumber, object source) - : base(subscriptionId, checkpointTag, progress, subscriptionMessageSequenceNumber, source) { - if (data == null) - throw new ArgumentNullException("data"); - _data = data; - _eventCategory = eventCategory; - } - private CommittedEventReceived( Guid subscriptionId, int phase, string eventCategory, ResolvedEvent data, float progress, long subscriptionMessageSequenceNumber) @@ -147,77 +119,38 @@ private CommittedEventReceived( eventCategory, data, progress, subscriptionMessageSequenceNumber, null) { } - public ResolvedEvent Data { - get { return _data; } - } + public ResolvedEvent Data { get; } - public string EventCategory { - get { return _eventCategory; } - } + public string EventCategory { get; } public static CommittedEventReceived FromCommittedEventDistributed( ReaderSubscriptionMessage.CommittedEventDistributed message, CheckpointTag checkpointTag, - string eventCategory, Guid subscriptionId, long subscriptionMessageSequenceNumber) { - return new CommittedEventReceived( + string eventCategory, Guid subscriptionId, long subscriptionMessageSequenceNumber) + => new( subscriptionId, checkpointTag, eventCategory, message.Data, message.Progress, subscriptionMessageSequenceNumber, message.Source); - } - public override string ToString() { - return CheckpointTag.ToString(); - } + public override string ToString() => CheckpointTag.ToString(); } [DerivedMessage(ProjectionMessage.EventReaderSubscription)] - public partial class ReaderAssignedReader : EventReaderSubscriptionMessageBase { - private readonly Guid _readerId; - - public ReaderAssignedReader(Guid subscriptionId, Guid readerId) - : base(subscriptionId, null, 0, 0, null) { - _readerId = readerId; - } - - public Guid ReaderId { - get { return _readerId; } - } + public partial class ReaderAssignedReader(Guid subscriptionId, Guid readerId) + : EventReaderSubscriptionMessageBase(subscriptionId, null, 0, 0, null) { + public Guid ReaderId { get; } = readerId; } } [DerivedMessage] -public abstract partial class EventReaderSubscriptionMessageBase : Message { - private readonly Guid _subscriptionId; - private readonly long _subscriptionMessageSequenceNumber; - private readonly object _source; - private readonly CheckpointTag _checkpointTag; - private readonly float _progress; - - internal EventReaderSubscriptionMessageBase(Guid subscriptionId, CheckpointTag checkpointTag, float progress, - long subscriptionMessageSequenceNumber, object source) { - _subscriptionId = subscriptionId; - _checkpointTag = checkpointTag; - _progress = progress; - _subscriptionMessageSequenceNumber = subscriptionMessageSequenceNumber; - _source = source; - } - - - public CheckpointTag CheckpointTag { - get { return _checkpointTag; } - } - - public float Progress { - get { return _progress; } - } - - public long SubscriptionMessageSequenceNumber { - get { return _subscriptionMessageSequenceNumber; } - } - - public Guid SubscriptionId { - get { return _subscriptionId; } - } - - public object Source { - get { return _source; } - } +public abstract partial class EventReaderSubscriptionMessageBase( + Guid subscriptionId, + CheckpointTag checkpointTag, + float progress, + long subscriptionMessageSequenceNumber, + object source) + : Message { + public CheckpointTag CheckpointTag { get; } = checkpointTag; + public float Progress { get; } = progress; + public long SubscriptionMessageSequenceNumber { get; } = subscriptionMessageSequenceNumber; + public Guid SubscriptionId { get; } = subscriptionId; + public object Source { get; } = source; } diff --git a/src/KurrentDB.Projections.Core/Messages/EventReaders/Feeds/FeedReaderMessage.cs b/src/KurrentDB.Projections.Core/Messages/EventReaders/Feeds/FeedReaderMessage.cs index 8ff734892a2..b5f6547477f 100644 --- a/src/KurrentDB.Projections.Core/Messages/EventReaders/Feeds/FeedReaderMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/EventReaders/Feeds/FeedReaderMessage.cs @@ -11,50 +11,41 @@ namespace KurrentDB.Projections.Core.Messages.EventReaders.Feeds; public static partial class FeedReaderMessage { [DerivedMessage] - public abstract partial class FeedReaderMessageBase : Message { - } + public abstract partial class FeedReaderMessageBase : Message; [DerivedMessage(ProjectionMessage.FeedReader)] - public sealed partial class ReadPage : FeedReaderMessageBase { - public readonly Guid CorrelationId; - public readonly IEnvelope Envelope; - public readonly ClaimsPrincipal User; - - public readonly QuerySourcesDefinition QuerySource; - public readonly CheckpointTag FromPosition; - public readonly int MaxEvents; - - public ReadPage( - Guid correlationId, IEnvelope envelope, ClaimsPrincipal user, QuerySourcesDefinition querySource, - CheckpointTag fromPosition, - int maxEvents) { - User = user; - CorrelationId = correlationId; - Envelope = envelope; - QuerySource = querySource; - FromPosition = fromPosition; - MaxEvents = maxEvents; - } + public sealed partial class ReadPage( + Guid correlationId, + IEnvelope envelope, + ClaimsPrincipal user, + QuerySourcesDefinition querySource, + CheckpointTag fromPosition, + int maxEvents) + : FeedReaderMessageBase { + public readonly Guid CorrelationId = correlationId; + public readonly IEnvelope Envelope = envelope; + public readonly ClaimsPrincipal User = user; + + public readonly QuerySourcesDefinition QuerySource = querySource; + public readonly CheckpointTag FromPosition = fromPosition; + public readonly int MaxEvents = maxEvents; } [DerivedMessage(ProjectionMessage.FeedReader)] - public sealed partial class FeedPage : FeedReaderMessageBase { + public sealed partial class FeedPage( + Guid correlationId, + FeedPage.ErrorStatus error, + TaggedResolvedEvent[] events, + CheckpointTag lastReaderPosition) + : FeedReaderMessageBase { public enum ErrorStatus { Success, NotAuthorized } - public readonly Guid CorrelationId; - public readonly ErrorStatus Error; - public readonly TaggedResolvedEvent[] Events; - public readonly CheckpointTag LastReaderPosition; - - public FeedPage( - Guid correlationId, ErrorStatus error, TaggedResolvedEvent[] events, CheckpointTag lastReaderPosition) { - CorrelationId = correlationId; - Error = error; - Events = events; - LastReaderPosition = lastReaderPosition; - } + public readonly Guid CorrelationId = correlationId; + public readonly ErrorStatus Error = error; + public readonly TaggedResolvedEvent[] Events = events; + public readonly CheckpointTag LastReaderPosition = lastReaderPosition; } } diff --git a/src/KurrentDB.Projections.Core/Messages/ICoreProjection.cs b/src/KurrentDB.Projections.Core/Messages/ICoreProjection.cs index c4d51197c3a..4ffcbf95368 100644 --- a/src/KurrentDB.Projections.Core/Messages/ICoreProjection.cs +++ b/src/KurrentDB.Projections.Core/Messages/ICoreProjection.cs @@ -10,5 +10,4 @@ public interface ICoreProjection : IHandle, IHandle, IHandle, - IHandle { -} + IHandle; diff --git a/src/KurrentDB.Projections.Core/Messages/IProjectionCheckpointManager.cs b/src/KurrentDB.Projections.Core/Messages/IProjectionCheckpointManager.cs index 85742c0fe4a..6ae5534a524 100644 --- a/src/KurrentDB.Projections.Core/Messages/IProjectionCheckpointManager.cs +++ b/src/KurrentDB.Projections.Core/Messages/IProjectionCheckpointManager.cs @@ -7,10 +7,8 @@ namespace KurrentDB.Projections.Core.Messages; public interface IProjectionCheckpointManager : IHandle, IHandle, - IHandle { -} + IHandle; public interface IEmittedStreamContainer : IProjectionCheckpointManager, IHandle, - IHandle { -} + IHandle; diff --git a/src/KurrentDB.Projections.Core/Messages/IQuerySources.cs b/src/KurrentDB.Projections.Core/Messages/IQuerySources.cs index 6126fa1ea3f..14a873fcfcc 100644 --- a/src/KurrentDB.Projections.Core/Messages/IQuerySources.cs +++ b/src/KurrentDB.Projections.Core/Messages/IQuerySources.cs @@ -5,53 +5,28 @@ namespace KurrentDB.Projections.Core.Messages; public interface IQuerySources { bool AllStreams { get; } - string[] Categories { get; } - string[] Streams { get; } - bool AllEvents { get; } - string[] Events { get; } - bool ByStreams { get; } - bool ByCustomPartitions { get; } - bool DefinesStateTransform { get; } - bool DefinesFold { get; } - bool HandlesDeletedNotifications { get; } - bool ProducesResults { get; } - bool IsBiState { get; } - bool IncludeLinksOption { get; } - string ResultStreamNameOption { get; } - string PartitionResultStreamNamePatternOption { get; } - bool ReorderEventsOption { get; } - int? ProcessingLagOption { get; } } public static class QuerySourcesExtensions { - public static bool HasStreams(this IQuerySources sources) { - var streams = sources.Streams; - return streams != null && streams.Length > 0; - } + public static bool HasStreams(this IQuerySources sources) => sources.Streams is { Length: > 0 }; - public static bool HasCategories(this IQuerySources sources) { - var categories = sources.Categories; - return categories != null && categories.Length > 0; - } + public static bool HasCategories(this IQuerySources sources) => sources.Categories is { Length: > 0 }; - public static bool HasEvents(this IQuerySources sources) { - var events = sources.Events; - return events != null && events.Length > 0; - } + public static bool HasEvents(this IQuerySources sources) => sources.Events is { Length: > 0 }; } diff --git a/src/KurrentDB.Projections.Core/Messages/ProjectionCoreServiceMessage.cs b/src/KurrentDB.Projections.Core/Messages/ProjectionCoreServiceMessage.cs index ad9e3d43769..42c285f7c3a 100644 --- a/src/KurrentDB.Projections.Core/Messages/ProjectionCoreServiceMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/ProjectionCoreServiceMessage.cs @@ -8,65 +8,34 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class ProjectionCoreServiceMessage { [DerivedMessage(ProjectionMessage.ServiceMessage)] - public partial class StartCore : Message { - public readonly Guid InstanceCorrelationId; - - public StartCore(Guid instanceCorrelationId) { - InstanceCorrelationId = instanceCorrelationId; - } + public partial class StartCore(Guid instanceCorrelationId) : Message { + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.ServiceMessage)] - public partial class StopCore : Message { - public Guid QueueId { get; } - - public StopCore(Guid queueId) { - QueueId = queueId; - } + public partial class StopCore(Guid queueId) : Message { + public Guid QueueId { get; } = queueId; } [DerivedMessage(ProjectionMessage.ServiceMessage)] - public partial class StopCoreTimeout : Message { - public Guid QueueId { get; } - - public StopCoreTimeout(Guid queueId) { - QueueId = queueId; - } + public partial class StopCoreTimeout(Guid queueId) : Message { + public Guid QueueId { get; } = queueId; } [DerivedMessage(ProjectionMessage.ServiceMessage)] - public partial class CoreTick : Message { - private readonly Action _action; - - public CoreTick(Action action) { - _action = action; - } - - public Action Action { - get { return _action; } - } + public partial class CoreTick(Action action) : Message { + public Action Action { get; } = action; } [DerivedMessage(ProjectionMessage.ServiceMessage)] - public partial class SubComponentStarted : Message { - public string SubComponent { get; } - public Guid InstanceCorrelationId { get; } - - public SubComponentStarted(string subComponent, Guid instanceCorrelationId) { - InstanceCorrelationId = instanceCorrelationId; - SubComponent = subComponent; - } + public partial class SubComponentStarted(string subComponent, Guid instanceCorrelationId) : Message { + public string SubComponent { get; } = subComponent; + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.ServiceMessage)] - public partial class SubComponentStopped : Message { - public readonly string SubComponent; - - public Guid QueueId { get; } - - public SubComponentStopped(string subComponent, Guid queueId) { - SubComponent = subComponent; - QueueId = queueId; - } + public partial class SubComponentStopped(string subComponent, Guid queueId) : Message { + public string SubComponent { get; } = subComponent; + public Guid QueueId { get; } = queueId; } } diff --git a/src/KurrentDB.Projections.Core/Messages/ProjectionManagementMessage.cs b/src/KurrentDB.Projections.Core/Messages/ProjectionManagementMessage.cs index d1cb3a96d77..4381a6f9303 100644 --- a/src/KurrentDB.Projections.Core/Messages/ProjectionManagementMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/ProjectionManagementMessage.cs @@ -17,525 +17,244 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class ProjectionManagementMessage { public static partial class Command { [DerivedMessage] - public abstract partial class ControlMessage : Message { - private readonly IEnvelope _envelope; - public readonly RunAs RunAs; + public abstract partial class ControlMessage(IEnvelope envelope, RunAs runAs) : Message { + public readonly RunAs RunAs = runAs; - protected ControlMessage(IEnvelope envelope, RunAs runAs) { - _envelope = envelope; - RunAs = runAs; - } - - public IEnvelope Envelope { - get { return _envelope; } - } + public IEnvelope Envelope { get; } = envelope; } [DerivedMessage(ProjectionMessage.Management)] - public partial class PostBatch : ControlMessage { - public ProjectionPost[] Projections { get; } - - public PostBatch( - IEnvelope envelope, RunAs runAs, ProjectionPost[] projections) - : base(envelope, runAs) { - Projections = projections; - } - - public class ProjectionPost { - public ProjectionMode Mode { get; } - public RunAs RunAs { get; } - public string Name { get; } - public string HandlerType { get; } - public string Query { get; } - public bool Enabled { get; } - public bool CheckpointsEnabled { get; } - public bool EmitEnabled { get; } - public bool EnableRunAs { get; } - public bool TrackEmittedStreams { get; } - - public ProjectionPost( - ProjectionMode mode, RunAs runAs, string name, string handlerType, string query, - bool enabled, bool checkpointsEnabled, bool emitEnabled, bool enableRunAs, - bool trackEmittedStreams) { - Mode = mode; - RunAs = runAs; - Name = name; - HandlerType = handlerType; - Query = query; - Enabled = enabled; - CheckpointsEnabled = checkpointsEnabled; - EmitEnabled = emitEnabled; - EnableRunAs = enableRunAs; - TrackEmittedStreams = trackEmittedStreams; - } + public partial class PostBatch(IEnvelope envelope, RunAs runAs, PostBatch.ProjectionPost[] projections) + : ControlMessage(envelope, runAs) { + public ProjectionPost[] Projections { get; } = projections; + + public class ProjectionPost( + ProjectionMode mode, + RunAs runAs, + string name, + string handlerType, + string query, + bool enabled, + bool checkpointsEnabled, + bool emitEnabled, + bool enableRunAs, + bool trackEmittedStreams) { + public ProjectionMode Mode { get; } = mode; + public RunAs RunAs { get; } = runAs; + public string Name { get; } = name; + public string HandlerType { get; } = handlerType; + public string Query { get; } = query; + public bool Enabled { get; } = enabled; + public bool CheckpointsEnabled { get; } = checkpointsEnabled; + public bool EmitEnabled { get; } = emitEnabled; + public bool EnableRunAs { get; } = enableRunAs; + public bool TrackEmittedStreams { get; } = trackEmittedStreams; } } [DerivedMessage(ProjectionMessage.Management)] public partial class Post : ControlMessage { - private readonly ProjectionMode _mode; - private readonly string _name; - private readonly string _handlerType; - private readonly string _query; - private readonly bool _enabled; - private readonly bool _checkpointsEnabled; - private readonly bool _emitEnabled; - private readonly bool _enableRunAs; - private readonly bool _trackEmittedStreams; - public Post( - IEnvelope envelope, ProjectionMode mode, string name, RunAs runAs, string handlerType, string query, - bool enabled, bool checkpointsEnabled, bool emitEnabled, bool trackEmittedStreams, + IEnvelope envelope, + ProjectionMode mode, + string name, + RunAs runAs, + string handlerType, + string query, + bool enabled, + bool checkpointsEnabled, + bool emitEnabled, + bool trackEmittedStreams, bool enableRunAs = false) : base(envelope, runAs) { - _name = name; - _handlerType = handlerType; - _mode = mode; - _query = query; - _enabled = enabled; - _checkpointsEnabled = checkpointsEnabled; - _emitEnabled = emitEnabled; - _trackEmittedStreams = trackEmittedStreams; - _enableRunAs = enableRunAs; + Name = name; + HandlerType = handlerType; + Mode = mode; + Query = query; + Enabled = enabled; + CheckpointsEnabled = checkpointsEnabled; + EmitEnabled = emitEnabled; + TrackEmittedStreams = trackEmittedStreams; + EnableRunAs = enableRunAs; } public Post( - IEnvelope envelope, ProjectionMode mode, string name, RunAs runAs, Type handlerType, string query, - bool enabled, bool checkpointsEnabled, bool emitEnabled, bool trackEmittedStreams, + IEnvelope envelope, + ProjectionMode mode, + string name, + RunAs runAs, + Type handlerType, + string query, + bool enabled, + bool checkpointsEnabled, + bool emitEnabled, + bool trackEmittedStreams, bool enableRunAs = false) : base(envelope, runAs) { - _name = name; - _handlerType = "native:" + handlerType.Namespace + "." + handlerType.Name; - _mode = mode; - _query = query; - _enabled = enabled; - _checkpointsEnabled = checkpointsEnabled; - _emitEnabled = emitEnabled; - _trackEmittedStreams = trackEmittedStreams; - _enableRunAs = enableRunAs; + Name = name; + HandlerType = $"native:{handlerType.Namespace}.{handlerType.Name}"; + Mode = mode; + Query = query; + Enabled = enabled; + CheckpointsEnabled = checkpointsEnabled; + EmitEnabled = emitEnabled; + TrackEmittedStreams = trackEmittedStreams; + EnableRunAs = enableRunAs; } // shortcut for posting ad-hoc JS queries public Post(IEnvelope envelope, RunAs runAs, string query, bool enabled) : base(envelope, runAs) { - _name = Guid.NewGuid().ToString("D"); - _handlerType = "JS"; - _mode = ProjectionMode.Transient; - _query = query; - _enabled = enabled; - _checkpointsEnabled = false; - _emitEnabled = false; - _trackEmittedStreams = false; - } - - public ProjectionMode Mode { - get { return _mode; } - } - - public string Query { - get { return _query; } - } - - public string Name { - get { return _name; } - } - - public string HandlerType { - get { return _handlerType; } - } - - public bool Enabled { - get { return _enabled; } - } - - public bool EmitEnabled { - get { return _emitEnabled; } - } - - public bool CheckpointsEnabled { - get { return _checkpointsEnabled; } - } - - public bool EnableRunAs { - get { return _enableRunAs; } - } - - public bool TrackEmittedStreams { - get { return _trackEmittedStreams; } - } + Name = Guid.NewGuid().ToString("D"); + HandlerType = "JS"; + Mode = ProjectionMode.Transient; + Query = query; + Enabled = enabled; + CheckpointsEnabled = false; + EmitEnabled = false; + TrackEmittedStreams = false; + } + + public ProjectionMode Mode { get; } + public string Query { get; } + public string Name { get; } + public string HandlerType { get; } + public bool Enabled { get; } + public bool EmitEnabled { get; } + public bool CheckpointsEnabled { get; } + public bool EnableRunAs { get; } + public bool TrackEmittedStreams { get; } } [DerivedMessage(ProjectionMessage.Management)] - public partial class Disable : ControlMessage { - private readonly string _name; - - public Disable(IEnvelope envelope, string name, RunAs runAs) - : base(envelope, runAs) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class Disable(IEnvelope envelope, string name, RunAs runAs) : ControlMessage(envelope, runAs) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class Enable : ControlMessage { - private readonly string _name; - - public Enable(IEnvelope envelope, string name, RunAs runAs) - : base(envelope, runAs) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class Enable(IEnvelope envelope, string name, RunAs runAs) : ControlMessage(envelope, runAs) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class Abort : ControlMessage { - private readonly string _name; - - public Abort(IEnvelope envelope, string name, RunAs runAs) - : base(envelope, runAs) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class Abort(IEnvelope envelope, string name, RunAs runAs) : ControlMessage(envelope, runAs) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class UpdateQuery : ControlMessage { - private readonly string _name; - private readonly string _query; - private readonly bool? _emitEnabled; - - public UpdateQuery( - IEnvelope envelope, string name, RunAs runAs, string query, bool? emitEnabled) - : base(envelope, runAs) { - _name = name; - _query = query; - _emitEnabled = emitEnabled; - } - - public string Query { - get { return _query; } - } - - public string Name { - get { return _name; } - } - - public bool? EmitEnabled { - get { return _emitEnabled; } - } + public partial class UpdateQuery(IEnvelope envelope, string name, RunAs runAs, string query, bool? emitEnabled) + : ControlMessage(envelope, runAs) { + public string Query { get; } = query; + public string Name { get; } = name; + public bool? EmitEnabled { get; } = emitEnabled; } [DerivedMessage(ProjectionMessage.Management)] - public partial class Reset : ControlMessage { - private readonly string _name; - - public Reset(IEnvelope envelope, string name, RunAs runAs) - : base(envelope, runAs) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class Reset(IEnvelope envelope, string name, RunAs runAs) : ControlMessage(envelope, runAs) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class Delete : ControlMessage { - private readonly string _name; - private readonly bool _deleteCheckpointStream; - private readonly bool _deleteStateStream; - private readonly bool _deleteEmittedStreams; - - public Delete( - IEnvelope envelope, string name, RunAs runAs, bool deleteCheckpointStream, bool deleteStateStream, - bool deleteEmittedStreams) - : base(envelope, runAs) { - _name = name; - _deleteCheckpointStream = deleteCheckpointStream; - _deleteStateStream = deleteStateStream; - _deleteEmittedStreams = deleteEmittedStreams; - } - - public string Name { - get { return _name; } - } - - public bool DeleteCheckpointStream { - get { return _deleteCheckpointStream; } - } - - public bool DeleteStateStream { - get { return _deleteStateStream; } - } - - public bool DeleteEmittedStreams { - get { return _deleteEmittedStreams; } - } + public partial class Delete( + IEnvelope envelope, + string name, + RunAs runAs, + bool deleteCheckpointStream, + bool deleteStateStream, + bool deleteEmittedStreams) + : ControlMessage(envelope, runAs) { + public string Name { get; } = name; + public bool DeleteCheckpointStream { get; } = deleteCheckpointStream; + public bool DeleteStateStream { get; } = deleteStateStream; + public bool DeleteEmittedStreams { get; } = deleteEmittedStreams; } [DerivedMessage(ProjectionMessage.Management)] - public partial class GetQuery : ControlMessage { - private readonly string _name; - - public GetQuery(IEnvelope envelope, string name, RunAs runAs) : - base(envelope, runAs) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class GetQuery(IEnvelope envelope, string name, RunAs runAs) : ControlMessage(envelope, runAs) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class GetConfig : ControlMessage { - private readonly string _name; - - public GetConfig(IEnvelope envelope, string name, RunAs runAs) : - base(envelope, runAs) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class GetConfig(IEnvelope envelope, string name, RunAs runAs) : ControlMessage(envelope, runAs) { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class UpdateConfig : ControlMessage { - private readonly string _name; - private readonly bool _emitEnabled; - private readonly bool _trackEmittedStreams; - private readonly int _checkpointAfterMs; - private readonly int _checkpointHandledThreshold; - private readonly int _checkpointUnhandledBytesThreshold; - private readonly int _pendingEventsThreshold; - private readonly int _maxWriteBatchLength; - private readonly int _maxAllowedWritesInFlight; - private readonly int? _projectionExecutionTimeout; - - public UpdateConfig(IEnvelope envelope, string name, bool emitEnabled, bool trackEmittedStreams, - int checkpointAfterMs, - int checkpointHandledThreshold, int checkpointUnhandledBytesThreshold, int pendingEventsThreshold, - int maxWriteBatchLength, int maxAllowedWritesInFlight, RunAs runAs, int? projectionExecutionTimeout) : - base(envelope, runAs) { - _name = name; - _emitEnabled = emitEnabled; - _trackEmittedStreams = trackEmittedStreams; - _checkpointAfterMs = checkpointAfterMs; - _checkpointHandledThreshold = checkpointHandledThreshold; - _checkpointUnhandledBytesThreshold = checkpointUnhandledBytesThreshold; - _pendingEventsThreshold = pendingEventsThreshold; - _maxWriteBatchLength = maxWriteBatchLength; - _maxAllowedWritesInFlight = maxAllowedWritesInFlight; - _projectionExecutionTimeout = projectionExecutionTimeout; - } - - public string Name { - get { return _name; } - } - - public bool EmitEnabled { - get { return _emitEnabled; } - } - - public bool TrackEmittedStreams { - get { return _trackEmittedStreams; } - } - - public int CheckpointAfterMs { - get { return _checkpointAfterMs; } - } - - public int CheckpointHandledThreshold { - get { return _checkpointHandledThreshold; } - } - - public int CheckpointUnhandledBytesThreshold { - get { return _checkpointUnhandledBytesThreshold; } - } - - public int PendingEventsThreshold { - get { return _pendingEventsThreshold; } - } - - public int MaxWriteBatchLength { - get { return _maxWriteBatchLength; } - } - - public int MaxAllowedWritesInFlight { - get { return _maxAllowedWritesInFlight; } - } - - public int? ProjectionExecutionTimeout { get => _projectionExecutionTimeout; } + public partial class UpdateConfig( + IEnvelope envelope, + string name, + bool emitEnabled, + bool trackEmittedStreams, + int checkpointAfterMs, + int checkpointHandledThreshold, + int checkpointUnhandledBytesThreshold, + int pendingEventsThreshold, + int maxWriteBatchLength, + int maxAllowedWritesInFlight, + RunAs runAs, + int? projectionExecutionTimeout) + : ControlMessage(envelope, runAs) { + public string Name { get; } = name; + public bool EmitEnabled { get; } = emitEnabled; + public bool TrackEmittedStreams { get; } = trackEmittedStreams; + public int CheckpointAfterMs { get; } = checkpointAfterMs; + public int CheckpointHandledThreshold { get; } = checkpointHandledThreshold; + public int CheckpointUnhandledBytesThreshold { get; } = checkpointUnhandledBytesThreshold; + public int PendingEventsThreshold { get; } = pendingEventsThreshold; + public int MaxWriteBatchLength { get; } = maxWriteBatchLength; + public int MaxAllowedWritesInFlight { get; } = maxAllowedWritesInFlight; + public int? ProjectionExecutionTimeout { get; } = projectionExecutionTimeout; } [DerivedMessage(ProjectionMessage.Management)] - public partial class GetStatistics : Message { - private readonly IEnvelope _envelope; - private readonly ProjectionMode? _mode; - private readonly string _name; - - public GetStatistics(IEnvelope envelope, ProjectionMode? mode, string name) { - _envelope = envelope; - _mode = mode; - _name = name; - } - - public ProjectionMode? Mode { - get { return _mode; } - } - - public string Name { - get { return _name; } - } - - public IEnvelope Envelope { - get { return _envelope; } - } + public partial class GetStatistics(IEnvelope envelope, ProjectionMode? mode, string name) : Message { + public ProjectionMode? Mode { get; } = mode; + public string Name { get; } = name; + public IEnvelope Envelope { get; } = envelope; } [DerivedMessage(ProjectionMessage.Management)] - public partial class GetState : Message { - private readonly IEnvelope _envelope; - private readonly string _name; - private readonly string _partition; - - public GetState(IEnvelope envelope, string name, string partition) { - if (envelope == null) - throw new ArgumentNullException("envelope"); - if (name == null) - throw new ArgumentNullException("name"); - if (partition == null) - throw new ArgumentNullException("partition"); - _envelope = envelope; - _name = name; - _partition = partition; - } - - public string Name { - get { return _name; } - } - - public IEnvelope Envelope { - get { return _envelope; } - } - - public string Partition { - get { return _partition; } - } + public partial class GetState(IEnvelope envelope, string name, string partition) : Message { + public string Name { get; } = Ensure.NotNull(name); + public IEnvelope Envelope { get; } = Ensure.NotNull(envelope); + public string Partition { get; } = Ensure.NotNull(partition); } [DerivedMessage(ProjectionMessage.Management)] - public partial class GetResult : Message { - private readonly IEnvelope _envelope; - private readonly string _name; - private readonly string _partition; - - public GetResult(IEnvelope envelope, string name, string partition) { - if (envelope == null) - throw new ArgumentNullException("envelope"); - if (name == null) - throw new ArgumentNullException("name"); - if (partition == null) - throw new ArgumentNullException("partition"); - _envelope = envelope; - _name = name; - _partition = partition; - } - - public string Name { - get { return _name; } - } - - public IEnvelope Envelope { - get { return _envelope; } - } - - public string Partition { - get { return _partition; } - } + public partial class GetResult(IEnvelope envelope, string name, string partition) : Message { + public string Name { get; } = Ensure.NotNull(name); + public IEnvelope Envelope { get; } = Ensure.NotNull(envelope); + public string Partition { get; } = Ensure.NotNull(partition); } } [DerivedMessage(ProjectionMessage.Management)] - public partial class OperationFailed : Message { - private readonly string _reason; - - public OperationFailed(string reason) { - _reason = reason; - } - - public string Reason { - get { return _reason; } - } + public partial class OperationFailed(string reason) : Message { + public string Reason { get; } = reason; } [DerivedMessage(ProjectionMessage.Management)] - public partial class NotFound : OperationFailed { - public NotFound() - : base("Not Found") { - } - } + public partial class NotFound() : OperationFailed("Not Found"); [DerivedMessage(ProjectionMessage.Management)] - public partial class NotAuthorized : OperationFailed { - public NotAuthorized() - : base("Not authorized") { - } - } + public partial class NotAuthorized() : OperationFailed("Not authorized"); [DerivedMessage(ProjectionMessage.Management)] - public partial class Conflict : OperationFailed { - public Conflict(string reason) - : base(reason) { - } - } - - public sealed class RunAs { - private readonly ClaimsPrincipal _runAs; - - public RunAs(ClaimsPrincipal runAs) { - _runAs = runAs; - } + public partial class Conflict(string reason) : OperationFailed(reason); - private static readonly RunAs _anonymous = new RunAs(SystemAccounts.Anonymous); - private static readonly RunAs _system = new RunAs(SystemAccounts.System); + public sealed class RunAs(ClaimsPrincipal runAs) { + public static RunAs Anonymous { get; } = new(SystemAccounts.Anonymous); - public static RunAs Anonymous { - get { return _anonymous; } - } + public static RunAs System { get; } = new(SystemAccounts.System); - public static RunAs System { - get { return _system; } - } + public ClaimsPrincipal Principal { get; } = runAs; - public ClaimsPrincipal Principal { - get { return _runAs; } - } - - public static bool ValidateRunAs(ProjectionMode mode, ReadWrite readWrite, ClaimsPrincipal existingRunAs, - Command.ControlMessage message, bool replace = false) { + public static bool ValidateRunAs(ProjectionMode mode, ReadWrite readWrite, Command.ControlMessage message, bool replace = false) { if (mode > ProjectionMode.Transient && readWrite == ReadWrite.Write - && (message.RunAs == null || message.RunAs.Principal == null - || !( - message.RunAs.Principal.LegacyRoleCheck(SystemRoles.Admins) - || message.RunAs.Principal.LegacyRoleCheck(SystemRoles.Operations) - ))) { - message.Envelope.ReplyWith(new NotAuthorized()); - return false; - } - - if (replace && message.RunAs.Principal == null) { + && (message.RunAs?.Principal == null + || !(message.RunAs.Principal.LegacyRoleCheck(SystemRoles.Admins) + || message.RunAs.Principal.LegacyRoleCheck(SystemRoles.Operations) + )) || replace && message.RunAs.Principal == null) { message.Envelope.ReplyWith(new NotAuthorized()); return false; } @@ -545,269 +264,98 @@ public static bool ValidateRunAs(ProjectionMode mode, ReadWrite readWrite, Claim } [DerivedMessage(ProjectionMessage.Management)] - public partial class Updated : Message { - private readonly string _name; - - public Updated(string name) { - _name = name; - } - - public string Name { - get { return _name; } - } + public partial class Updated(string name) : Message { + public string Name { get; } = name; } [DerivedMessage(ProjectionMessage.Management)] - public partial class Statistics : Message { - private readonly ProjectionStatistics[] _projections; - - public Statistics(ProjectionStatistics[] projections) { - _projections = projections; - } - - public ProjectionStatistics[] Projections { - get { return _projections; } - } + public partial class Statistics(ProjectionStatistics[] projections) : Message { + public ProjectionStatistics[] Projections { get; } = projections; } [DerivedMessage] - public abstract partial class ProjectionDataBase : Message { - private readonly string _name; - private readonly string _partition; - private readonly CheckpointTag _position; - private readonly Exception _exception; - - protected ProjectionDataBase( - string name, string partition, CheckpointTag position, Exception exception = null) { - _name = name; - _partition = partition; - _position = position; - _exception = exception; - } - - public string Name { - get { return _name; } - } - - public Exception Exception { - get { return _exception; } - } - - public string Partition { - get { return _partition; } - } - - public CheckpointTag Position { - get { return _position; } - } + public abstract partial class ProjectionDataBase(string name, string partition, CheckpointTag position, Exception exception = null) + : Message { + public string Name { get; } = name; + public Exception Exception { get; } = exception; + public string Partition { get; } = partition; + public CheckpointTag Position { get; } = position; } [DerivedMessage(ProjectionMessage.Management)] - public partial class ProjectionState : ProjectionDataBase { - private readonly string _state; - - public ProjectionState( - string name, string partition, string state, CheckpointTag position, Exception exception = null) - : base(name, partition, position, exception) { - _state = state; - } - - public string State { - get { return _state; } - } + public partial class ProjectionState(string name, string partition, string state, CheckpointTag position, Exception exception = null) + : ProjectionDataBase(name, partition, position, exception) { + public string State { get; } = state; } [DerivedMessage(ProjectionMessage.Management)] - public partial class ProjectionResult : ProjectionDataBase { - private readonly string _result; - - public ProjectionResult( - string name, string partition, string result, CheckpointTag position, Exception exception = null) - : base(name, partition, position, exception) { - _result = result; - } - - public string Result { - get { return _result; } - } + public partial class ProjectionResult(string name, string partition, string result, CheckpointTag position, Exception exception = null) + : ProjectionDataBase(name, partition, position, exception) { + public string Result { get; } = result; } [DerivedMessage(ProjectionMessage.Management)] - public partial class ProjectionQuery : Message { - private readonly string _name; - private readonly string _query; - private readonly bool _emitEnabled; - private readonly ProjectionSourceDefinition _definition; - private readonly ProjectionOutputConfig _outputConfig; - private readonly string _projectionType; - private readonly bool? _trackEmittedStreams; - private readonly bool? _checkpointsEnabled; - - public ProjectionQuery( - string name, - string query, - bool emitEnabled, - string projectionType, - bool? trackEmittedStreams, - bool? checkpointsEnabled, - ProjectionSourceDefinition definition, - ProjectionOutputConfig outputConfig) { - _name = name; - _query = query; - _emitEnabled = emitEnabled; - _projectionType = projectionType; - _trackEmittedStreams = trackEmittedStreams; - _checkpointsEnabled = checkpointsEnabled; - _definition = definition; - _outputConfig = outputConfig; - } - - public string Name { - get { return _name; } - } - - public string Query { - get { return _query; } - } - - public bool EmitEnabled { - get { return _emitEnabled; } - } - - public bool? TrackEmittedStreams { - get { return _trackEmittedStreams; } - } - - public bool? CheckpointsEnabled { - get { return _checkpointsEnabled; } - } - - public string Type { - get { return _projectionType; } - } - - public ProjectionSourceDefinition Definition { - get { return _definition; } - } - - public ProjectionOutputConfig OutputConfig { - get { return _outputConfig; } - } + public partial class ProjectionQuery( + string name, + string query, + bool emitEnabled, + string projectionType, + bool? trackEmittedStreams, + bool? checkpointsEnabled, + ProjectionSourceDefinition definition, + ProjectionOutputConfig outputConfig) + : Message { + public string Name { get; } = name; + public string Query { get; } = query; + public bool EmitEnabled { get; } = emitEnabled; + public bool? TrackEmittedStreams { get; } = trackEmittedStreams; + public bool? CheckpointsEnabled { get; } = checkpointsEnabled; + public string Type { get; } = projectionType; + public ProjectionSourceDefinition Definition { get; } = definition; + public ProjectionOutputConfig OutputConfig { get; } = outputConfig; } public static partial class Internal { [DerivedMessage(ProjectionMessage.Management)] - public partial class CleanupExpired : Message { - } + public partial class CleanupExpired : Message; [DerivedMessage(ProjectionMessage.Management)] - public partial class ReadTimeout : Message { - private readonly Guid _correlationId; - private readonly string _streamId; - private readonly Dictionary _parameters; - - public Guid CorrelationId { - get { return _correlationId; } - } - - public string StreamId { - get { return _streamId; } - } - - public Dictionary Parameters { - get { return _parameters; } - } - - public ReadTimeout(Guid correlationId, string streamId, Dictionary parameters) { - _correlationId = correlationId; - _streamId = streamId; - _parameters = parameters; - } + public partial class ReadTimeout(Guid correlationId, string streamId, Dictionary parameters) + : Message { + public Guid CorrelationId { get; } = correlationId; + public string StreamId { get; } = streamId; + public Dictionary Parameters { get; } = parameters; - public ReadTimeout(Guid correlationId, string streamId) : this(correlationId, streamId, - new Dictionary()) { - } + public ReadTimeout(Guid correlationId, string streamId) : this(correlationId, streamId, new()) { } } [DerivedMessage(ProjectionMessage.Management)] - public partial class Deleted : Message { - private readonly string _name; - private readonly Guid _id; - - public Deleted(string name, Guid id) { - _name = name; - _id = id; - } - - public string Name { - get { return _name; } - } - - public Guid Id { - get { return _id; } - } + public partial class Deleted(string name, Guid id) : Message { + public string Name { get; } = name; + public Guid Id { get; } = id; } } [DerivedMessage(ProjectionMessage.Management)] - public partial class ProjectionConfig : Message { - private readonly bool _emitEnabled; - private readonly bool _trackEmittedStreams; - private readonly int _checkpointAfterMs; - private readonly int _checkpointHandledThreshold; - private readonly int _checkpointUnhandledBytesThreshold; - private readonly int _pendingEventsThreshold; - private readonly int _maxWriteBatchLength; - private readonly int _maxAllowedWritesInFlight; - private readonly int? _projectionExecutionTimeout; - - public ProjectionConfig(bool emitEnabled, bool trackEmittedStreams, int checkpointAfterMs, - int checkpointHandledThreshold, - int checkpointUnhandledBytesThreshold, int pendingEventsThreshold, int maxWriteBatchLength, - int maxAllowedWritesInFlight, int? projectionExecutionTimeout) { - _emitEnabled = emitEnabled; - _trackEmittedStreams = trackEmittedStreams; - _checkpointAfterMs = checkpointAfterMs; - _checkpointHandledThreshold = checkpointHandledThreshold; - _checkpointUnhandledBytesThreshold = checkpointUnhandledBytesThreshold; - _pendingEventsThreshold = pendingEventsThreshold; - _maxWriteBatchLength = maxWriteBatchLength; - _maxAllowedWritesInFlight = maxAllowedWritesInFlight; - _projectionExecutionTimeout = projectionExecutionTimeout; - } - - public bool EmitEnabled { - get { return _emitEnabled; } - } - - public bool TrackEmittedStreams { - get { return _trackEmittedStreams; } - } - - public int CheckpointAfterMs { - get { return _checkpointAfterMs; } - } - - public int CheckpointHandledThreshold { - get { return _checkpointHandledThreshold; } - } - - public int CheckpointUnhandledBytesThreshold { - get { return _checkpointUnhandledBytesThreshold; } - } - - public int PendingEventsThreshold { - get { return _pendingEventsThreshold; } - } - - public int MaxWriteBatchLength { - get { return _maxWriteBatchLength; } - } - - public int MaxAllowedWritesInFlight { - get { return _maxAllowedWritesInFlight; } - } - - public int? ProjectionExecutionTimeout { get => _projectionExecutionTimeout; } + public partial class ProjectionConfig( + bool emitEnabled, + bool trackEmittedStreams, + int checkpointAfterMs, + int checkpointHandledThreshold, + int checkpointUnhandledBytesThreshold, + int pendingEventsThreshold, + int maxWriteBatchLength, + int maxAllowedWritesInFlight, + int? projectionExecutionTimeout) + : Message { + public bool EmitEnabled { get; } = emitEnabled; + public bool TrackEmittedStreams { get; } = trackEmittedStreams; + public int CheckpointAfterMs { get; } = checkpointAfterMs; + public int CheckpointHandledThreshold { get; } = checkpointHandledThreshold; + public int CheckpointUnhandledBytesThreshold { get; } = checkpointUnhandledBytesThreshold; + public int PendingEventsThreshold { get; } = pendingEventsThreshold; + public int MaxWriteBatchLength { get; } = maxWriteBatchLength; + public int MaxAllowedWritesInFlight { get; } = maxAllowedWritesInFlight; + public int? ProjectionExecutionTimeout { get; } = projectionExecutionTimeout; } } diff --git a/src/KurrentDB.Projections.Core/Messages/ProjectionSubsystemMessage.cs b/src/KurrentDB.Projections.Core/Messages/ProjectionSubsystemMessage.cs index fa46e1cda87..4b6d3c4a1e6 100644 --- a/src/KurrentDB.Projections.Core/Messages/ProjectionSubsystemMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/ProjectionSubsystemMessage.cs @@ -8,75 +8,43 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class ProjectionSubsystemMessage { [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class RestartSubsystem : Message { - public IEnvelope ReplyEnvelope { get; } - - public RestartSubsystem(IEnvelope replyEnvelope) { - ReplyEnvelope = replyEnvelope; - } + public partial class RestartSubsystem(IEnvelope replyEnvelope) : Message { + public IEnvelope ReplyEnvelope { get; } = replyEnvelope; } [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class InvalidSubsystemRestart : Message { - public string SubsystemState { get; } - public string Reason { get; } - - public InvalidSubsystemRestart(string subsystemState, string reason) { - SubsystemState = subsystemState; - Reason = reason; - } + public partial class InvalidSubsystemRestart(string subsystemState, string reason) : Message { + public string SubsystemState { get; } = subsystemState; + public string Reason { get; } = reason; } [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class SubsystemRestarting : Message { - } + public partial class SubsystemRestarting : Message; [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class StartComponents : Message { - public Guid InstanceCorrelationId { get; } - - public StartComponents(Guid instanceCorrelationId) { - InstanceCorrelationId = instanceCorrelationId; - } + public partial class StartComponents(Guid instanceCorrelationId) : Message { + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class ComponentStarted : Message { - public string ComponentName { get; } - public Guid InstanceCorrelationId { get; } - - public ComponentStarted(string componentName, Guid instanceCorrelationId) { - ComponentName = componentName; - InstanceCorrelationId = instanceCorrelationId; - } + public partial class ComponentStarted(string componentName, Guid instanceCorrelationId) : Message { + public string ComponentName { get; } = componentName; + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class StopComponents : Message { - public Guid InstanceCorrelationId { get; } - - public StopComponents(Guid instanceCorrelationId) { - InstanceCorrelationId = instanceCorrelationId; - } + public partial class StopComponents(Guid instanceCorrelationId) : Message { + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class ComponentStopped : Message { - public string ComponentName { get; } - public Guid InstanceCorrelationId { get; } - - public ComponentStopped(string componentName, Guid instanceCorrelationId) { - ComponentName = componentName; - InstanceCorrelationId = instanceCorrelationId; - } + public partial class ComponentStopped(string componentName, Guid instanceCorrelationId) : Message { + public string ComponentName { get; } = componentName; + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.Subsystem)] - public partial class IODispatcherDrained : Message { - public string ComponentName { get; } - - public IODispatcherDrained(string componentName) { - ComponentName = componentName; - } + public partial class IODispatcherDrained(string componentName) : Message { + public string ComponentName { get; } = componentName; } } diff --git a/src/KurrentDB.Projections.Core/Messages/QuerySourcesDefinition.cs b/src/KurrentDB.Projections.Core/Messages/QuerySourcesDefinition.cs index 5f6c5196d74..336e153751c 100644 --- a/src/KurrentDB.Projections.Core/Messages/QuerySourcesDefinition.cs +++ b/src/KurrentDB.Projections.Core/Messages/QuerySourcesDefinition.cs @@ -23,45 +23,25 @@ public class QuerySourcesDefinition : IQuerySources { [DataMember(Name = "byCustomPartitions")] public bool ByCustomPartitions { get; set; } - bool IQuerySources.DefinesStateTransform { - get { return Options != null && Options.DefinesStateTransform; } - } + bool IQuerySources.DefinesStateTransform => Options is { DefinesStateTransform: true }; - bool IQuerySources.ProducesResults { - get { return Options != null && Options.ProducesResults; } - } + bool IQuerySources.ProducesResults => Options is { ProducesResults: true }; - bool IQuerySources.DefinesFold { - get { return Options != null && Options.DefinesFold; } - } + bool IQuerySources.DefinesFold => Options is { DefinesFold: true }; - bool IQuerySources.HandlesDeletedNotifications { - get { return Options != null && Options.HandlesDeletedNotifications; } - } + bool IQuerySources.HandlesDeletedNotifications => Options is { HandlesDeletedNotifications: true }; - bool IQuerySources.IncludeLinksOption { - get { return Options != null && Options.IncludeLinks; } - } + bool IQuerySources.IncludeLinksOption => Options is { IncludeLinks: true }; - string IQuerySources.ResultStreamNameOption { - get { return Options != null ? Options.ResultStreamName : null; } - } + string IQuerySources.ResultStreamNameOption => Options?.ResultStreamName; - string IQuerySources.PartitionResultStreamNamePatternOption { - get { return Options != null ? Options.PartitionResultStreamNamePattern : null; } - } + string IQuerySources.PartitionResultStreamNamePatternOption => Options?.PartitionResultStreamNamePattern; - bool IQuerySources.ReorderEventsOption { - get { return Options != null && Options.ReorderEvents; } - } + bool IQuerySources.ReorderEventsOption => Options is { ReorderEvents: true }; - int? IQuerySources.ProcessingLagOption { - get { return Options != null ? Options.ProcessingLag : null; } - } + int? IQuerySources.ProcessingLagOption => Options?.ProcessingLag; - bool IQuerySources.IsBiState { - get { return Options != null ? Options.IsBiState : false; } - } + bool IQuerySources.IsBiState => Options is { IsBiState: true }; [DataMember(Name = "options")] public QuerySourcesDefinitionOptions Options { get; set; } @@ -71,14 +51,14 @@ public static QuerySourcesDefinition From(IQuerySources sources) { AllStreams = sources.AllStreams, ByStreams = sources.ByStreams, ByCustomPartitions = sources.ByCustomPartitions, - Categories = (sources.Categories ?? new string[0]).ToArray(), - Events = (sources.Events ?? new string[0]).ToArray(), - Streams = (sources.Streams ?? new string[0]).ToArray(), + Categories = (sources.Categories ?? []).ToArray(), + Events = (sources.Events ?? []).ToArray(), + Streams = (sources.Streams ?? []).ToArray(), Options = new QuerySourcesDefinitionOptions { DefinesStateTransform = sources.DefinesStateTransform, ProducesResults = sources.ProducesResults, - DefinesFold = sources.DefinesFold, + // DefinesFold = sources.DefinesFold, HandlesDeletedNotifications = sources.HandlesDeletedNotifications, IncludeLinks = sources.IncludeLinksOption, PartitionResultStreamNamePattern = sources.PartitionResultStreamNamePatternOption, diff --git a/src/KurrentDB.Projections.Core/Messages/ReaderCoreServiceMessage.cs b/src/KurrentDB.Projections.Core/Messages/ReaderCoreServiceMessage.cs index 534c8fd5fae..e4f9a3e572c 100644 --- a/src/KurrentDB.Projections.Core/Messages/ReaderCoreServiceMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/ReaderCoreServiceMessage.cs @@ -8,20 +8,12 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class ReaderCoreServiceMessage { [DerivedMessage(ProjectionMessage.ReaderCoreService)] - public partial class StartReader : Message { - public Guid InstanceCorrelationId { get; } - - public StartReader(Guid instanceCorrelationId) { - InstanceCorrelationId = instanceCorrelationId; - } + public partial class StartReader(Guid instanceCorrelationId) : Message { + public Guid InstanceCorrelationId { get; } = instanceCorrelationId; } [DerivedMessage(ProjectionMessage.ReaderCoreService)] - public partial class StopReader : Message { - public Guid QueueId { get; } - - public StopReader(Guid queueId) { - QueueId = queueId; - } + public partial class StopReader(Guid queueId) : Message { + public Guid QueueId { get; } = queueId; } } diff --git a/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionManagement.cs b/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionManagement.cs index 70996c09eb7..aa576e82ac8 100644 --- a/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionManagement.cs +++ b/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionManagement.cs @@ -2,6 +2,7 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; +using KurrentDB.Common.Utils; using KurrentDB.Core.Messaging; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Projections.Core.Services.Processing.Strategies; @@ -11,68 +12,28 @@ namespace KurrentDB.Projections.Core.Messages; public static partial class ReaderSubscriptionManagement { [DerivedMessage] - public abstract partial class ReaderSubscriptionManagementMessage : Message { - private readonly Guid _subscriptionId; - - protected ReaderSubscriptionManagementMessage(Guid subscriptionId) { - _subscriptionId = subscriptionId; - } - - public Guid SubscriptionId { - get { return _subscriptionId; } - } + public abstract partial class ReaderSubscriptionManagementMessage(Guid subscriptionId) : Message { + public Guid SubscriptionId { get; } = subscriptionId; } [DerivedMessage(ProjectionMessage.ReaderSubscriptionManagement)] - public partial class Subscribe : ReaderSubscriptionManagementMessage { - private readonly CheckpointTag _fromPosition; - private readonly IReaderStrategy _readerStrategy; - private readonly ReaderSubscriptionOptions _options; - - public Subscribe( - Guid subscriptionId, CheckpointTag from, - IReaderStrategy readerStrategy, ReaderSubscriptionOptions readerSubscriptionOptions) : base( - subscriptionId) { - if (@from == null) - throw new ArgumentNullException("from"); - if (readerStrategy == null) - throw new ArgumentNullException("readerStrategy"); - _fromPosition = @from; - _readerStrategy = readerStrategy; - _options = readerSubscriptionOptions; - } - - public CheckpointTag FromPosition { - get { return _fromPosition; } - } - - public IReaderStrategy ReaderStrategy { - get { return _readerStrategy; } - } - - public ReaderSubscriptionOptions Options { - get { return _options; } - } + public partial class Subscribe( + Guid subscriptionId, + CheckpointTag from, + IReaderStrategy readerStrategy, + ReaderSubscriptionOptions readerSubscriptionOptions) + : ReaderSubscriptionManagementMessage(subscriptionId) { + public CheckpointTag FromPosition { get; } = Ensure.NotNull(from); + public IReaderStrategy ReaderStrategy { get; } = Ensure.NotNull(readerStrategy); + public ReaderSubscriptionOptions Options { get; } = readerSubscriptionOptions; } [DerivedMessage(ProjectionMessage.ReaderSubscriptionManagement)] - public partial class Pause : ReaderSubscriptionManagementMessage { - public Pause(Guid subscriptionId) - : base(subscriptionId) { - } - } + public partial class Pause(Guid subscriptionId) : ReaderSubscriptionManagementMessage(subscriptionId); [DerivedMessage(ProjectionMessage.ReaderSubscriptionManagement)] - public partial class Resume : ReaderSubscriptionManagementMessage { - public Resume(Guid subscriptionId) - : base(subscriptionId) { - } - } + public partial class Resume(Guid subscriptionId) : ReaderSubscriptionManagementMessage(subscriptionId); [DerivedMessage(ProjectionMessage.ReaderSubscriptionManagement)] - public partial class Unsubscribe : ReaderSubscriptionManagementMessage { - public Unsubscribe(Guid subscriptionId) - : base(subscriptionId) { - } - } + public partial class Unsubscribe(Guid subscriptionId) : ReaderSubscriptionManagementMessage(subscriptionId); } diff --git a/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionMessage.cs b/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionMessage.cs index 78cdf725fe4..fdf02174c1c 100644 --- a/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionMessage.cs +++ b/src/KurrentDB.Projections.Core/Messages/ReaderSubscriptionMessage.cs @@ -4,127 +4,73 @@ using System; using KurrentDB.Core.Data; using KurrentDB.Core.Messaging; -using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using ResolvedEvent = KurrentDB.Projections.Core.Services.Processing.ResolvedEvent; namespace KurrentDB.Projections.Core.Messages; public static partial class ReaderSubscriptionMessage { [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class SubscriptionMessage : Message { - private readonly Guid _correlationId; - private readonly object _source; - - public SubscriptionMessage(Guid correlationId, object source) { - _correlationId = correlationId; - _source = source; - } - - public Guid CorrelationId { - get { return _correlationId; } - } - - public object Source { - get { return _source; } - } + public partial class SubscriptionMessage(Guid correlationId, object source) : Message { + public Guid CorrelationId { get; } = correlationId; + public object Source { get; } = source; } [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class EventReaderIdle : SubscriptionMessage { - private readonly DateTime _idleTimestampUtc; - - public EventReaderIdle(Guid correlationId, DateTime idleTimestampUtc, object source = null) - : base(correlationId, source) { - _idleTimestampUtc = idleTimestampUtc; - } - - public DateTime IdleTimestampUtc { - get { return _idleTimestampUtc; } - } + public partial class EventReaderIdle(Guid correlationId, DateTime idleTimestampUtc, object source = null) + : SubscriptionMessage(correlationId, source) { + public DateTime IdleTimestampUtc { get; } = idleTimestampUtc; } [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public sealed partial class EventReaderStarting : SubscriptionMessage { - private readonly long _lastCommitPosition; - - public EventReaderStarting(Guid correlationId, long lastCommitPosition, object source = null) - : base(correlationId, source) { - _lastCommitPosition = lastCommitPosition; - } - - public long LastCommitPosition { - get { return _lastCommitPosition; } - } + public sealed partial class EventReaderStarting(Guid correlationId, long lastCommitPosition, object source = null) + : SubscriptionMessage(correlationId, source) { + public long LastCommitPosition { get; } = lastCommitPosition; } [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class EventReaderEof : SubscriptionMessage { - public EventReaderEof(Guid correlationId, object source = null) - : base(correlationId, source) { - } - } + public partial class EventReaderEof(Guid correlationId, object source = null) : SubscriptionMessage(correlationId, source); [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class EventReaderPartitionDeleted : SubscriptionMessage { - private readonly string _partition; - private readonly TFPos? _deleteLinkOrEventPosition; - private readonly TFPos? _deleteEventOrLinkTargetPosition; - private readonly string _positionStreamId; - private readonly long? _positionEventNumber; - - public EventReaderPartitionDeleted( - Guid correlationId, string partition, TFPos? deleteLinkOrEventPosition, - TFPos? deleteEventOrLinkTargetPosition, string positionStreamId, long? positionEventNumber, object source = null) - : base(correlationId, source) { - _partition = partition; - _deleteLinkOrEventPosition = deleteLinkOrEventPosition; - _deleteEventOrLinkTargetPosition = deleteEventOrLinkTargetPosition; - _positionStreamId = positionStreamId; - _positionEventNumber = positionEventNumber; - } - - public string Partition { - get { return _partition; } - } - - public TFPos? DeleteEventOrLinkTargetPosition { - get { return _deleteEventOrLinkTargetPosition; } - } - - public string PositionStreamId { - get { return _positionStreamId; } - } - - public long? PositionEventNumber { - get { return _positionEventNumber; } - } - - public TFPos? DeleteLinkOrEventPosition { - get { return _deleteLinkOrEventPosition; } - } + public partial class EventReaderPartitionDeleted( + Guid correlationId, + string partition, + TFPos? deleteLinkOrEventPosition, + TFPos? deleteEventOrLinkTargetPosition, + string positionStreamId, + long? positionEventNumber, + object source = null) + : SubscriptionMessage(correlationId, source) { + public string Partition { get; } = partition; + public TFPos? DeleteEventOrLinkTargetPosition { get; } = deleteEventOrLinkTargetPosition; + public string PositionStreamId { get; } = positionStreamId; + public long? PositionEventNumber { get; } = positionEventNumber; + public TFPos? DeleteLinkOrEventPosition { get; } = deleteLinkOrEventPosition; } [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public sealed partial class EventReaderNotAuthorized : SubscriptionMessage { - public EventReaderNotAuthorized(Guid correlationId, object source = null) - : base(correlationId, source) { - } - } + public sealed partial class EventReaderNotAuthorized(Guid correlationId, object source = null) + : SubscriptionMessage(correlationId, source); [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class CommittedEventDistributed : SubscriptionMessage { + public partial class CommittedEventDistributed( + Guid correlationId, + ResolvedEvent data, + long? safeTransactionFileReaderJoinPosition, + float progress, + object source = null) + : SubscriptionMessage(correlationId, source) { public static CommittedEventDistributed Sample( Guid correlationId, TFPos position, TFPos originalPosition, string positionStreamId, long positionSequenceNumber, string eventStreamId, long eventSequenceNumber, bool resolvedLinkTo, Guid eventId, string eventType, bool isJson, byte[] data, byte[] metadata, long? safeTransactionFileReaderJoinPosition, float progress) { - return new CommittedEventDistributed( + return new( correlationId, new ResolvedEvent( positionStreamId, positionSequenceNumber, eventStreamId, eventSequenceNumber, resolvedLinkTo, position, originalPosition, eventId, eventType, isJson, data, metadata, null, null, - default(DateTime)), + default), safeTransactionFileReaderJoinPosition, progress); } @@ -132,7 +78,7 @@ public static CommittedEventDistributed Sample( Guid correlationId, TFPos position, string eventStreamId, long eventSequenceNumber, bool resolvedLinkTo, Guid eventId, string eventType, bool isJson, byte[] data, byte[] metadata, DateTime? timestamp = null) { - return new CommittedEventDistributed( + return new( correlationId, new ResolvedEvent( eventStreamId, eventSequenceNumber, eventStreamId, eventSequenceNumber, resolvedLinkTo, @@ -142,57 +88,24 @@ public static CommittedEventDistributed Sample( position.PreparePosition, 11.1f); } - private readonly ResolvedEvent _data; - private readonly long? _safeTransactionFileReaderJoinPosition; - private readonly float _progress; - //NOTE: committed event with null event _data means - end of the source reached. // Current last available TF commit position is in _position.CommitPosition // TODO: separate message? - public CommittedEventDistributed( - Guid correlationId, ResolvedEvent data, long? safeTransactionFileReaderJoinPosition, float progress, - object source = null, CheckpointTag preTagged = null) - : base(correlationId, source) { - _data = data; - _safeTransactionFileReaderJoinPosition = safeTransactionFileReaderJoinPosition; - _progress = progress; - } - - public CommittedEventDistributed(Guid correlationId, ResolvedEvent data, CheckpointTag preTagged = null) - : this(correlationId, data, data.Position.PreparePosition, 11.1f, preTagged) { + public CommittedEventDistributed(Guid correlationId, ResolvedEvent data) + : this(correlationId, data, data.Position.PreparePosition, 11.1f) { } - public ResolvedEvent Data { - get { return _data; } - } - - public long? SafeTransactionFileReaderJoinPosition { - get { return _safeTransactionFileReaderJoinPosition; } - } - - public float Progress { - get { return _progress; } - } + public ResolvedEvent Data { get; } = data; + public long? SafeTransactionFileReaderJoinPosition { get; } = safeTransactionFileReaderJoinPosition; + public float Progress { get; } = progress; } [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class Faulted : SubscriptionMessage { - private readonly string _reason; - - public Faulted( - Guid correlationId, string reason, object source = null) - : base(correlationId, source) { - _reason = reason; - } - - public string Reason { - get { return _reason; } - } + public partial class Faulted(Guid correlationId, string reason, object source = null) : SubscriptionMessage(correlationId, source) { + public string Reason { get; } = reason; } [DerivedMessage(ProjectionMessage.ReaderSubscription)] - public partial class ReportProgress : SubscriptionMessage { - public ReportProgress(Guid correlationId, object source = null) : base(correlationId, source) { } - } + public partial class ReportProgress(Guid correlationId, object source = null) : SubscriptionMessage(correlationId, source); } diff --git a/src/KurrentDB.Projections.Core/Messaging/PublishToWrapEnvelop.cs b/src/KurrentDB.Projections.Core/Messaging/PublishToWrapEnvelop.cs index 1932b5061cc..64f5389e07c 100644 --- a/src/KurrentDB.Projections.Core/Messaging/PublishToWrapEnvelop.cs +++ b/src/KurrentDB.Projections.Core/Messaging/PublishToWrapEnvelop.cs @@ -8,18 +8,8 @@ namespace KurrentDB.Projections.Core.Messaging; // When this envelope is replied to it puts that message in the original envelope, however // it arranges for this to be done elsewhere by sending a message on the provided publisher. -class PublishToWrapEnvelop : IEnvelope { - private readonly IPublisher _publisher; - private readonly IEnvelope _nestedEnvelope; - private readonly string _extraInformation; - - public PublishToWrapEnvelop(IPublisher publisher, IEnvelope nestedEnvelope, string extraInformation) { - _publisher = publisher; - _nestedEnvelope = nestedEnvelope; - _extraInformation = extraInformation; - } - +internal class PublishToWrapEnvelop(IPublisher publisher, IEnvelope nestedEnvelope, string extraInformation) : IEnvelope { public void ReplyWith(T message) where T : Message { - _publisher.Publish(new UnwrapEnvelopeMessage(() => _nestedEnvelope.ReplyWith(message), _extraInformation)); + publisher.Publish(new UnwrapEnvelopeMessage(() => nestedEnvelope.ReplyWith(message), extraInformation)); } } diff --git a/src/KurrentDB.Projections.Core/Messaging/UnwrapEnvelopeMessage.cs b/src/KurrentDB.Projections.Core/Messaging/UnwrapEnvelopeMessage.cs index 894b051d187..e0285de563c 100644 --- a/src/KurrentDB.Projections.Core/Messaging/UnwrapEnvelopeMessage.cs +++ b/src/KurrentDB.Projections.Core/Messaging/UnwrapEnvelopeMessage.cs @@ -8,20 +8,8 @@ namespace KurrentDB.Projections.Core.Messaging; [DerivedMessage(ProjectionMessage.Misc)] -public partial class UnwrapEnvelopeMessage : Message { - private readonly Action _action; - private readonly string _extraInformation; +public partial class UnwrapEnvelopeMessage(Action action, string extraInformation) : Message { + public Action Action { get; } = action; - public UnwrapEnvelopeMessage(Action action, string extraInformation) { - _action = action; - _extraInformation = extraInformation; - } - - public Action Action { - get { return _action; } - } - - public override string ToString() => - $"{base.ToString()}, " + - $"Extra Information: {_extraInformation}"; + public override string ToString() => $"{base.ToString()}, Extra Information: {extraInformation}"; } diff --git a/src/KurrentDB.Projections.Core/Metrics/IProjectionStateSerializationTracker.cs b/src/KurrentDB.Projections.Core/Metrics/IProjectionStateSerializationTracker.cs index 019d4f0b77f..4281d4eea3e 100644 --- a/src/KurrentDB.Projections.Core/Metrics/IProjectionStateSerializationTracker.cs +++ b/src/KurrentDB.Projections.Core/Metrics/IProjectionStateSerializationTracker.cs @@ -6,7 +6,7 @@ namespace KurrentDB.Projections.Core.Metrics; public interface IProjectionStateSerializationTracker { - public static IProjectionStateSerializationTracker NoOp { get; } = NoOpTracker.Instance; + public static IProjectionStateSerializationTracker NoOp => NoOpTracker.Instance; public void StateSerialized(Instant start); } diff --git a/src/KurrentDB.Projections.Core/Metrics/ProjectionTracker.cs b/src/KurrentDB.Projections.Core/Metrics/ProjectionTracker.cs index 6b9296c8df5..4a23f89bce5 100644 --- a/src/KurrentDB.Projections.Core/Metrics/ProjectionTracker.cs +++ b/src/KurrentDB.Projections.Core/Metrics/ProjectionTracker.cs @@ -10,13 +10,10 @@ namespace KurrentDB.Projections.Core.Metrics; public class ProjectionTracker : IProjectionTracker { - - public const string Projection = "projection"; public static KeyValuePair StatusRunning = new("status", "Running"); public static KeyValuePair StatusFaulted = new("status", "Faulted"); public static KeyValuePair StatusStopped = new("status", "Stopped"); - private ProjectionStatistics[] _currentStats = []; public void OnNewStats(ProjectionStatistics[] newStats) { diff --git a/src/KurrentDB.Projections.Core/ProjectionCoreWorkersNode.cs b/src/KurrentDB.Projections.Core/ProjectionCoreWorkersNode.cs index 954ce55dc54..2ec03488de6 100644 --- a/src/KurrentDB.Projections.Core/ProjectionCoreWorkersNode.cs +++ b/src/KurrentDB.Projections.Core/ProjectionCoreWorkersNode.cs @@ -28,13 +28,13 @@ public static Dictionary CreateCoreWorkers( while (coreWorkers.Count < projectionsStandardComponents.ProjectionWorkerThreadCount) { var coreInputBus = new InMemoryBus("bus"); var coreInputQueue = new QueuedHandlerThreadPool(coreInputBus, - "Projection Core #" + coreWorkers.Count, + $"Projection Core #{coreWorkers.Count}", standardComponents.QueueStatsManager, standardComponents.QueueTrackers, groupName: "Projection Core"); var coreOutputBus = new InMemoryBus("output bus"); var coreOutputQueue = new QueuedHandlerThreadPool(coreOutputBus, - "Projection Core #" + coreWorkers.Count + " output", + $"Projection Core #{coreWorkers.Count} output", standardComponents.QueueStatsManager, standardComponents.QueueTrackers, groupName: "Projection Core"); @@ -67,12 +67,9 @@ public static Dictionary CreateCoreWorkers( coreOutput.Subscribe(forwarder); if (projectionsStandardComponents.RunProjections >= ProjectionType.System) { - coreOutput.Subscribe( - Forwarder.Create(standardComponents.MainQueue)); - coreOutput.Subscribe( - Forwarder.Create(standardComponents.MainQueue)); - coreOutput.Subscribe( - Forwarder.Create(projectionsStandardComponents + coreOutput.Subscribe(Forwarder.Create(standardComponents.MainQueue)); + coreOutput.Subscribe(Forwarder.Create(standardComponents.MainQueue)); + coreOutput.Subscribe(Forwarder.Create(projectionsStandardComponents .LeaderOutputQueue)); } @@ -82,7 +79,7 @@ public static Dictionary CreateCoreWorkers( coreInputBus.Subscribe(new UnwrapEnvelopeHandler()); - coreWorkers.Add(workerId, new CoreWorker(workerId, coreInputQueue, coreOutputQueue)); + coreWorkers.Add(workerId, new CoreWorker(coreInputQueue, coreOutputQueue)); } var queues = coreWorkers.Select(v => v.Value.CoreInputQueue).ToArray(); @@ -100,24 +97,16 @@ public static Dictionary CreateCoreWorkers( } } -public class CoreWorker { - public Guid WorkerId { get; } - public IQueuedHandler CoreInputQueue { get; } - public IQueuedHandler CoreOutputQueue { get; } - - public CoreWorker(Guid workerId, IQueuedHandler inputQueue, IQueuedHandler outputQueue) { - WorkerId = workerId; - CoreInputQueue = inputQueue; - CoreOutputQueue = outputQueue; - } +public class CoreWorker(IQueuedHandler inputQueue, IQueuedHandler outputQueue) { + public IQueuedHandler CoreInputQueue { get; } = inputQueue; public void Start() { CoreInputQueue.Start(); - CoreOutputQueue.Start(); + outputQueue.Start(); } public async Task Stop() { await CoreInputQueue.Stop(); - await CoreOutputQueue.Stop(); + await outputQueue.Stop(); } } diff --git a/src/KurrentDB.Projections.Core/ProjectionManagerNode.cs b/src/KurrentDB.Projections.Core/ProjectionManagerNode.cs index aa5f65ffdd3..17c866f13f9 100644 --- a/src/KurrentDB.Projections.Core/ProjectionManagerNode.cs +++ b/src/KurrentDB.Projections.Core/ProjectionManagerNode.cs @@ -22,7 +22,7 @@ namespace KurrentDB.Projections.Core; -public class ProjectionManagerNode { +public static class ProjectionManagerNode { public static void CreateManagerService( StandardComponents standardComponents, ProjectionsStandardComponents projectionsStandardComponents, @@ -140,8 +140,7 @@ private static void SubscribeOutputBus( managerOutput.Subscribe(standardComponents.TimerService); managerOutput.Subscribe(Forwarder.Create(standardComponents.MainQueue)); - managerOutput.Subscribe( - Forwarder.Create(standardComponents.MainQueue)); + managerOutput.Subscribe(Forwarder.Create(standardComponents.MainQueue)); managerOutput.Subscribe(forwarder); // self forward all diff --git a/src/KurrentDB.Projections.Core/ProjectionWorkerNode.cs b/src/KurrentDB.Projections.Core/ProjectionWorkerNode.cs index e714077aaf6..3c670c301d1 100644 --- a/src/KurrentDB.Projections.Core/ProjectionWorkerNode.cs +++ b/src/KurrentDB.Projections.Core/ProjectionWorkerNode.cs @@ -21,11 +21,8 @@ namespace KurrentDB.Projections.Core; public class ProjectionWorkerNode { private readonly ProjectionType _runProjections; private readonly ProjectionCoreService _projectionCoreService; - private readonly ISubscriber _coreOutputBus; private readonly EventReaderCoreService _eventReaderCoreService; - private readonly ReaderSubscriptionDispatcher _subscriptionDispatcher; - private readonly FeedReaderService _feedReaderService; private readonly IODispatcher _ioDispatcher; private readonly IPublisher _leaderOutputQueue; @@ -42,23 +39,20 @@ public ProjectionWorkerNode( IPublisher leaderOutputQueue, ProjectionsStandardComponents configuration) { _runProjections = runProjections; - Ensure.NotNull(dbConfig, "dbConfig"); + Ensure.NotNull(dbConfig); _leaderOutputQueue = leaderOutputQueue; - _coreOutputBus = outputBus; - - _subscriptionDispatcher = new ReaderSubscriptionDispatcher(outputQueue); - - _ioDispatcher = new IODispatcher(outputQueue, inputQueue, true); - _eventReaderCoreService = new EventReaderCoreService( + CoreOutputBus = outputBus; + _subscriptionDispatcher = new(outputQueue); + _ioDispatcher = new(outputQueue, inputQueue, true); + _eventReaderCoreService = new( outputQueue, - _ioDispatcher, 10, dbConfig.WriterCheckpoint, runHeadingReader: runProjections >= ProjectionType.System, faultOutOfOrderProjections: faultOutOfOrderProjections); - _feedReaderService = new FeedReaderService(_subscriptionDispatcher, timeProvider); + _feedReaderService = new(_subscriptionDispatcher, timeProvider); if (runProjections >= ProjectionType.System) { _projectionCoreService = new ProjectionCoreService( workerId, @@ -71,17 +65,14 @@ public ProjectionWorkerNode( } } - public ISubscriber CoreOutputBus { - get { return _coreOutputBus; } - } + public ISubscriber CoreOutputBus { get; } public void SetupMessaging(ISubscriber coreInputBus) { coreInputBus.Subscribe(_subscriptionDispatcher .CreateSubscriber()); coreInputBus.Subscribe(_subscriptionDispatcher .CreateSubscriber()); - coreInputBus.Subscribe( - _subscriptionDispatcher.CreateSubscriber()); + coreInputBus.Subscribe(_subscriptionDispatcher.CreateSubscriber()); coreInputBus.Subscribe(_subscriptionDispatcher .CreateSubscriber()); coreInputBus.Subscribe(_subscriptionDispatcher diff --git a/src/KurrentDB.Projections.Core/ProjectionsStandardComponents.cs b/src/KurrentDB.Projections.Core/ProjectionsStandardComponents.cs index 8d7990bd507..3423fcce93f 100644 --- a/src/KurrentDB.Projections.Core/ProjectionsStandardComponents.cs +++ b/src/KurrentDB.Projections.Core/ProjectionsStandardComponents.cs @@ -7,47 +7,27 @@ namespace KurrentDB.Projections.Core; -public class ProjectionsStandardComponents { - public ProjectionsStandardComponents( - int projectionWorkerThreadCount, - ProjectionType runProjections, - ISubscriber leaderOutputBus, - IPublisher leaderOutputQueue, - ISubscriber leaderInputBus, - IPublisher leaderInputQueue, - bool faultOutOfOrderProjections, int projectionCompilationTimeout, int projectionExecutionTimeout, - int maxProjectionStateSize, - ProjectionTrackers projectionTrackers) { - ProjectionWorkerThreadCount = projectionWorkerThreadCount; - RunProjections = runProjections; - LeaderOutputBus = leaderOutputBus; - LeaderOutputQueue = leaderOutputQueue; - LeaderInputQueue = leaderInputQueue; - LeaderInputBus = leaderInputBus; - FaultOutOfOrderProjections = faultOutOfOrderProjections; - ProjectionCompilationTimeout = projectionCompilationTimeout; - ProjectionExecutionTimeout = projectionExecutionTimeout; - MaxProjectionStateSize = maxProjectionStateSize; - ProjectionTrackers = projectionTrackers; - } - - public int ProjectionWorkerThreadCount { get; } - - public ProjectionType RunProjections { get; } - - public ISubscriber LeaderOutputBus { get; } - public IPublisher LeaderOutputQueue { get; } - - public IPublisher LeaderInputQueue { get; } - public ISubscriber LeaderInputBus { get; } - - public bool FaultOutOfOrderProjections { get; } - - public int ProjectionCompilationTimeout { get; } - - public int ProjectionExecutionTimeout { get; } - - public int MaxProjectionStateSize { get; } - - public ProjectionTrackers ProjectionTrackers { get; } +public class ProjectionsStandardComponents( + int projectionWorkerThreadCount, + ProjectionType runProjections, + ISubscriber leaderOutputBus, + IPublisher leaderOutputQueue, + ISubscriber leaderInputBus, + IPublisher leaderInputQueue, + bool faultOutOfOrderProjections, + int projectionCompilationTimeout, + int projectionExecutionTimeout, + int maxProjectionStateSize, + ProjectionTrackers projectionTrackers) { + public int ProjectionWorkerThreadCount { get; } = projectionWorkerThreadCount; + public ProjectionType RunProjections { get; } = runProjections; + public ISubscriber LeaderOutputBus { get; } = leaderOutputBus; + public IPublisher LeaderOutputQueue { get; } = leaderOutputQueue; + public IPublisher LeaderInputQueue { get; } = leaderInputQueue; + public ISubscriber LeaderInputBus { get; } = leaderInputBus; + public bool FaultOutOfOrderProjections { get; } = faultOutOfOrderProjections; + public int ProjectionCompilationTimeout { get; } = projectionCompilationTimeout; + public int ProjectionExecutionTimeout { get; } = projectionExecutionTimeout; + public int MaxProjectionStateSize { get; } = maxProjectionStateSize; + public ProjectionTrackers ProjectionTrackers { get; } = projectionTrackers; } diff --git a/src/KurrentDB.Projections.Core/ProjectionsSubsystem.cs b/src/KurrentDB.Projections.Core/ProjectionsSubsystem.cs index 646f028e24e..847fe0d9915 100644 --- a/src/KurrentDB.Projections.Core/ProjectionsSubsystem.cs +++ b/src/KurrentDB.Projections.Core/ProjectionsSubsystem.cs @@ -40,16 +40,16 @@ public record ProjectionSubsystemOptions( int ExecutionTimeout, int MaxProjectionStateSize); -public sealed class ProjectionsSubsystem : ISubsystem, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle { - +public sealed class ProjectionsSubsystem + : ISubsystem, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle { static readonly ILogger Logger = Log.ForContext(); public const int VERSION = 4; @@ -60,8 +60,8 @@ public sealed class ProjectionsSubsystem : ISubsystem, private readonly bool _startStandardProjections; private readonly TimeSpan _projectionsQueryExpiry; - private readonly InMemoryBus _leaderInputBus; - private readonly InMemoryBus _leaderOutputBus; + private readonly InMemoryBus _leaderInputBus = new("manager input bus"); + private readonly InMemoryBus _leaderOutputBus = new("ProjectionManagerAndCoreCoordinatorOutput"); private IQueuedHandler _leaderInputQueue; private IQueuedHandler _leaderOutputQueue; @@ -69,7 +69,7 @@ public sealed class ProjectionsSubsystem : ISubsystem, private IDictionary _coreWorkers; private Dictionary _queueMap; private bool _subsystemStarted; - private readonly TaskCompletionSource _subsystemInitialized; + private readonly TaskCompletionSource _subsystemInitialized = new(); private readonly bool _faultOutOfOrderProjections; @@ -88,19 +88,18 @@ public sealed class ProjectionsSubsystem : ISubsystem, private SubsystemState _subsystemState = SubsystemState.NotReady; private Guid _instanceCorrelationId; - private readonly List _standardProjections = new List { + private readonly List _standardProjections = [ "$by_category", "$stream_by_category", "$streams", "$by_event_type", "$by_correlation_id" - }; + ]; public ProjectionsSubsystem(ProjectionSubsystemOptions projectionSubsystemOptions) { - if (projectionSubsystemOptions.RunProjections <= ProjectionType.System) - _projectionWorkerThreadCount = 1; - else - _projectionWorkerThreadCount = projectionSubsystemOptions.ProjectionWorkerThreadCount; + _projectionWorkerThreadCount = projectionSubsystemOptions.RunProjections <= ProjectionType.System + ? 1 + : projectionSubsystemOptions.ProjectionWorkerThreadCount; _runProjections = projectionSubsystemOptions.RunProjections; // Projection manager & Projection Core Coordinator @@ -113,11 +112,6 @@ public ProjectionsSubsystem(ProjectionSubsystemOptions projectionSubsystemOption _startStandardProjections = projectionSubsystemOptions.StartStandardProjections; _projectionsQueryExpiry = projectionSubsystemOptions.ProjectionQueryExpiry; _faultOutOfOrderProjections = projectionSubsystemOptions.FaultOutOfOrderProjections; - - _leaderInputBus = new InMemoryBus("manager input bus"); - _leaderOutputBus = new InMemoryBus("ProjectionManagerAndCoreCoordinatorOutput"); - - _subsystemInitialized = new(); _executionTimeout = projectionSubsystemOptions.ExecutionTimeout; _compilationTimeout = projectionSubsystemOptions.CompilationTimeout; _maxProjectionStateSize = projectionSubsystemOptions.MaxProjectionStateSize; @@ -191,7 +185,6 @@ private static void ConfigureProjectionMetrics( MetricsConfiguration conf, out IProjectionTracker projectionTracker, out ProjectionTrackers projectionTrackers) { - projectionTracker = IProjectionTracker.NoOp; Func serializationTrackerFactory = @@ -204,7 +197,8 @@ private static void ConfigureProjectionMetrics( var tracker = new ProjectionTracker(); projectionTracker = tracker; - projectionMeter.CreateObservableCounter($"{serviceName}-projection-events-processed-after-restart-total", tracker.ObserveEventsProcessed); + projectionMeter.CreateObservableCounter($"{serviceName}-projection-events-processed-after-restart-total", + tracker.ObserveEventsProcessed); projectionMeter.CreateObservableUpDownCounter($"{serviceName}-projection-progress", tracker.ObserveProgress); projectionMeter.CreateObservableUpDownCounter($"{serviceName}-projection-running", tracker.ObserveRunning); projectionMeter.CreateObservableUpDownCounter($"{serviceName}-projection-status", tracker.ObserveStatus); @@ -220,7 +214,7 @@ private static void ConfigureProjectionMetrics( conf.LegacyProjectionsNaming), name: name, expectedScrapeIntervalSeconds: conf.ExpectedScrapeIntervalSeconds - )); + )); } List> executionTrackerFactories = []; @@ -258,7 +252,8 @@ IProjectionExecutionTracker ExecutionTrackerFactory(string name) } public void ConfigureServices(IServiceCollection services, IConfiguration configuration) => - services.AddSingleton(provider => new ProjectionManagement(_leaderInputQueue, provider.GetRequiredService())); + services.AddSingleton(provider + => new ProjectionManagement(_leaderInputQueue, provider.GetRequiredService())); private static void CreateAwakerService(StandardComponents standardComponents) { var awakeReaderService = new AwakeService(); @@ -272,12 +267,14 @@ public void Handle(SystemMessage.SystemCoreReady message) { if (_subsystemState != SubsystemState.NotReady) return; _subsystemState = SubsystemState.Ready; - if (_nodeState == VNodeState.Leader) { - StartComponents(); - return; - } - if (_nodeState == VNodeState.Follower || _nodeState == VNodeState.ReadOnlyReplica) { - PublishInitialized(); + switch (_nodeState) { + case VNodeState.Leader: + StartComponents(); + return; + case VNodeState.Follower: + case VNodeState.ReadOnlyReplica: + PublishInitialized(); + break; } } @@ -286,14 +283,16 @@ public void Handle(SystemMessage.StateChangeMessage message) { if (_subsystemState == SubsystemState.NotReady) return; - if (_nodeState == VNodeState.Leader) { - StartComponents(); - return; + switch (_nodeState) { + case VNodeState.Leader: + StartComponents(); + return; + case VNodeState.Follower: + case VNodeState.ReadOnlyReplica: + PublishInitialized(); + break; } - if (_nodeState == VNodeState.Follower || _nodeState == VNodeState.ReadOnlyReplica) { - PublishInitialized(); - } StopComponents(); } @@ -303,11 +302,14 @@ private void StartComponents() { _nodeState); return; } + if (_subsystemState != SubsystemState.Ready && _subsystemState != SubsystemState.Stopped) { - Logger.Debug("PROJECTIONS SUBSYSTEM: Not starting because system is not ready or stopped. Current Subsystem state: {subsystemState}", + Logger.Debug( + "PROJECTIONS SUBSYSTEM: Not starting because system is not ready or stopped. Current Subsystem state: {subsystemState}", _subsystemState); return; } + if (_runningComponentCount > 0) { Logger.Warning("PROJECTIONS SUBSYSTEM: Subsystem is stopped, but components are still running."); return; @@ -323,7 +325,9 @@ private void StartComponents() { private void StopComponents() { if (_subsystemState != SubsystemState.Started) { - Logger.Debug("PROJECTIONS SUBSYSTEM: Not stopping because subsystem is not in a started state. Current Subsystem state: {state}", _subsystemState); + Logger.Debug( + "PROJECTIONS SUBSYSTEM: Not stopping because subsystem is not in a started state. Current Subsystem state: {state}", + _subsystemState); return; } @@ -334,7 +338,7 @@ private void StopComponents() { public void Handle(ProjectionSubsystemMessage.RestartSubsystem message) { if (_restarting) { - var info = "PROJECTIONS SUBSYSTEM: Not restarting because the subsystem is already being restarted."; + const string info = "PROJECTIONS SUBSYSTEM: Not restarting because the subsystem is already being restarted."; Logger.Information(info); message.ReplyEnvelope.ReplyWith(new ProjectionSubsystemMessage.InvalidSubsystemRestart("Restarting", info)); return; @@ -393,7 +397,8 @@ private void AllComponentsStarted() { PublishInitialized(); if (_nodeState != VNodeState.Leader) { - Logger.Information("PROJECTIONS SUBSYSTEM: Node state is no longer Leader. Stopping projections. Current node state: {nodeState}", + Logger.Information( + "PROJECTIONS SUBSYSTEM: Node state is no longer Leader. Stopping projections. Current node state: {nodeState}", _nodeState); StopComponents(); } @@ -449,7 +454,7 @@ private void PublishInitialized() { } public Task Start() { - if (_subsystemStarted == false) { + if (!_subsystemStarted) { _leaderInputQueue?.Start(); _leaderOutputQueue?.Start(); diff --git a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Disable.cs b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Disable.cs index 4d52ef07516..f3925ec7448 100644 --- a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Disable.cs +++ b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Disable.cs @@ -31,7 +31,7 @@ public override async Task Disable(DisableReq request, ServerCallCo _publisher.Publish(options.WriteCheckpoint ? new ProjectionManagementMessage.Command.Disable(envelope, name, runAs) - : (Message)new ProjectionManagementMessage.Command.Abort(envelope, name, runAs)); + : new ProjectionManagementMessage.Command.Abort(envelope, name, runAs)); await disableSource.Task; diff --git a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.RestartSubsystem.cs b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.RestartSubsystem.cs index 3c919b5bd58..48b5b1058a2 100644 --- a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.RestartSubsystem.cs +++ b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.RestartSubsystem.cs @@ -32,7 +32,7 @@ public override async Task RestartSubsystem(Empty empty, ServerCallContex void OnMessage(Message message) { switch (message) { - case ProjectionSubsystemMessage.SubsystemRestarting _: + case ProjectionSubsystemMessage.SubsystemRestarting: restart.TrySetResult(true); break; case ProjectionSubsystemMessage.InvalidSubsystemRestart fail: diff --git a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Statistics.cs b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Statistics.cs index b78c346dd3b..646e3313364 100644 --- a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Statistics.cs +++ b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.Statistics.cs @@ -33,7 +33,7 @@ public override async Task Statistics(StatisticsReq request, IServerStreamWriter ModeOneofCase.Continuous => ProjectionMode.Continuous, ModeOneofCase.Transient => ProjectionMode.Transient, ModeOneofCase.OneTime => ProjectionMode.OneTime, - _ => default(Nullable) + _ => default(ProjectionMode?) }; var envelope = new CallbackEnvelope(OnMessage); diff --git a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.cs b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.cs index d857a72bbb9..ecdb5fc698e 100644 --- a/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.cs +++ b/src/KurrentDB.Projections.Core/Services/Grpc/ProjectionManagement.cs @@ -15,9 +15,8 @@ // ReSharper disable CheckNamespace namespace EventStore.Client.Projections { - partial class Projections { - partial class ProjectionsBase : ServiceBase { - } + internal partial class Projections { + partial class ProjectionsBase : ServiceBase; } } @@ -27,10 +26,8 @@ internal partial class ProjectionManagement : Client.Projections.Projections.Pro private readonly IAuthorizationProvider _authorizationProvider; public ProjectionManagement(IPublisher publisher, IAuthorizationProvider authorizationProvider) { - if (publisher == null) - throw new ArgumentNullException(nameof(publisher)); - if (authorizationProvider == null) - throw new ArgumentNullException(nameof(authorizationProvider)); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(authorizationProvider); _publisher = publisher; _authorizationProvider = authorizationProvider; } diff --git a/src/KurrentDB.Projections.Core/Services/Http/ProjectionsController.cs b/src/KurrentDB.Projections.Core/Services/Http/ProjectionsController.cs index 03e6e79ab4d..95fa8bdd27c 100644 --- a/src/KurrentDB.Projections.Core/Services/Http/ProjectionsController.cs +++ b/src/KurrentDB.Projections.Core/Services/Http/ProjectionsController.cs @@ -15,7 +15,6 @@ using KurrentDB.Core.Util; using KurrentDB.Projections.Core.Messages; using KurrentDB.Projections.Core.Messages.EventReaders.Feeds; -using KurrentDB.Projections.Core.Services.Processing; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Transport.Http; using KurrentDB.Transport.Http.Codecs; @@ -28,7 +27,7 @@ namespace KurrentDB.Projections.Core.Services.Http; public class ProjectionsController : CommunicationController { private static readonly ILogger Log = Serilog.Log.ForContext(); - private static readonly ICodec[] SupportedCodecs = { Codec.Json }; + private static readonly ICodec[] SupportedCodecs = [Codec.Json]; private readonly MiniWeb _clusterNodeJs; private readonly MiniWeb _miniWebPrelude; @@ -53,9 +52,9 @@ protected override void SubscribeCore(IHttpService service) { HttpHelpers.RegisterRedirectAction(service, "/web/projections", "/web/projections.htm"); Register(service, "/projections", - HttpMethod.Get, OnProjections, Codec.NoCodecs, new ICodec[] { Codec.ManualEncoding }, new Operation(Operations.Projections.List)); + HttpMethod.Get, OnProjections, Codec.NoCodecs, [Codec.ManualEncoding], new Operation(Operations.Projections.List)); Register(service, "/projections/restart", - HttpMethod.Post, OnProjectionsRestart, new ICodec[] { Codec.ManualEncoding }, SupportedCodecs, new Operation(Operations.Projections.Restart)); + HttpMethod.Post, OnProjectionsRestart, [Codec.ManualEncoding], SupportedCodecs, new Operation(Operations.Projections.Restart)); Register(service, "/projections/any", HttpMethod.Get, OnProjectionsGetAny, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.List)); Register(service, "/projections/all-non-transient", @@ -67,17 +66,23 @@ protected override void SubscribeCore(IHttpService service) { Register(service, "/projections/continuous", HttpMethod.Get, OnProjectionsGetContinuous, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.List)); Register(service, "/projections/transient?name={name}&type={type}&enabled={enabled}", - HttpMethod.Post, OnProjectionsPostTransient, new ICodec[] { Codec.ManualEncoding }, SupportedCodecs, new Operation(Operations.Projections.Create).WithParameter(Operations.Projections.Parameters.Query)); + HttpMethod.Post, OnProjectionsPostTransient, [Codec.ManualEncoding], SupportedCodecs, + new Operation(Operations.Projections.Create).WithParameter(Operations.Projections.Parameters.Query)); Register(service, "/projections/onetime?name={name}&type={type}&enabled={enabled}&checkpoints={checkpoints}&emit={emit}&trackemittedstreams={trackemittedstreams}", - HttpMethod.Post, OnProjectionsPostOneTime, new ICodec[] { Codec.ManualEncoding }, SupportedCodecs, new Operation(Operations.Projections.Create).WithParameter(Operations.Projections.Parameters.OneTime)); + HttpMethod.Post, OnProjectionsPostOneTime, [Codec.ManualEncoding], SupportedCodecs, + new Operation(Operations.Projections.Create).WithParameter(Operations.Projections.Parameters.OneTime)); Register(service, "/projections/continuous?name={name}&type={type}&enabled={enabled}&emit={emit}&trackemittedstreams={trackemittedstreams}", - HttpMethod.Post, OnProjectionsPostContinuous, new ICodec[] { Codec.ManualEncoding }, SupportedCodecs, new Operation(Operations.Projections.Create).WithParameter(Operations.Projections.Parameters.Continuous)); + HttpMethod.Post, OnProjectionsPostContinuous, [Codec.ManualEncoding], SupportedCodecs, + new Operation(Operations.Projections.Create).WithParameter(Operations.Projections.Parameters.Continuous)); Register(service, "/projection/{name}/query?config={config}", - HttpMethod.Get, OnProjectionQueryGet, Codec.NoCodecs, new ICodec[] { Codec.ManualEncoding }, new Operation(Operations.Projections.Read)); + HttpMethod.Get, OnProjectionQueryGet, Codec.NoCodecs, [Codec.ManualEncoding], + new Operation(Operations.Projections.Read)); Register(service, "/projection/{name}/query?type={type}&emit={emit}", - HttpMethod.Put, OnProjectionQueryPut, new ICodec[] { Codec.ManualEncoding }, SupportedCodecs, new Operation(Operations.Projections.Update)); /* source of transient projections can be set by a normal user. Authorization checks are done internally for non-transient projections. */ + HttpMethod.Put, OnProjectionQueryPut, [Codec.ManualEncoding], SupportedCodecs, + new Operation(Operations.Projections + .Update)); /* a normal user can set a source of transient projections. Authorization checks are done internally for non-transient projections. */ Register(service, "/projection/{name}", HttpMethod.Get, OnProjectionStatusGet, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Status)); Register(service, @@ -86,23 +91,34 @@ protected override void SubscribeCore(IHttpService service) { Register(service, "/projection/{name}/statistics", HttpMethod.Get, OnProjectionStatisticsGet, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Statistics)); Register(service, "/projections/read-events", - HttpMethod.Post, OnProjectionsReadEvents, SupportedCodecs, SupportedCodecs, new Operation(Operations.Projections.DebugProjection)); + HttpMethod.Post, OnProjectionsReadEvents, SupportedCodecs, SupportedCodecs, + new Operation(Operations.Projections.DebugProjection)); Register(service, "/projection/{name}/state?partition={partition}", HttpMethod.Get, OnProjectionStateGet, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.State)); Register(service, "/projection/{name}/result?partition={partition}", HttpMethod.Get, OnProjectionResultGet, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Result)); Register(service, "/projection/{name}/command/disable?enableRunAs={enableRunAs}", - HttpMethod.Post, OnProjectionCommandDisable, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Disable)); /* transient projections can be stopped by a normal user. Authorization checks are done internally for non-transient projections.*/ + HttpMethod.Post, OnProjectionCommandDisable, Codec.NoCodecs, SupportedCodecs, + new Operation(Operations.Projections + .Disable)); /* a normal user can stop transient projections. Authorization checks are done internally for non-transient projections.*/ Register(service, "/projection/{name}/command/enable?enableRunAs={enableRunAs}", - HttpMethod.Post, OnProjectionCommandEnable, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Enable)); /* transient projections can be enabled by a normal user. Authorization checks are done internally for non-transient projections.*/ + HttpMethod.Post, OnProjectionCommandEnable, Codec.NoCodecs, SupportedCodecs, + new Operation(Operations.Projections + .Enable)); /* a normal user can enable transient projections. Authorization checks are done internally for non-transient projections.*/ Register(service, "/projection/{name}/command/reset?enableRunAs={enableRunAs}", - HttpMethod.Post, OnProjectionCommandReset, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Reset)); /* transient projections can be reset by a normal user (when debugging). Authorization checks are done internally for non-transient projections.*/ + HttpMethod.Post, OnProjectionCommandReset, Codec.NoCodecs, SupportedCodecs, + new Operation(Operations.Projections + .Reset)); /* a normal user can reset transient projections (when debugging). Authorization checks are done internally for non-transient projections.*/ Register(service, "/projection/{name}/command/abort?enableRunAs={enableRunAs}", - HttpMethod.Post, OnProjectionCommandAbort, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.Abort)); /* transient projections can be aborted by a normal user. Authorization checks are done internally for non-transient projections.*/ + HttpMethod.Post, OnProjectionCommandAbort, Codec.NoCodecs, SupportedCodecs, + new Operation(Operations.Projections + .Abort)); /* a normal user can abort transient projections. Authorization checks are done internally for non-transient projections.*/ Register(service, "/projection/{name}/config", - HttpMethod.Get, OnProjectionConfigGet, Codec.NoCodecs, SupportedCodecs, new Operation(Operations.Projections.ReadConfiguration)); + HttpMethod.Get, OnProjectionConfigGet, Codec.NoCodecs, SupportedCodecs, + new Operation(Operations.Projections.ReadConfiguration)); Register(service, "/projection/{name}/config", - HttpMethod.Put, OnProjectionConfigPut, SupportedCodecs, SupportedCodecs, new Operation(Operations.Projections.UpdateConfiguration)); + HttpMethod.Put, OnProjectionConfigPut, SupportedCodecs, SupportedCodecs, + new Operation(Operations.Projections.UpdateConfiguration)); } private void OnProjections(HttpEntityManager http, UriTemplateMatch match) { @@ -111,49 +127,43 @@ private void OnProjections(HttpEntityManager http, UriTemplateMatch match) { http.ReplyTextContent( "Moved", 302, "Found", ContentType.PlainText, - new[] { - new KeyValuePair( - "Location", new Uri(match.BaseUri, "/web/projections.htm").AbsoluteUri) - }, x => Log.Debug(x, "Reply Text Content Failed.")); + [new("Location", new Uri(match.BaseUri, "/web/projections.htm").AbsoluteUri)], + x => Log.Debug(x, "Reply Text Content Failed.")); } private void OnProjectionsRestart(HttpEntityManager http, UriTemplateMatch match) { if (_httpForwarder.ForwardRequest(http)) return; - var envelope = new SendToHttpEnvelope(_networkSendQueue, http, - (e, message) => e.To("Restarting"), - (e, message) => { - switch (message) { - case ProjectionSubsystemMessage.SubsystemRestarting _: - return Configure.Ok(e.ContentType); - default: - return Configure.InternalServerError(); - } - }, CreateErrorEnvelope(http) + var envelope = new SendToHttpEnvelope( + _networkSendQueue, + http, + (e, _) => e.To("Restarting"), + (e, message) => message != null ? Configure.Ok(e.ContentType) : Configure.InternalServerError(), + CreateErrorEnvelope(http) ); Publish(new ProjectionSubsystemMessage.RestartSubsystem(envelope)); } private void OnProjectionsGetAny(HttpEntityManager http, UriTemplateMatch match) { - ProjectionsGet(http, match, null); + ProjectionsGet(http, null); } private void OnProjectionsGetAllNonTransient(HttpEntityManager http, UriTemplateMatch match) { - ProjectionsGet(http, match, ProjectionMode.AllNonTransient); + ProjectionsGet(http, ProjectionMode.AllNonTransient); } private void OnProjectionsGetTransient(HttpEntityManager http, UriTemplateMatch match) { - ProjectionsGet(http, match, ProjectionMode.Transient); + ProjectionsGet(http, ProjectionMode.Transient); } private void OnProjectionsGetOneTime(HttpEntityManager http, UriTemplateMatch match) { - ProjectionsGet(http, match, ProjectionMode.OneTime); + ProjectionsGet(http, ProjectionMode.OneTime); } private void OnProjectionsGetContinuous(HttpEntityManager http, UriTemplateMatch match) { - ProjectionsGet(http, match, ProjectionMode.Continuous); + ProjectionsGet(http, ProjectionMode.Continuous); } private void OnProjectionsPostTransient(HttpEntityManager http, UriTemplateMatch match) { @@ -174,14 +184,10 @@ private void OnProjectionQueryGet(HttpEntityManager http, UriTemplateMatch match SendToHttpEnvelope envelope; var withConfig = IsOn(match, "config", false); - if (withConfig) - envelope = new SendToHttpEnvelope( - _networkSendQueue, http, QueryConfigFormatter, QueryConfigConfigurator, CreateErrorEnvelope(http)); - else - envelope = new SendToHttpEnvelope( - _networkSendQueue, http, QueryFormatter, QueryConfigurator, CreateErrorEnvelope(http)); - Publish(new ProjectionManagementMessage.Command.GetQuery(envelope, match.BoundVariables["name"], - GetRunAs(http, match))); + envelope = withConfig + ? new(_networkSendQueue, http, QueryConfigFormatter, QueryConfigConfigurator, CreateErrorEnvelope(http)) + : new(_networkSendQueue, http, QueryFormatter, QueryConfigurator, CreateErrorEnvelope(http)); + Publish(new ProjectionManagementMessage.Command.GetQuery(envelope, match.BoundVariables["name"], GetRunAs(http))); } private void OnProjectionQueryPut(HttpEntityManager http, UriTemplateMatch match) { @@ -192,11 +198,11 @@ private void OnProjectionQueryPut(HttpEntityManager http, UriTemplateMatch match _networkSendQueue, http, DefaultFormatter, OkResponseConfigurator, CreateErrorEnvelope(http)); var emitEnabled = IsOn(match, "emit", null); http.ReadTextRequestAsync( - (o, s) => + (_, s) => Publish( - new ProjectionManagementMessage.Command.UpdateQuery( - envelope, match.BoundVariables["name"], GetRunAs(http, match), - s, emitEnabled: emitEnabled)), Console.WriteLine); + new ProjectionManagementMessage.Command.UpdateQuery( + envelope, match.BoundVariables["name"], GetRunAs(http), + s, emitEnabled: emitEnabled)), Console.WriteLine); } private void OnProjectionConfigGet(HttpEntityManager http, UriTemplateMatch match) { @@ -207,7 +213,7 @@ private void OnProjectionConfigGet(HttpEntityManager http, UriTemplateMatch matc _networkSendQueue, http, ProjectionConfigFormatter, ProjectionConfigConfigurator, CreateErrorEnvelope(http)); Publish( new ProjectionManagementMessage.Command.GetConfig( - envelope, match.BoundVariables["name"], GetRunAs(http, match))); + envelope, match.BoundVariables["name"], GetRunAs(http))); } private void OnProjectionConfigPut(HttpEntityManager http, UriTemplateMatch match) { @@ -228,11 +234,12 @@ private void OnProjectionConfigPut(HttpEntityManager http, UriTemplateMatch matc SendBadRequest(o, $"projectionExecutionTimeout should be positive. Found : {config.ProjectionExecutionTimeout}"); return; } + var message = new ProjectionManagementMessage.Command.UpdateConfig( envelope, match.BoundVariables["name"], config.EmitEnabled, config.TrackEmittedStreams, config.CheckpointAfterMs, config.CheckpointHandledThreshold, config.CheckpointUnhandledBytesThreshold, config.PendingEventsThreshold, - config.MaxWriteBatchLength, config.MaxAllowedWritesInFlight, GetRunAs(http, match), config.ProjectionExecutionTimeout); + config.MaxWriteBatchLength, config.MaxAllowedWritesInFlight, GetRunAs(http), config.ProjectionExecutionTimeout); Publish(message); }, ex => Log.Debug("Failed to update projection configuration. Error: {e}", ex)); } @@ -243,8 +250,7 @@ private void OnProjectionCommandDisable(HttpEntityManager http, UriTemplateMatch var envelope = new SendToHttpEnvelope( _networkSendQueue, http, DefaultFormatter, OkResponseConfigurator, CreateErrorEnvelope(http)); - Publish(new ProjectionManagementMessage.Command.Disable(envelope, match.BoundVariables["name"], - GetRunAs(http, match))); + Publish(new ProjectionManagementMessage.Command.Disable(envelope, match.BoundVariables["name"], GetRunAs(http))); } private void OnProjectionCommandEnable(HttpEntityManager http, UriTemplateMatch match) { @@ -254,7 +260,7 @@ private void OnProjectionCommandEnable(HttpEntityManager http, UriTemplateMatch var envelope = new SendToHttpEnvelope( _networkSendQueue, http, DefaultFormatter, OkResponseConfigurator, CreateErrorEnvelope(http)); var name = match.BoundVariables["name"]; - Publish(new ProjectionManagementMessage.Command.Enable(envelope, name, GetRunAs(http, match))); + Publish(new ProjectionManagementMessage.Command.Enable(envelope, name, GetRunAs(http))); } private void OnProjectionCommandReset(HttpEntityManager http, UriTemplateMatch match) { @@ -263,8 +269,7 @@ private void OnProjectionCommandReset(HttpEntityManager http, UriTemplateMatch m var envelope = new SendToHttpEnvelope( _networkSendQueue, http, DefaultFormatter, OkResponseConfigurator, CreateErrorEnvelope(http)); - Publish(new ProjectionManagementMessage.Command.Reset(envelope, match.BoundVariables["name"], - GetRunAs(http, match))); + Publish(new ProjectionManagementMessage.Command.Reset(envelope, match.BoundVariables["name"], GetRunAs(http))); } private void OnProjectionCommandAbort(HttpEntityManager http, UriTemplateMatch match) { @@ -273,8 +278,7 @@ private void OnProjectionCommandAbort(HttpEntityManager http, UriTemplateMatch m var envelope = new SendToHttpEnvelope( _networkSendQueue, http, DefaultFormatter, OkResponseConfigurator, CreateErrorEnvelope(http)); - Publish(new ProjectionManagementMessage.Command.Abort(envelope, match.BoundVariables["name"], - GetRunAs(http, match))); + Publish(new ProjectionManagementMessage.Command.Abort(envelope, match.BoundVariables["name"], GetRunAs(http))); } private void OnProjectionStatusGet(HttpEntityManager http, UriTemplateMatch match) { @@ -282,11 +286,10 @@ private void OnProjectionStatusGet(HttpEntityManager http, UriTemplateMatch matc return; var envelope = - new SendToHttpWithConversionEnvelope - ( - _networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator, - status => new ProjectionStatisticsHttpFormatted(status.Projections[0], s => MakeUrl(http, s)), - CreateErrorEnvelope(http)); + new SendToHttpWithConversionEnvelope( + _networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator, + status => new(status.Projections[0], s => MakeUrl(http, s)), + CreateErrorEnvelope(http)); Publish(new ProjectionManagementMessage.Command.GetStatistics(envelope, null, match.BoundVariables["name"])); } @@ -298,7 +301,7 @@ private void OnProjectionDelete(HttpEntityManager http, UriTemplateMatch match) _networkSendQueue, http, DefaultFormatter, OkResponseConfigurator, CreateErrorEnvelope(http)); Publish( new ProjectionManagementMessage.Command.Delete( - envelope, match.BoundVariables["name"], GetRunAs(http, match), + envelope, match.BoundVariables["name"], GetRunAs(http), IsOn(match, "deleteCheckpointStream", false), IsOn(match, "deleteStateStream", false), IsOn(match, "deleteEmittedStreams", false))); @@ -312,7 +315,7 @@ private void OnProjectionStatisticsGet(HttpEntityManager http, UriTemplateMatch new SendToHttpWithConversionEnvelope ( _networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator, - status => new ProjectionsStatisticsHttpFormatted(status, s => MakeUrl(http, s)), + status => new(status, s => MakeUrl(http, s)), CreateErrorEnvelope(http)); Publish(new ProjectionManagementMessage.Command.GetStatistics(envelope, null, match.BoundVariables["name"])); } @@ -339,7 +342,7 @@ private void OnProjectionResultGet(HttpEntityManager http, UriTemplateMatch matc envelope, match.BoundVariables["name"], match.BoundVariables["partition"] ?? "")); } - public class ReadEventsBody { + private class ReadEventsBody { public QuerySourcesDefinition Query { get; set; } public JObject Position { get; set; } public int? MaxEvents { get; set; } @@ -353,11 +356,9 @@ private void OnProjectionsReadEvents(HttpEntityManager http, UriTemplateMatch ma _networkSendQueue, http, FeedPageFormatter, FeedPageConfigurator, CreateErrorEnvelope(http)); http.ReadTextRequestAsync( - (o, body) => { + (_, body) => { var bodyParsed = body.ParseJson(); - var fromPosition = CheckpointTag.FromJson( - new JTokenReader(bodyParsed.Position), new ProjectionVersion(0, 0, 0)); - + var fromPosition = CheckpointTag.FromJson(new JTokenReader(bodyParsed.Position), new(0, 0, 0)); Publish( new FeedReaderMessage.ReadPage( @@ -371,7 +372,7 @@ private void OnProjectionsReadEvents(HttpEntityManager http, UriTemplateMatch ma x => Log.Debug(x, "Read Request Body Failed.")); } - private void ProjectionsGet(HttpEntityManager http, UriTemplateMatch match, ProjectionMode? mode) { + private void ProjectionsGet(HttpEntityManager http, ProjectionMode? mode) { if (_httpForwarder.ForwardRequest(http)) return; @@ -379,7 +380,7 @@ private void ProjectionsGet(HttpEntityManager http, UriTemplateMatch match, Proj new SendToHttpWithConversionEnvelope( _networkSendQueue, http, DefaultFormatter, OkNoCacheResponseConfigurator, - status => new ProjectionsStatisticsHttpFormatted(status, s => MakeUrl(http, s)), + status => new(status, s => MakeUrl(http, s)), CreateErrorEnvelope(http)); Publish(new ProjectionManagementMessage.Command.GetStatistics(envelope, mode, null)); } @@ -390,14 +391,14 @@ private void ProjectionsPost(HttpEntityManager http, UriTemplateMatch match, Pro var envelope = new SendToHttpEnvelope( _networkSendQueue, http, DefaultFormatter, (codec, message) => { - var localPath = string.Format("/projection/{0}", message.Name); + var localPath = $"/projection/{message.Name}"; var url = MakeUrl(http, localPath); return new ResponseConfiguration( 201, "Created", codec.ContentType, codec.Encoding, new KeyValuePair("Location", url)); }, CreateErrorEnvelope(http)); http.ReadTextRequestAsync( - (o, s) => { + (_, s) => { ProjectionManagementMessage.Command.Post postMessage; string handlerType = match.BoundVariables["type"] ?? "JS"; bool emitEnabled = IsOn(match, "emit", false); @@ -408,14 +409,13 @@ private void ProjectionsPost(HttpEntityManager http, UriTemplateMatch match, Pro trackEmittedStreams = false; } - var runAs = GetRunAs(http, match); - if (mode <= ProjectionMode.OneTime && string.IsNullOrEmpty(name)) - postMessage = new ProjectionManagementMessage.Command.Post( + var runAs = GetRunAs(http); + postMessage = mode <= ProjectionMode.OneTime && string.IsNullOrEmpty(name) + ? new( envelope, mode, Guid.NewGuid().ToString("D"), runAs, handlerType, s, enabled: enabled, checkpointsEnabled: checkpointsEnabled, emitEnabled: emitEnabled, - trackEmittedStreams: trackEmittedStreams, enableRunAs: true); - else - postMessage = new ProjectionManagementMessage.Command.Post( + trackEmittedStreams: trackEmittedStreams, enableRunAs: true) + : new( envelope, mode, name, runAs, handlerType, s, enabled: enabled, checkpointsEnabled: checkpointsEnabled, emitEnabled: emitEnabled, trackEmittedStreams: trackEmittedStreams, enableRunAs: true); @@ -423,51 +423,41 @@ private void ProjectionsPost(HttpEntityManager http, UriTemplateMatch match, Pro }, x => Log.Debug(x, "Reply Text Body Failed.")); } - private ResponseConfiguration - StateConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionState state) { + private static ResponseConfiguration StateConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionState state) { if (state.Exception != null) return Configure.InternalServerError(); - else - return state.Position != null - ? Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false, - new KeyValuePair(SystemHeaders.ProjectionPosition, state.Position.ToJsonString()), - new KeyValuePair(SystemHeaders.LegacyProjectionPosition, state.Position.ToJsonString())) - : Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); + return state.Position != null + ? Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false, + new KeyValuePair(SystemHeaders.ProjectionPosition, state.Position.ToJsonString()), + new KeyValuePair(SystemHeaders.LegacyProjectionPosition, state.Position.ToJsonString())) + : Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); } - private ResponseConfiguration ResultConfigurator(ICodec codec, - ProjectionManagementMessage.ProjectionResult state) { + private static ResponseConfiguration ResultConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionResult state) { if (state.Exception != null) return Configure.InternalServerError(); - else - return state.Position != null - ? Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false, - new KeyValuePair(SystemHeaders.ProjectionPosition, state.Position.ToJsonString()), - new KeyValuePair(SystemHeaders.LegacyProjectionPosition, state.Position.ToJsonString())) - : Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); + return state.Position != null + ? Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false, + new KeyValuePair(SystemHeaders.ProjectionPosition, state.Position.ToJsonString()), + new KeyValuePair(SystemHeaders.LegacyProjectionPosition, state.Position.ToJsonString())) + : Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); } - private ResponseConfiguration FeedPageConfigurator(ICodec codec, FeedReaderMessage.FeedPage page) { - if (page.Error == FeedReaderMessage.FeedPage.ErrorStatus.NotAuthorized) - return Configure.Unauthorized(); - return Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); + private static ResponseConfiguration FeedPageConfigurator(ICodec codec, FeedReaderMessage.FeedPage page) { + return page.Error == FeedReaderMessage.FeedPage.ErrorStatus.NotAuthorized + ? Configure.Unauthorized() + : Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); } - private string StateFormatter(ICodec codec, ProjectionManagementMessage.ProjectionState state) { - if (state.Exception != null) - return state.Exception.ToString(); - else - return state.State; + private static string StateFormatter(ICodec codec, ProjectionManagementMessage.ProjectionState state) { + return state.Exception != null ? state.Exception.ToString() : state.State; } - private string ResultFormatter(ICodec codec, ProjectionManagementMessage.ProjectionResult state) { - if (state.Exception != null) - return state.Exception.ToString(); - else - return state.Result; + private static string ResultFormatter(ICodec codec, ProjectionManagementMessage.ProjectionResult state) { + return state.Exception != null ? state.Exception.ToString() : state.Result; } - private string FeedPageFormatter(ICodec codec, FeedReaderMessage.FeedPage page) { + private static string FeedPageFormatter(ICodec codec, FeedReaderMessage.FeedPage page) { if (page.Error != FeedReaderMessage.FeedPage.ErrorStatus.Success) return null; @@ -475,64 +465,63 @@ private string FeedPageFormatter(ICodec codec, FeedReaderMessage.FeedPage page) CorrelationId = page.CorrelationId, ReaderPosition = page.LastReaderPosition.ToJsonRaw(), Events = (from e in page.Events - let resolvedEvent = e.ResolvedEvent - let isJson = resolvedEvent.IsJson - let data = isJson - ? EatException(() => (object)(resolvedEvent.Data.ParseJson()), resolvedEvent.Data) - : resolvedEvent.Data - let metadata = isJson - ? EatException(() => (object)(resolvedEvent.Metadata.ParseJson()), - resolvedEvent.Metadata) - : resolvedEvent.Metadata - let linkMetadata = isJson - ? EatException(() => (object)(resolvedEvent.PositionMetadata.ParseJson()), - resolvedEvent.PositionMetadata) - : resolvedEvent.PositionMetadata - select new { - EventStreamId = resolvedEvent.EventStreamId, - EventNumber = resolvedEvent.EventSequenceNumber, - EventType = resolvedEvent.EventType, - Data = data, - Metadata = metadata, - LinkMetadata = linkMetadata, - IsJson = isJson, - ReaderPosition = e.ReaderPosition.ToJsonRaw(), - }).ToArray() + let resolvedEvent = e.ResolvedEvent + let isJson = resolvedEvent.IsJson + let data = isJson + ? EatException(object () => resolvedEvent.Data.ParseJson(), resolvedEvent.Data) + : resolvedEvent.Data + let metadata = isJson + ? EatException(object () => resolvedEvent.Metadata.ParseJson(), + resolvedEvent.Metadata) + : resolvedEvent.Metadata + let linkMetadata = isJson + ? EatException(object () => resolvedEvent.PositionMetadata.ParseJson(), + resolvedEvent.PositionMetadata) + : resolvedEvent.PositionMetadata + select new { + EventStreamId = resolvedEvent.EventStreamId, + EventNumber = resolvedEvent.EventSequenceNumber, + EventType = resolvedEvent.EventType, + Data = data, + Metadata = metadata, + LinkMetadata = linkMetadata, + IsJson = isJson, + ReaderPosition = e.ReaderPosition.ToJsonRaw(), + }).ToArray() }.ToJson(); } - private ResponseConfiguration - QueryConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { + private static ResponseConfiguration QueryConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { return Configure.Ok("application/javascript", Helper.UTF8NoBom, null, null, false); } - private string QueryFormatter(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { + private static string QueryFormatter(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { return state.Query; } - private string QueryConfigFormatter(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { + private static string QueryConfigFormatter(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { return state.ToJson(); } - private ResponseConfiguration QueryConfigConfigurator(ICodec codec, + private static ResponseConfiguration QueryConfigConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionQuery state) { return Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); } - private string ProjectionConfigFormatter(ICodec codec, ProjectionManagementMessage.ProjectionConfig config) { + private static string ProjectionConfigFormatter(ICodec codec, ProjectionManagementMessage.ProjectionConfig config) { return config.ToJson(); } - private ResponseConfiguration ProjectionConfigConfigurator(ICodec codec, + private static ResponseConfiguration ProjectionConfigConfigurator(ICodec codec, ProjectionManagementMessage.ProjectionConfig state) { return Configure.Ok(ContentType.Json, Helper.UTF8NoBom, null, null, false); } - private ResponseConfiguration OkResponseConfigurator(ICodec codec, T message) { - return new ResponseConfiguration(200, "OK", codec.ContentType, Helper.UTF8NoBom); + private static ResponseConfiguration OkResponseConfigurator(ICodec codec, T message) { + return new(200, "OK", codec.ContentType, Helper.UTF8NoBom); } - private ResponseConfiguration OkNoCacheResponseConfigurator(ICodec codec, T message) { + private static ResponseConfiguration OkNoCacheResponseConfigurator(ICodec codec, T message) { return Configure.Ok(codec.ContentType, codec.Encoding, null, null, false); } @@ -565,38 +554,35 @@ private IEnvelope CreateErrorEnvelope(HttpEntityManager http) { null))))); } - private ResponseConfiguration NotFoundConfigurator(ICodec codec, ProjectionManagementMessage.NotFound message) { - return new ResponseConfiguration(404, "Not Found", ContentType.PlainText, Helper.UTF8NoBom); + private static ResponseConfiguration NotFoundConfigurator(ICodec codec, ProjectionManagementMessage.NotFound message) { + return new(404, "Not Found", ContentType.PlainText, Helper.UTF8NoBom); } - private string NotFoundFormatter(ICodec codec, ProjectionManagementMessage.NotFound message) { + private static string NotFoundFormatter(ICodec codec, ProjectionManagementMessage.NotFound message) { return message.Reason; } - private ResponseConfiguration NotAuthorizedConfigurator( - ICodec codec, ProjectionManagementMessage.NotAuthorized message) { - return new ResponseConfiguration(401, "Not Authorized", ContentType.PlainText, Encoding.UTF8); + private static ResponseConfiguration NotAuthorizedConfigurator(ICodec codec, ProjectionManagementMessage.NotAuthorized message) { + return new(401, "Not Authorized", ContentType.PlainText, Encoding.UTF8); } - private string NotAuthorizedFormatter(ICodec codec, ProjectionManagementMessage.NotAuthorized message) { + private static string NotAuthorizedFormatter(ICodec codec, ProjectionManagementMessage.NotAuthorized message) { return message.Reason; } - private ResponseConfiguration OperationFailedConfigurator( - ICodec codec, ProjectionManagementMessage.OperationFailed message) { - return new ResponseConfiguration(500, "Failed", ContentType.PlainText, Helper.UTF8NoBom); + private static ResponseConfiguration OperationFailedConfigurator(ICodec codec, ProjectionManagementMessage.OperationFailed message) { + return new(500, "Failed", ContentType.PlainText, Helper.UTF8NoBom); } - private string OperationFailedFormatter(ICodec codec, ProjectionManagementMessage.OperationFailed message) { + private static string OperationFailedFormatter(ICodec codec, ProjectionManagementMessage.OperationFailed message) { return message.Reason; } - private ResponseConfiguration ConflictConfigurator( - ICodec codec, ProjectionManagementMessage.OperationFailed message) { - return new ResponseConfiguration(409, "Conflict", ContentType.PlainText, Helper.UTF8NoBom); + private static ResponseConfiguration ConflictConfigurator(ICodec codec, ProjectionManagementMessage.OperationFailed message) { + return new(409, "Conflict", ContentType.PlainText, Helper.UTF8NoBom); } - private string ConflictFormatter(ICodec codec, ProjectionManagementMessage.OperationFailed message) { + private static string ConflictFormatter(ICodec codec, ProjectionManagementMessage.OperationFailed message) { return message.Reason; } @@ -604,31 +590,27 @@ private static string DefaultFormatter(ICodec codec, T message) { return codec.To(message); } - private ResponseConfiguration InvalidSubsystemRestartConfigurator(ICodec codec, ProjectionSubsystemMessage.InvalidSubsystemRestart message) { - return new ResponseConfiguration(HttpStatusCode.BadRequest, "Bad Request", ContentType.PlainText, - Helper.UTF8NoBom); + private static ResponseConfiguration InvalidSubsystemRestartConfigurator(ICodec codec, + ProjectionSubsystemMessage.InvalidSubsystemRestart message) { + return new(HttpStatusCode.BadRequest, "Bad Request", ContentType.PlainText, Helper.UTF8NoBom); } - private string InvalidSubsystemRestartFormatter(ICodec codec, ProjectionSubsystemMessage.InvalidSubsystemRestart message) { + private static string InvalidSubsystemRestartFormatter(ICodec codec, ProjectionSubsystemMessage.InvalidSubsystemRestart message) { return message.Reason; } - - private static ProjectionManagementMessage.RunAs GetRunAs(HttpEntityManager http, UriTemplateMatch match) { - return new ProjectionManagementMessage.RunAs(http.User); - } + private static ProjectionManagementMessage.RunAs GetRunAs(HttpEntityManager http) => new(http.User); private static bool? IsOn(UriTemplateMatch match, string option, bool? def) { var rawValue = match.BoundVariables[option]; if (string.IsNullOrEmpty(rawValue)) return def; var value = rawValue.ToLowerInvariant(); - if ("yes" == value || "true" == value || "1" == value) - return true; - if ("no" == value || "false" == value || "0" == value) - return false; - //TODO: throw? - return def; + return value switch { + "yes" or "true" or "1" => true, + "no" or "false" or "0" => false, + _ => def + }; } private static bool IsOn(UriTemplateMatch match, string option, bool def) { @@ -636,11 +618,11 @@ private static bool IsOn(UriTemplateMatch match, string option, bool def) { if (string.IsNullOrEmpty(rawValue)) return def; var value = rawValue.ToLowerInvariant(); - return "yes" == value || "true" == value || "1" == value; + return value is "yes" or "true" or "1"; } - public static T EatException(Func func, T defaultValue = default(T)) { - Ensure.NotNull(func, "func"); + private static T EatException(Func func, T defaultValue = default) { + Ensure.NotNull(func); try { return func(); } catch (Exception) { @@ -648,7 +630,7 @@ private static bool IsOn(UriTemplateMatch match, string option, bool def) { } } - public class ProjectionConfigData { + private class ProjectionConfigData { public bool EmitEnabled { get; set; } public bool TrackEmittedStreams { get; set; } public int CheckpointAfterMs { get; set; } @@ -657,7 +639,6 @@ public class ProjectionConfigData { public int PendingEventsThreshold { get; set; } public int MaxWriteBatchLength { get; set; } public int MaxAllowedWritesInFlight { get; set; } - public int? ProjectionExecutionTimeout { get; set; } } } diff --git a/src/KurrentDB.Projections.Core/Services/Http/ProjectionsStatisticsHttpFormatted.cs b/src/KurrentDB.Projections.Core/Services/Http/ProjectionsStatisticsHttpFormatted.cs index 81da3a16d28..1c1728667ef 100644 --- a/src/KurrentDB.Projections.Core/Services/Http/ProjectionsStatisticsHttpFormatted.cs +++ b/src/KurrentDB.Projections.Core/Services/Http/ProjectionsStatisticsHttpFormatted.cs @@ -8,15 +8,10 @@ namespace KurrentDB.Projections.Core.Services.Http; public class ProjectionsStatisticsHttpFormatted { - private readonly ProjectionStatisticsHttpFormatted[] _projections; - public ProjectionsStatisticsHttpFormatted( - ProjectionManagementMessage.Statistics source, Func makeAbsouteUrl) { - _projections = - source.Projections.Select(v => new ProjectionStatisticsHttpFormatted(v, makeAbsouteUrl)).ToArray(); + ProjectionManagementMessage.Statistics source, Func makeAbsoluteUrl) { + Projections = source.Projections.Select(v => new ProjectionStatisticsHttpFormatted(v, makeAbsoluteUrl)).ToArray(); } - public ProjectionStatisticsHttpFormatted[] Projections { - get { return _projections; } - } + public ProjectionStatisticsHttpFormatted[] Projections { get; } } diff --git a/src/KurrentDB.Projections.Core/Services/IProjectionStateHandler.cs b/src/KurrentDB.Projections.Core/Services/IProjectionStateHandler.cs index 509bfcc01f7..d1e8a814764 100644 --- a/src/KurrentDB.Projections.Core/Services/IProjectionStateHandler.cs +++ b/src/KurrentDB.Projections.Core/Services/IProjectionStateHandler.cs @@ -69,12 +69,11 @@ public static bool ProcessEvent( this IProjectionStateHandler self, string partition, CheckpointTag eventPosition, string streamId, string eventType, string category, Guid eventId, long eventSequenceNumber, string metadata, string data, out string state, out EmittedEventEnvelope[] emittedEvents, bool isJson = true) { - string ignoredSharedState; return self.ProcessEvent( partition, eventPosition, category, new ResolvedEvent( streamId, eventSequenceNumber, streamId, eventSequenceNumber, false, new TFPos(0, -1), eventId, - eventType, isJson, data, metadata), out state, out ignoredSharedState, out emittedEvents); + eventType, isJson, data, metadata), out state, out _, out emittedEvents); } public static bool ProcessEvent( @@ -87,8 +86,4 @@ public static bool ProcessEvent( streamId, eventSequenceNumber, streamId, eventSequenceNumber, false, new TFPos(0, -1), eventId, eventType, isJson, data, metadata), out state, out sharedState, out emittedEvents); } - - public static string GetNativeHandlerName(this Type handlerType) { - return "native:" + handlerType.Namespace + "." + handlerType.Name; - } } diff --git a/src/KurrentDB.Projections.Core/Services/Interpreted/JintProjectionStateHandler.cs b/src/KurrentDB.Projections.Core/Services/Interpreted/JintProjectionStateHandler.cs index 0c0d51317bd..b9da0b0a25f 100644 --- a/src/KurrentDB.Projections.Core/Services/Interpreted/JintProjectionStateHandler.cs +++ b/src/KurrentDB.Projections.Core/Services/Interpreted/JintProjectionStateHandler.cs @@ -28,41 +28,39 @@ using KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; using ILogger = Serilog.ILogger; - #nullable enable + namespace KurrentDB.Projections.Core.Services.Interpreted; public class JintProjectionStateHandler : IProjectionStateHandler { private readonly ILogger _logger = Serilog.Log.ForContext(); private readonly bool _enableContentTypeValidation; - private static readonly Stopwatch _sw = Stopwatch.StartNew(); + private static readonly Stopwatch Watch = Stopwatch.StartNew(); private readonly Engine _engine; - private readonly SourceDefinitionBuilder _definitionBuilder; - private readonly List _emitted; + private readonly SourceDefinitionBuilder _definitionBuilder = new(); + private readonly List _emitted = []; private readonly InterpreterRuntime _interpreterRuntime; private readonly JsonParser _parser; private readonly JsSerializationMeasurer _jsSerializer; private CheckpointTag? _currentPosition; - private JsValue _state; - private JsValue _sharedState; + private JsValue _state = JsValue.Undefined; + private JsValue _sharedState = JsValue.Undefined; - public JintProjectionStateHandler(string source, bool enableContentTypeValidation, - TimeSpan compilationTimeout, TimeSpan executionTimeout, + public JintProjectionStateHandler(string source, + bool enableContentTypeValidation, + TimeSpan compilationTimeout, + TimeSpan executionTimeout, JsFunctionCallMeasurer jsFunctionCaller, JsSerializationMeasurer jsSerializer) { - _enableContentTypeValidation = enableContentTypeValidation; _jsSerializer = jsSerializer; - _definitionBuilder = new SourceDefinitionBuilder(); _definitionBuilder.NoWhen(); _definitionBuilder.AllEvents(); TimeConstraint timeConstraint = new(compilationTimeout, executionTimeout); - _engine = new Engine(opts => opts.Constraint(timeConstraint).DisableStringCompilation()); - _state = JsValue.Undefined; - _sharedState = JsValue.Undefined; - _interpreterRuntime = new InterpreterRuntime(_engine, _definitionBuilder, jsFunctionCaller); + _engine = new(opts => opts.Constraint(timeConstraint).DisableStringCompilation()); + _interpreterRuntime = new(_engine, _definitionBuilder, jsFunctionCaller); _engine.Global.FastAddProperty("log", new ClrFunction(_engine, "log", Log), false, false, false); timeConstraint.Compiling(); @@ -70,12 +68,10 @@ public JintProjectionStateHandler(string source, bool enableContentTypeValidatio timeConstraint.Executing(); _parser = _interpreterRuntime.SwitchToExecutionMode(); - _engine.Global.FastAddProperty("emit", new ClrFunction(_engine, "emit", Emit, 4), true, false, true); _engine.Global.FastAddProperty("linkTo", new ClrFunction(_engine, "linkTo", LinkTo, 3), true, false, true); _engine.Global.FastAddProperty("linkStreamTo", new ClrFunction(_engine, "linkStreamTo", LinkStreamTo, 3), true, false, true); _engine.Global.FastAddProperty("copyTo", new ClrFunction(_engine, "copyTo", CopyTo, 3), true, false, true); - _emitted = new List(); } public void Dispose() { @@ -100,10 +96,7 @@ public void Load(string? state) { private void LoadCurrentState(JsValue jsValue) { if (_definitionBuilder.IsBiState) { if (_state == null || _state == JsValue.Undefined) - _state = new JsArray(_engine, new[] - { - JsValue.Undefined, JsValue.Undefined - }); + _state = new JsArray(_engine, [JsValue.Undefined, JsValue.Undefined]); _state.AsArray()[0] = jsValue; } else { @@ -124,10 +117,7 @@ public void LoadShared(string? state) { private void LoadCurrentSharedState(JsValue jsValue) { if (_definitionBuilder.IsBiState) { if (_state == null || _state == JsValue.Undefined) - _state = new JsArray(_engine, new[] - { - JsValue.Undefined, JsValue.Undefined, - }); + _state = new JsArray(_engine, [JsValue.Undefined, JsValue.Undefined]); _state.AsArray()[1] = jsValue; } else { @@ -139,7 +129,6 @@ public void Initialize() { _engine.Constraints.Reset(); var state = _interpreterRuntime.InitializeState(); LoadCurrentState(state); - } public void InitializeShared() { @@ -159,7 +148,9 @@ public void InitializeShared() { return partition.IsNumber() ? partition.AsNumber().ToString() : partition.AsString(); } - public bool ProcessPartitionCreated(string partition, CheckpointTag createPosition, ResolvedEvent @event, + public bool ProcessPartitionCreated(string partition, + CheckpointTag createPosition, + ResolvedEvent @event, out EmittedEventEnvelope[]? emittedEvents) { _engine.Constraints.Reset(); _currentPosition = createPosition; @@ -183,17 +174,20 @@ public bool ProcessPartitionDeleted(string partition, CheckpointTag deletePositi public string? TransformStateToResult() { _engine.Constraints.Reset(); var result = _interpreterRuntime.TransformStateToResult(_state); - if (result == JsValue.Null || result == JsValue.Undefined) - return null; - return Serialize(result); + return result == JsValue.Null || result == JsValue.Undefined ? null : Serialize(result); } - public bool ProcessEvent(string partition, CheckpointTag eventPosition, string category, ResolvedEvent @event, - out string? newState, out string? newSharedState, out EmittedEventEnvelope[]? emittedEvents) { + public bool ProcessEvent(string partition, + CheckpointTag eventPosition, + string category, + ResolvedEvent @event, + out string? newState, + out string? newSharedState, + out EmittedEventEnvelope[]? emittedEvents) { _currentPosition = eventPosition; _engine.Constraints.Reset(); if ((@event.IsJson && string.IsNullOrWhiteSpace(@event.Data)) || - (!_enableContentTypeValidation && !@event.IsJson && string.IsNullOrEmpty(@event.Data))) { + (!_enableContentTypeValidation && !@event.IsJson && string.IsNullOrEmpty(@event.Data))) { PrepareOutput(out newState, out newSharedState, out emittedEvents); return true; } @@ -210,21 +204,12 @@ private void PrepareOutput(out string? newState, out string? newSharedState, out if (_definitionBuilder.IsBiState && _state.IsArray()) { var arr = _state.AsArray(); if (arr.TryGetValue(0, out var state)) { - if (_state.IsString()) { - newState = _state.AsString(); - } else { - newState = ConvertToStringHandlingNulls(state); - } + newState = _state.IsString() ? _state.AsString() : ConvertToStringHandlingNulls(state); } else { newState = ""; } - if (arr.TryGetValue(1, out var sharedState)) { - newSharedState = ConvertToStringHandlingNulls(sharedState); - } else { - newSharedState = null; - } - + newSharedState = arr.TryGetValue(1, out var sharedState) ? ConvertToStringHandlingNulls(sharedState) : null; } else if (_state.IsString()) { newState = _state.AsString(); newSharedState = null; @@ -234,13 +219,18 @@ private void PrepareOutput(out string? newState, out string? newSharedState, out } } - private string? ConvertToStringHandlingNulls(JsValue value) { - if (value.IsNull() || value.IsUndefined()) - return null; - return Serialize(value); + private string? ConvertToStringHandlingNulls(JsValue value) => value.IsNull() || value.IsUndefined() ? null : Serialize(value); + + private ExtraMetaData ToExtraMetaData(ObjectInstance md) { + var meta = EnsureNonNullObjectValue(md, "metaData"); + var d = meta.GetOwnProperties() + .Where(kvp => kvp.Value.Value.Type is not (Types.Empty or Types.Undefined)) + .ToDictionary(kvp => kvp.Key.AsString(), kvp => Serialize(kvp.Value.Value)); + + return new(d); } - JsValue Emit(JsValue thisValue, JsValue[] parameters) { + private JsValue Emit(JsValue thisValue, JsValue[] parameters) { if (parameters.Length < 3) throw new ArgumentException("invalid number of parameters"); @@ -256,17 +246,10 @@ JsValue Emit(JsValue thisValue, JsValue[] parameters) { var data = Serialize(eventBody); ExtraMetaData? metadata = null; if (parameters.Length == 4) { - var md = parameters.At(3).AsObject(); - var d = new Dictionary(); - foreach (var kvp in md.GetOwnProperties()) { - if (kvp.Value.Value.Type is Types.Empty or Types.Undefined) - continue; - d.Add(kvp.Key.AsString(), Serialize(kvp.Value.Value)); - } - - metadata = new ExtraMetaData(d); + metadata = ToExtraMetaData(parameters.At(3).AsObject()); } - _emitted.Add(new EmittedEventEnvelope(new EmittedDataEvent(stream, Guid.NewGuid(), eventType, true, data, metadata, _currentPosition, null))); + + _emitted.Add(new(new EmittedDataEvent(stream, Guid.NewGuid(), eventType, true, data, metadata, _currentPosition, null))); return JsValue.Undefined; } @@ -280,9 +263,9 @@ private static ObjectInstance EnsureNonNullObjectValue(JsValue parameter, string private static string EnsureNonNullStringValue(JsValue parameter, string parameterName) { if (parameter != JsValue.Null && - parameter.IsString() && - (parameter.AsString() is { } value && - !string.IsNullOrWhiteSpace(value))) + parameter.IsString() && + (parameter.AsString() is { } value && + !string.IsNullOrWhiteSpace(value))) return value; if (parameter == JsValue.Null || parameter == JsValue.Undefined || parameter.IsString()) @@ -291,106 +274,95 @@ private static string EnsureNonNullStringValue(JsValue parameter, string paramet throw new ArgumentException("string expected", parameterName); } - string? AsString(JsValue? value, bool formatForRaw) { + private string? AsString(JsValue? value, bool formatForRaw) { return value switch { JsBoolean b => b.AsBoolean() ? "true" : "false", JsString s => formatForRaw ? $"\"{s.AsString()}\"" : s.AsString(), JsNumber n => n.AsNumber().ToString(CultureInfo.InvariantCulture), JsNull => null, - JsUndefined => null, { } v => Serialize(value), + JsUndefined => null, + not null => Serialize(value), _ => null }; } - JsValue LinkTo(JsValue thisValue, JsValue[] parameters) { + private JsValue LinkTo(JsValue thisValue, JsValue[] parameters) { if (parameters.Length != 2 && parameters.Length != 3) throw new ArgumentException("wrong number of parameters"); var stream = EnsureNonNullStringValue(parameters.At(0), "streamId"); var @event = EnsureNonNullObjectValue(parameters.At(1), "event"); - if (!@event.TryGetValue("sequenceNumber", out var numberValue) | !@event.TryGetValue("streamId", out var sourceValue) || !numberValue.IsNumber() - || !sourceValue.IsString()) { + if (!@event.TryGetValue("sequenceNumber", out var numberValue) | !@event.TryGetValue("streamId", out var sourceValue) || + !numberValue.IsNumber() || !sourceValue.IsString()) { throw new Exception($"Invalid link to event {numberValue}@{sourceValue}"); } var number = (long)numberValue.AsNumber(); var source = sourceValue.AsString(); - ExtraMetaData? metadata = null; - if (parameters.Length == 3) { - var md = EnsureNonNullObjectValue(parameters.At(2), "metaData"); - var d = new Dictionary(); - foreach (var kvp in md.GetOwnProperties()) { - d.Add(kvp.Key.AsString(), AsString(kvp.Value.Value, true)); - } - metadata = new ExtraMetaData(d); - } + ExtraMetaData? metadata = parameters.Length switch { + 3 => ToExtraMetaData(parameters.At(2).AsObject()), + _ => null + }; - _emitted.Add(new EmittedEventEnvelope( - new EmittedDataEvent(stream, Guid.NewGuid(), SystemEventTypes.LinkTo, false, $"{number}@{source}", metadata, _currentPosition, null))); + _emitted.Add(new(new EmittedDataEvent(stream, Guid.NewGuid(), SystemEventTypes.LinkTo, false, $"{number}@{source}", metadata, + _currentPosition, null))); return JsValue.Undefined; } - JsValue LinkStreamTo(JsValue thisValue, JsValue[] parameters) { - + private JsValue LinkStreamTo(JsValue thisValue, JsValue[] parameters) { var stream = EnsureNonNullStringValue(parameters.At(0), "streamId"); var linkedStreamId = EnsureNonNullStringValue(parameters.At(1), "linkedStreamId"); - if (parameters.Length == 3) { - } + ExtraMetaData? metadata = parameters.Length switch { + 3 => ToExtraMetaData(parameters.At(2).AsObject()), + _ => null + }; - ExtraMetaData? metadata = null; - if (parameters.Length == 3) { - var md = parameters.At(4).AsObject(); - var d = new Dictionary(); - foreach (var kvp in md.GetOwnProperties()) { - d.Add(kvp.Key.AsString(), AsString(kvp.Value.Value, true)); - } - metadata = new ExtraMetaData(d); - } - _emitted.Add(new EmittedEventEnvelope( - new EmittedDataEvent(stream, Guid.NewGuid(), SystemEventTypes.StreamReference, false, linkedStreamId, metadata, _currentPosition, null))); + _emitted.Add(new( + new EmittedDataEvent(stream, Guid.NewGuid(), SystemEventTypes.StreamReference, false, linkedStreamId, metadata, + _currentPosition, null))); return JsValue.Undefined; } - JsValue CopyTo(JsValue thisValue, JsValue[] parameters) { - return JsValue.Undefined; - } + private JsValue CopyTo(JsValue thisValue, JsValue[] parameters) => JsValue.Undefined; - void Log(string message) { - _logger.Debug(message, Array.Empty()); + private void Log(string message) { + _logger.Debug(message); } private JsValue Log(JsValue thisValue, JsValue[] parameters) { - if (parameters.Length == 0) - return JsValue.Undefined; - if (parameters.Length == 1) { - var p0 = parameters.At(0); - if (p0 != null && p0.IsPrimitive()) - Log(p0.ToString()); - if (p0 is ObjectInstance oi) - Log(Serialize(oi)); - return JsValue.Undefined; - } - - - if (parameters.Length > 1) { - var sb = new StringBuilder(); - for (int i = 0; i < parameters.Length; i++) { - if (i > 1) - sb.Append(" ,"); - var p = parameters.At(i); - if (p != null && p.IsPrimitive()) - Log(p.ToString()); - if (p is ObjectInstance oi) - sb.Append(Serialize(oi)); + switch (parameters.Length) { + case 0: + break; + case 1: { + var p0 = parameters.At(0); + if (p0 != null && p0.IsPrimitive()) + Log(p0.ToString()); + if (p0 is ObjectInstance oi) + Log(Serialize(oi)); + break; } + case > 1: { + var sb = new StringBuilder(); + for (int i = 0; i < parameters.Length; i++) { + if (i > 1) + sb.Append(" ,"); + var p = parameters.At(i); + if (p != null && p.IsPrimitive()) + Log(p.ToString()); + if (p is ObjectInstance oi) + sb.Append(Serialize(oi)); + } - Log(sb.ToString()); + Log(sb.ToString()); + break; + } } + return JsValue.Undefined; } - class TimeConstraint : Constraint { + private class TimeConstraint : Constraint { private readonly TimeSpan _compilationTimeout; private readonly TimeSpan _executionTimeout; private TimeSpan _start; @@ -411,27 +383,27 @@ public void Compiling() { public void Executing() { _timeout = _executionTimeout; _executing = true; - } + public override void Reset() { - _start = _sw.Elapsed; + _start = Watch.Elapsed; } public override void Check() { - if (_sw.Elapsed - _start >= _timeout) { + if (Watch.Elapsed - _start >= _timeout) { if (Debugger.IsAttached) return; var action = _executing ? "execute" : "compile"; - throw new TimeoutException($"Projection script took too long to {action} (took: {_sw.Elapsed - _start:c}, allowed: {_timeout:c}"); + throw new TimeoutException( + $"Projection script took too long to {action} (took: {Watch.Elapsed - _start:c}, allowed: {_timeout:c}"); } } } - class InterpreterRuntime : ObjectInstance { - - private readonly Dictionary _handlers; - private readonly List<(TransformType, ScriptFunction)> _transforms; - private readonly List _createdHandlers; + private class InterpreterRuntime : ObjectInstance { + private readonly Dictionary _handlers = new(StringComparer.Ordinal); + private readonly List<(TransformType, ScriptFunction)> _transforms = []; + private readonly List _createdHandlers = []; private ScriptFunction? _init; private ScriptFunction? _initShared; private ScriptFunction? _any; @@ -451,55 +423,56 @@ class InterpreterRuntime : ObjectInstance { private readonly JsFunctionCallMeasurer _jsFunctionCaller; private readonly JsonParser _parser; - private static readonly Dictionary> _possibleProperties = new Dictionary>() { - ["when"] = i => i.FastAddProperty("when", i._whenInstance, true, false, true), - ["partitionBy"] = i => i.FastAddProperty("partitionBy", i._partitionByInstance, true, false, true), - ["outputState"] = i => i.FastAddProperty("outputState", i._outputStateInstance, true, false, true), - ["foreachStream"] = i => i.FastAddProperty("foreachStream", i._foreachStreamInstance, true, false, true), - ["transformBy"] = i => i.FastAddProperty("transformBy", i._transformByInstance, true, false, true), - ["filterBy"] = i => i.FastAddProperty("filterBy", i._filterByInstance, true, false, true), - ["outputTo"] = i => i.FastAddProperty("outputTo", i._outputToInstance, true, false, true), - ["$defines_state_transform"] = i => i.FastAddProperty("$defines_state_transform", i._definesStateTransformInstance, true, false, true), - }; + private static readonly Dictionary> _possibleProperties = + new() { + ["when"] = i => i.FastAddProperty("when", i._whenInstance, true, false, true), + ["partitionBy"] = i => i.FastAddProperty("partitionBy", i._partitionByInstance, true, false, true), + ["outputState"] = i => i.FastAddProperty("outputState", i._outputStateInstance, true, false, true), + ["foreachStream"] = i => i.FastAddProperty("foreachStream", i._foreachStreamInstance, true, false, true), + ["transformBy"] = i => i.FastAddProperty("transformBy", i._transformByInstance, true, false, true), + ["filterBy"] = i => i.FastAddProperty("filterBy", i._filterByInstance, true, false, true), + ["outputTo"] = i => i.FastAddProperty("outputTo", i._outputToInstance, true, false, true), + ["$defines_state_transform"] = i + => i.FastAddProperty("$defines_state_transform", i._definesStateTransformInstance, true, false, true), + }; - private static readonly Dictionary _availableProperties = new Dictionary() { - ["fromStream"] = new[] { "when", "partitionBy", "outputState" }, - ["fromAll"] = new[] { "when", "partitionBy", "outputState", "foreachStream" }, - ["fromStreams"] = new[] { "when", "partitionBy", "outputState" }, - ["fromCategory"] = new[] { "when", "partitionBy", "outputState", "foreachStream" }, - ["when"] = new[] { "transformBy", "filterBy", "outputState", "outputTo", "$defines_state_transform" }, - ["foreachStream"] = new[] { "when" }, - ["outputState"] = new[] { "transformBy", "filterBy", "outputTo" }, - ["partitionBy"] = new[] { "when" }, - ["transformBy"] = new[] { "transformBy", "filterBy", "outputState", "outputTo" }, - ["filterBy"] = new[] { "transformBy", "filterBy", "outputState", "outputTo" }, - ["outputTo"] = Array.Empty(), - ["execution"] = Array.Empty() + private static readonly Dictionary _availableProperties = new() { + ["fromStream"] = ["when", "partitionBy", "outputState"], + ["fromAll"] = ["when", "partitionBy", "outputState", "foreachStream"], + ["fromStreams"] = ["when", "partitionBy", "outputState"], + ["fromCategory"] = ["when", "partitionBy", "outputState", "foreachStream"], + ["when"] = ["transformBy", "filterBy", "outputState", "outputTo", "$defines_state_transform"], + ["foreachStream"] = ["when"], + ["outputState"] = ["transformBy", "filterBy", "outputTo"], + ["partitionBy"] = ["when"], + ["transformBy"] = ["transformBy", "filterBy", "outputState", "outputTo"], + ["filterBy"] = ["transformBy", "filterBy", "outputState", "outputTo"], + ["outputTo"] = [], + ["execution"] = [] }; private static readonly Dictionary> _setters = - new Dictionary>(StringComparer.OrdinalIgnoreCase) { - {"$includeLinks", (options, value) => options.SetIncludeLinks(value.IsBoolean()? value.AsBoolean() : throw new Exception("Invalid value"))}, - {"reorderEvents", (options, value) => options.SetReorderEvents(value.IsBoolean()? value.AsBoolean(): throw new Exception("Invalid value"))}, - {"processingLag", (options, value) => options.SetProcessingLag(value.IsNumber() ? (int)value.AsNumber() : throw new Exception("Invalid value"))}, - {"resultStreamName", (options, value) => options.SetResultStreamNameOption(value.IsString() ? value.AsString() : throw new Exception("Invalid value"))}, - {"biState", (options, value) => options.SetIsBiState(value.IsBoolean()? value.AsBoolean() : throw new Exception("Invalid value"))}, + new(StringComparer.OrdinalIgnoreCase) { + ["$includeLinks"] = (options, value) + => options.SetIncludeLinks(value.IsBoolean() ? value.AsBoolean() : throw new("Invalid value")), + ["reorderEvents"] = (options, value) + => options.SetReorderEvents(value.IsBoolean() ? value.AsBoolean() : throw new("Invalid value")), + ["processingLag"] = (options, value) + => options.SetProcessingLag(value.IsNumber() ? (int)value.AsNumber() : throw new("Invalid value")), + ["resultStreamName"] = (options, value) + => options.SetResultStreamNameOption(value.IsString() ? value.AsString() : throw new("Invalid value")), + ["biState"] = (options, value) => options.SetIsBiState(value.IsBoolean() ? value.AsBoolean() : throw new("Invalid value")) }; - private readonly List _definitionFunctions; + private readonly List _definitionFunctions = []; public InterpreterRuntime( Engine engine, SourceDefinitionBuilder builder, JsFunctionCallMeasurer jsFunctionCaller) : base(engine) { - _definitionBuilder = builder; _jsFunctionCaller = jsFunctionCaller; - _handlers = new Dictionary(StringComparer.Ordinal); - _createdHandlers = new List(); - _transforms = new List<(TransformType, ScriptFunction)>(); _parser = new JsonParser(engine); - _definitionFunctions = new List(); AddDefinitionFunction("options", SetOptions, 1); AddDefinitionFunction("fromStream", FromStream, 1); AddDefinitionFunction("fromCategory", FromCategory, 4); @@ -516,7 +489,6 @@ public InterpreterRuntime( _filterByInstance = new ClrFunction(engine, "filterBy", FilterBy, 1); _outputToInstance = new ClrFunction(engine, "outputTo", OutputTo, 1); _definesStateTransformInstance = new ClrFunction(engine, "$defines_state_transform", DefinesStateTransform); - } private void AddDefinitionFunction(string name, Func func, int length) { @@ -535,27 +507,38 @@ private JsValue FromStream(JsValue _, JsValue[] parameters) { } private JsValue FromCategory(JsValue thisValue, JsValue[] parameters) { - if (parameters.Length == 0) - return this; - if (parameters.Length == 1 && parameters.At(0).IsArray()) { - foreach (var cat in parameters.At(0).AsArray()) { - if (cat is not JsString s) { - throw new ArgumentException("categories"); + switch (parameters.Length) { + case 0: + return this; + case 1 when parameters.At(0).IsArray(): { + foreach (var cat in parameters.At(0).AsArray()) { + if (cat is not JsString s) { + throw new ArgumentException("categories"); + } + + _definitionBuilder.FromStream($"$ce-{s.AsString()}"); } - _definitionBuilder.FromStream($"$ce-{s.AsString()}"); + + break; } - } else if (parameters.Length > 1) { - foreach (var cat in parameters) { - if (cat is not JsString s) { - throw new ArgumentException("categories"); + case > 1: { + foreach (var cat in parameters) { + if (cat is not JsString s) { + throw new ArgumentException("categories"); + } + + _definitionBuilder.FromStream($"$ce-{s.AsString()}"); } - _definitionBuilder.FromStream($"$ce-{s.AsString()}"); + + break; + } + default: { + var p0 = parameters.At(0); + if (p0 is not JsString s) + throw new ArgumentException("category"); + _definitionBuilder.FromCategory(s.AsString()); + break; } - } else { - var p0 = parameters.At(0); - if (p0 is not JsString s) - throw new ArgumentException("category"); - _definitionBuilder.FromCategory(s.AsString()); } RestrictProperties("fromCategory"); @@ -572,6 +555,7 @@ private JsValue When(JsValue thisValue, JsValue[] parameters) { } } } + _definitionBuilder.SetDefinesFold(); RestrictProperties("when"); return this; @@ -580,8 +564,6 @@ private JsValue When(JsValue thisValue, JsValue[] parameters) { private JsValue PartitionBy(JsValue thisValue, JsValue[] parameters) { if (parameters.At(0) is ScriptFunction partitionFunction) { _definitionBuilder.SetByCustomPartitions(); - - _partitionFunction = partitionFunction; RestrictProperties("partitionBy"); return this; @@ -625,27 +607,23 @@ private JsValue DefinesStateTransform(JsValue thisValue, JsValue[] parameters) { } private JsValue FilterBy(JsValue thisValue, JsValue[] parameters) { - if (parameters.At(0) is ScriptFunction fi) { - _definitionBuilder.SetDefinesStateTransform(); - _definitionBuilder.SetOutputState(); - _transforms.Add((TransformType.Filter, fi)); - RestrictProperties("filterBy"); - return this; - } + if (parameters.At(0) is not ScriptFunction fi) throw new ArgumentException("expected function"); - throw new ArgumentException("expected function"); + _definitionBuilder.SetDefinesStateTransform(); + _definitionBuilder.SetOutputState(); + _transforms.Add((TransformType.Filter, fi)); + RestrictProperties("filterBy"); + return this; } private JsValue TransformBy(JsValue thisValue, JsValue[] parameters) { - if (parameters.At(0) is ScriptFunction fi) { - _definitionBuilder.SetDefinesStateTransform(); - _definitionBuilder.SetOutputState(); - _transforms.Add((TransformType.Transform, fi)); - RestrictProperties("transformBy"); - return this; - } + if (parameters.At(0) is not ScriptFunction fi) throw new ArgumentException("expected function"); - throw new ArgumentException("expected function"); + _definitionBuilder.SetDefinesStateTransform(); + _definitionBuilder.SetOutputState(); + _transforms.Add((TransformType.Transform, fi)); + RestrictProperties("transformBy"); + return this; } private JsValue OnEvent(JsValue thisValue, JsValue[] parameters) { @@ -733,6 +711,7 @@ public JsValue Handle(JsValue state, EventEnvelope eventEnvelope) { } else { newState = eventEnvelope.BodyRaw; } + return newState == Undefined ? state : newState; } @@ -747,6 +726,7 @@ public JsValue TransformStateToResult(JsValue state) { if (!(result.IsBoolean() && result.AsBoolean()) || result == Null || result == Undefined) { return Null; } + break; } case TransformType.None: @@ -760,16 +740,18 @@ public JsValue TransformStateToResult(JsValue state) { return state; } - JsValue FromAll(JsValue _, JsValue[] __) { + private JsValue FromAll(JsValue _, JsValue[] __) { _definitionBuilder.FromAll(); RestrictProperties("fromAll"); return this; } - JsValue FromStreams(JsValue _, JsValue[] parameters) { + private JsValue FromStreams(JsValue _, JsValue[] parameters) { IEnumerator? streams = null; try { - streams = parameters.At(0).IsArray() ? parameters.At(0).AsArray().GetEnumerator() : parameters.AsEnumerable().GetEnumerator(); + streams = parameters.At(0).IsArray() + ? parameters.At(0).AsArray().GetEnumerator() + : parameters.AsEnumerable().GetEnumerator(); while (streams.MoveNext()) { if (!streams.Current.IsString()) throw new ArgumentException("streams"); @@ -783,8 +765,7 @@ JsValue FromStreams(JsValue _, JsValue[] parameters) { return this; } - - JsValue SetOptions(JsValue thisValue, JsValue[] parameters) { + private JsValue SetOptions(JsValue thisValue, JsValue[] parameters) { var p0 = parameters.At(0); if (p0 is ObjectInstance opts) { foreach (var kvp in opts.GetOwnProperties()) { @@ -799,11 +780,8 @@ JsValue SetOptions(JsValue thisValue, JsValue[] parameters) { return Undefined; } - public JsValue GetPartition(EventEnvelope envelope) { - if (_partitionFunction != null) - return _jsFunctionCaller.Call("partitionBy", _partitionFunction, envelope); - return Null; - } + public JsValue GetPartition(EventEnvelope envelope) + => _partitionFunction != null ? _jsFunctionCaller.Call("partitionBy", _partitionFunction, envelope) : Null; public void HandleCreated(JsValue state, EventEnvelope envelope) { for (int i = 0; i < _createdHandlers.Count; i++) { @@ -811,7 +789,7 @@ public void HandleCreated(JsValue state, EventEnvelope envelope) { } } - enum TransformType { + private enum TransformType { None, Filter, Transform @@ -822,10 +800,10 @@ public JsonParser SwitchToExecutionMode() { foreach (var globalProp in _definitionFunctions) { _engine.Global.RemoveOwnProperty(globalProp); } + return _parser; } - public void HandleDeleted(JsValue state, string partition, bool isSoftDelete) { if (_deleted != null) { _jsFunctionCaller.Call("$deleted", _deleted, state, Null, partition, isSoftDelete); @@ -833,50 +811,45 @@ public void HandleDeleted(JsValue state, string partition, bool isSoftDelete) { } } - EventEnvelope CreateEnvelope(string partition, ResolvedEvent @event, string category) { - var envelope = new EventEnvelope(_engine, _parser, this); - envelope.Partition = partition; - envelope.BodyRaw = @event.Data; - envelope.MetadataRaw = @event.Metadata; - envelope.StreamId = @event.EventStreamId; - envelope.EventId = @event.EventId.ToString("D"); - envelope.EventType = @event.EventType; - envelope.LinkMetadataRaw = @event.PositionMetadata; - envelope.IsJson = @event.IsJson; - envelope.Category = category; - envelope.SequenceNumber = @event.EventSequenceNumber; - return envelope; - } - sealed class EventEnvelope : ObjectInstance { - private readonly JsonParser _parser; - private readonly JintProjectionStateHandler _parent; + private EventEnvelope CreateEnvelope(string partition, ResolvedEvent @event, string category) + => new(_engine, _parser, this) { + Partition = partition, + BodyRaw = @event.Data, + MetadataRaw = @event.Metadata, + StreamId = @event.EventStreamId, + EventId = @event.EventId.ToString("D"), + EventType = @event.EventType, + LinkMetadataRaw = @event.PositionMetadata, + IsJson = @event.IsJson, + Category = category, + SequenceNumber = @event.EventSequenceNumber + }; + private sealed class EventEnvelope(Engine engine, JsonParser parser, JintProjectionStateHandler parent) + : ObjectInstance(engine) { public string StreamId { - set => SetOwnProperty("streamId", new PropertyDescriptor(value, false, true, false)); + set => SetOwnProperty("streamId", new(value, false, true, false)); } + public long SequenceNumber { - set => SetOwnProperty("sequenceNumber", new PropertyDescriptor(value, false, true, false)); + set => SetOwnProperty("sequenceNumber", new(value, false, true, false)); } public string EventType { - get => _parent.AsString(Get("eventType"), false) ?? ""; - set => SetOwnProperty("eventType", new PropertyDescriptor(value, false, true, false)); + get => parent.AsString(Get("eventType"), false) ?? ""; + set => SetOwnProperty("eventType", new(value, false, true, false)); } - public JsValue Body { - get { - if (TryGetValue("body", out var value) && value is ObjectInstance oi) - return oi; - if (EnsureBody(out JsValue objectInstance)) - return objectInstance; - - return Undefined; - } - } + private JsValue Body + => TryGetValue("body", out var value) && value is ObjectInstance oi + ? oi + : EnsureBody(out JsValue objectInstance) + ? objectInstance + : Undefined; private bool EnsureBody(out JsValue objectInstance) { if (IsJson && TryGetValue("bodyRaw", out var raw) && raw is not JsUndefined) { - var body = raw.IsNull() ? raw : _parser.Parse(raw.AsString()); + var body = raw.IsNull() ? raw : parser.Parse(raw.AsString()); var pd = new PropertyDescriptor(body, false, true, false); SetOwnProperty("body", pd); SetOwnProperty("data", pd); @@ -890,33 +863,27 @@ private bool EnsureBody(out JsValue objectInstance) { public bool IsJson { get => Get("isJson").AsBoolean(); - set => SetOwnProperty("isJson", new PropertyDescriptor(value, false, true, false)); + set => SetOwnProperty("isJson", new(value, false, true, false)); } public string? BodyRaw { - get => _parent.AsString(Get("bodyRaw"), false); - set => SetOwnProperty("bodyRaw", new PropertyDescriptor(value, false, true, false)); + get => parent.AsString(Get("bodyRaw"), false); + set => SetOwnProperty("bodyRaw", new(value, false, true, false)); } - private JsValue Metadata { - get { - if (TryGetValue("metadata", out var value) && value is ObjectInstance oi) - return oi; - if (EnsureMetadata(out value)) - return value; - - return Undefined; - } - } + private JsValue Metadata + => TryGetValue("metadata", out var value) && value is ObjectInstance oi + ? oi + : EnsureMetadata(out value) + ? value + : Undefined; private bool EnsureMetadata(out JsValue value) { if (TryGetValue("metadataRaw", out var raw) && raw is not JsUndefined) { - var metadata = raw.IsNull() ? raw : _parser.Parse(raw.AsString()); - SetOwnProperty("metadata", new PropertyDescriptor(metadata, false, true, false)); - { - value = metadata; - return true; - } + var metadata = raw.IsNull() ? raw : parser.Parse(raw.AsString()); + SetOwnProperty("metadata", new(metadata, false, true, false)); + value = metadata; + return true; } value = Undefined; @@ -924,28 +891,22 @@ private bool EnsureMetadata(out JsValue value) { } public string MetadataRaw { - set => FastSetProperty("metadataRaw", new PropertyDescriptor(value, false, true, false)); + set => FastSetProperty("metadataRaw", new(value, false, true, false)); } - private JsValue LinkMetadata { - get { - if (TryGetValue("linkMetadata", out var value) && value is ObjectInstance oi) - return oi; - if (EnsureLinkMetadata(out value)) - return value; - - return Undefined; - } - } + private JsValue LinkMetadata + => TryGetValue("linkMetadata", out var value) && value is ObjectInstance oi + ? oi + : EnsureLinkMetadata(out value) + ? value + : Undefined; private bool EnsureLinkMetadata(out JsValue value) { if (TryGetValue("linkMetadataRaw", out var raw) && raw is not JsUndefined) { - var metadata = raw.IsNull() ? raw : _parser.Parse(raw.AsString()); - SetOwnProperty("linkMetadata", new PropertyDescriptor(metadata, false, true, false)); - { - value = metadata; - return true; - } + var metadata = raw.IsNull() ? raw : parser.Parse(raw.AsString()); + SetOwnProperty("linkMetadata", new(metadata, false, true, false)); + value = metadata; + return true; } value = Undefined; @@ -953,39 +914,28 @@ private bool EnsureLinkMetadata(out JsValue value) { } public string LinkMetadataRaw { - set => SetOwnProperty("linkMetadataRaw", new PropertyDescriptor(value, false, true, false)); + set => SetOwnProperty("linkMetadataRaw", new(value, false, true, false)); } public string Partition { - set => SetOwnProperty("partition", new PropertyDescriptor(value, false, true, false)); + set => SetOwnProperty("partition", new(value, false, true, false)); } public string Category { - set => SetOwnProperty("category", new PropertyDescriptor(value, false, true, false)); + set => SetOwnProperty("category", new(value, false, true, false)); } public string EventId { - set => SetOwnProperty("eventId", new PropertyDescriptor(value, false, true, false)); - } - - public EventEnvelope(Engine engine, JsonParser parser, JintProjectionStateHandler parent) : base(engine) { - _parser = parser; - _parent = parent; + set => SetOwnProperty("eventId", new(value, false, true, false)); } public override JsValue Get(JsValue property, JsValue receiver) { - if (property == "body" || property == "data") { - return Body; - } - - if (property == "metadata") { - return Metadata; - } - - if (property == "linkMetadata") { - return LinkMetadata; - } - return base.Get(property, receiver); + return property.AsString() switch { + "body" or "data" => Body, + "metadata" => Metadata, + "linkMetadata" => LinkMetadata, + _ => base.Get(property, receiver) + }; } public override List GetOwnPropertyKeys(Types types = Types.String | Types.Symbol) { @@ -1006,9 +956,7 @@ public override IEnumerable> GetOwnPro EnsureLinkMetadata(out _); } - var list = base.GetOwnProperties(); - - return list; + return base.GetOwnProperties(); } } @@ -1018,15 +966,13 @@ public string Serialize(JsValue value) { } public class Serializer { - private readonly WriteState[] _iterators; - private readonly ArrayBufferWriter _bufferWriter; + private readonly WriteState[] _iterators = new WriteState[64]; + private readonly ArrayBufferWriter _bufferWriter = new(1024 * 1024); private readonly Utf8JsonWriter _writer; - private readonly Dictionary _knownPropertyNames; + private readonly Dictionary _knownPropertyNames = new(); private int _depth; public Serializer() { - _iterators = new WriteState[64]; - _bufferWriter = new ArrayBufferWriter(1024 * 1024); _writer = new Utf8JsonWriter( _bufferWriter, new JsonWriterOptions { @@ -1034,7 +980,6 @@ public Serializer() { SkipValidation = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }); - _knownPropertyNames = new Dictionary(); } public ReadOnlyMemory Serialize(JsValue value) { @@ -1042,24 +987,23 @@ public ReadOnlyMemory Serialize(JsValue value) { _bufferWriter.Clear(); _writer.Reset(); - if (value is JsArray array) { - _iterators[_depth] = new WriteState(array); - } else if (value is ObjectInstance oi) { - _iterators[_depth] = new WriteState(oi); - } else { - _iterators[_depth] = new WriteState(value); - } + _iterators[_depth] = value switch { + JsArray array => new(array), + ObjectInstance oi => new(oi), + _ => new(value) + }; + ref var current = ref _iterators[0]; while (current.Write(_writer, ref _depth, _iterators, _knownPropertyNames)) { current = ref _iterators[_depth]; } + _writer.Flush(); return _bufferWriter.WrittenMemory; - } - struct WriteState { + private struct WriteState { private enum Type { Complete, Array, @@ -1067,10 +1011,9 @@ private enum Type { Primitive, } - private static readonly IEnumerator> _emptyIterator = - new NoopIterator(); + private static readonly IEnumerator> _emptyIterator = new NoopIterator(); - class NoopIterator : IEnumerator> { + private class NoopIterator : IEnumerator> { public KeyValuePair Current => default; object? IEnumerator.Current => default; @@ -1078,9 +1021,7 @@ class NoopIterator : IEnumerator> { public void Dispose() { } - public bool MoveNext() { - return false; - } + public bool MoveNext() => false; public void Reset() { } @@ -1128,27 +1069,28 @@ public bool Write( ref int depth, WriteState[] writeStates, Dictionary knownPropertyNames) { - switch (_type) { case Type.Array: if (_position == -1) { writer.WriteStartArray(); _position++; } + var instance = (JsArray)_instance; for (; _position < _length; _position++) { var value = instance[(uint)_position]; if (value.Type == Types.Object) { - if (value is JsArray ai) { - writeStates[++depth] = new WriteState(ai); - } else { - writeStates[++depth] = new WriteState(value.AsObject()); - } + writeStates[++depth] = value is JsArray ai + ? new(ai) + : new(value.AsObject()); + _position++; return true; } + SerializePrimitive(value, writer); } + writer.WriteEndArray(); break; case Type.Object: @@ -1156,6 +1098,7 @@ public bool Write( writer.WriteStartObject(); _started = true; } + while (_iterator.MoveNext()) { var (name, propertyDescriptor) = _iterator.Current; var value = propertyDescriptor.Value; @@ -1164,17 +1107,15 @@ public bool Write( WriteMaybeCachedPropertyName(name.AsString(), knownPropertyNames, writer); if (value.Type == Types.Object) { - if (value is JsArray ai) { - writeStates[++depth] = new WriteState(ai); - } else { - writeStates[++depth] = new WriteState(value.AsObject()); - } + writeStates[++depth] = value is JsArray ai + ? new(ai) + : new(value.AsObject()); + _position++; return true; - } else { - SerializePrimitive(value, writer); } + SerializePrimitive(value, writer); } writer.WriteEndObject(); @@ -1183,6 +1124,7 @@ public bool Write( SerializePrimitive(_instance, writer); break; } + writeStates[depth] = default; depth--; return depth >= 0; @@ -1190,19 +1132,21 @@ public bool Write( } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - private static void WriteMaybeCachedPropertyName(string name, Dictionary knownPropertyNames, Utf8JsonWriter writer) { + private static void WriteMaybeCachedPropertyName(string name, + Dictionary knownPropertyNames, + Utf8JsonWriter writer) { if (!knownPropertyNames.TryGetValue(name, out var propertyName)) { propertyName = JsonEncodedText.Encode(name); if (knownPropertyNames.Count < 1000) { knownPropertyNames.Add(name, propertyName); } } + writer.WritePropertyName(propertyName); } [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] - static void SerializePrimitive(JsValue value, Utf8JsonWriter writer) { - + private static void SerializePrimitive(JsValue value, Utf8JsonWriter writer) { switch (value.Type) { case Types.Null: case Types.Undefined: @@ -1210,10 +1154,7 @@ static void SerializePrimitive(JsValue value, Utf8JsonWriter writer) { writer.WriteNullValue(); break; case Types.Boolean: - if (ReferenceEquals(value, JsBoolean.False)) - writer.WriteBooleanValue(false); - else - writer.WriteBooleanValue(true); + writer.WriteBooleanValue(!ReferenceEquals(value, JsBoolean.False)); break; case Types.Number: writer.WriteNumberValue(value.AsNumber()); @@ -1232,7 +1173,12 @@ static void SerializePrimitive(JsValue value, Utf8JsonWriter writer) { } internal static class ObjectInstanceExtensions { - public static void FastAddProperty(this ObjectInstance target, string name, JsValue value, bool writable, bool enumerable, bool configurable) { - target.FastSetProperty(name, new PropertyDescriptor(value, writable, enumerable, configurable)); + public static void FastAddProperty(this ObjectInstance target, + string name, + JsValue value, + bool writable, + bool enumerable, + bool configurable) { + target.FastSetProperty(name, new(value, writable, enumerable, configurable)); } } diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjection.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjection.cs index 98cf5056f86..de4998865e8 100644 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjection.cs +++ b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjection.cs @@ -16,24 +16,24 @@ using KurrentDB.Core.Services.UserManagement; using KurrentDB.Projections.Core.Common; using KurrentDB.Projections.Core.Messages; -using KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; using KurrentDB.Projections.Core.Services.Processing; using KurrentDB.Projections.Core.Services.Processing.Emitting; using KurrentDB.Projections.Core.Utils; using Serilog; +using static KurrentDB.Core.Messages.ClientMessage; +using static KurrentDB.Projections.Core.Services.Management.ManagedProjectionStateHandler; using ILogger = Serilog.ILogger; using ReadStreamResult = KurrentDB.Core.Data.ReadStreamResult; namespace KurrentDB.Projections.Core.Services.Management; - public static class PersistedStateExtensions { - public static Boolean CheckpointStreamNeedsDeleted(this ManagedProjection.PersistedState persistedState) { - return persistedState.DeleteCheckpointStream && persistedState.CheckpointsDisabled.GetValueOrDefault() == false; + public static bool CheckpointStreamNeedsDeleted(this ManagedProjection.PersistedState persistedState) { + return persistedState.DeleteCheckpointStream && !persistedState.CheckpointsDisabled.GetValueOrDefault(); } - public static Boolean EmitStreamNeedsDeleted(this ManagedProjection.PersistedState persistedState) { - return (persistedState.DeleteEmittedStreams && persistedState.EmitEnabled.GetValueOrDefault()); + public static bool EmitStreamNeedsDeleted(this ManagedProjection.PersistedState persistedState) { + return persistedState.DeleteEmittedStreams && persistedState.EmitEnabled.GetValueOrDefault(); } } @@ -61,58 +61,37 @@ public class PersistedState { public long? Epoch { get; set; } public long? Version { get; set; } public SerializedRunAs RunAs { get; set; } - public int CheckpointHandledThreshold { get; set; } - public int CheckpointAfterMs { get; set; } - public int CheckpointUnhandledBytesThreshold { get; set; } - public int PendingEventsThreshold { get; set; } - public int MaxWriteBatchLength { get; set; } - public int MaxAllowedWritesInFlight { get; set; } + public int CheckpointHandledThreshold { get; set; } = ProjectionConsts.CheckpointHandledThreshold; + public int CheckpointAfterMs { get; set; } = (int)ProjectionConsts.CheckpointAfterMs.TotalMilliseconds; + public int CheckpointUnhandledBytesThreshold { get; set; } = ProjectionConsts.CheckpointUnhandledBytesThreshold; + public int PendingEventsThreshold { get; set; } = ProjectionConsts.PendingEventsThreshold; + public int MaxWriteBatchLength { get; set; } = ProjectionConsts.MaxWriteBatchLength; + public int MaxAllowedWritesInFlight { get; set; } = ProjectionConsts.MaxAllowedWritesInFlight; public int? ProjectionSubsystemVersion { get; set; } - public int? ProjectionExecutionTimeout { get; set; } - - public PersistedState() { - CheckpointHandledThreshold = ProjectionConsts.CheckpointHandledThreshold; - CheckpointAfterMs = (int)ProjectionConsts.CheckpointAfterMs.TotalMilliseconds; - CheckpointUnhandledBytesThreshold = ProjectionConsts.CheckpointUnhandledBytesThreshold; - PendingEventsThreshold = ProjectionConsts.PendingEventsThreshold; - MaxWriteBatchLength = ProjectionConsts.MaxWriteBatchLength; - MaxAllowedWritesInFlight = ProjectionConsts.MaxAllowedWritesInFlight; - } } private readonly IPublisher _output; - - private readonly RequestResponseDispatcher - _streamDispatcher; - - private readonly RequestResponseDispatcher - _writeDispatcher; - + private readonly RequestResponseDispatcher _streamDispatcher; + private readonly RequestResponseDispatcher _writeDispatcher; private readonly ReadDispatcher _readDispatcher; - private readonly - RequestResponseDispatcher - + private readonly RequestResponseDispatcher _getStateDispatcher; - private readonly - RequestResponseDispatcher - + private readonly RequestResponseDispatcher _getResultDispatcher; - private readonly ILogger _logger; private readonly ITimeProvider _timeProvider; private readonly Guid _workerId; - private readonly Guid _id; private readonly long _projectionId; private readonly string _name; private readonly bool _enabledToRun; private ManagedProjectionState _state; - internal PersistedState PersistedProjectionState = new PersistedState(); + internal PersistedState PersistedProjectionState = new(); - private bool _persistedStateLoaded = false; + private bool _persistedStateLoaded; private string _faultedReason; @@ -120,18 +99,17 @@ private readonly private DateTime _lastAccessed; private long _lastWrittenVersion = -1; - private ClaimsPrincipal _runAs; - internal bool Prepared; - internal bool Created; + protected internal bool Prepared { get; set; } + protected internal bool Created { get; set; } private bool _pendingWritePersistedState; private readonly TimeSpan _projectionsQueryExpiry; - private ManagedProjectionStateBase _stateHandler; + private BaseHandler _stateHandler; private IEnvelope _lastReplyEnvelope; private bool _writing; - private IODispatcher _ioDispatcher; + private readonly IODispatcher _ioDispatcher; private ProjectionConfig _projectionConfig; - private IEmittedStreamsDeleter _emittedStreamsDeleter; + private EmittedStreamsDeleter _emittedStreamsDeleter; public ManagedProjection( Guid workerId, @@ -140,32 +118,21 @@ public ManagedProjection( string name, bool enabledToRun, ILogger logger, - RequestResponseDispatcher streamDispatcher, - RequestResponseDispatcher writeDispatcher, + RequestResponseDispatcher streamDispatcher, + RequestResponseDispatcher writeDispatcher, ReadDispatcher readDispatcher, IPublisher output, ITimeProvider timeProvider, - RequestResponseDispatcher - getStateDispatcher, - RequestResponseDispatcher - - getResultDispatcher, + RequestResponseDispatcher getStateDispatcher, + RequestResponseDispatcher getResultDispatcher, IODispatcher ioDispatcher, TimeSpan projectionQueryExpiry) { - if (id == Guid.Empty) - throw new ArgumentException("id"); - if (name == null) - throw new ArgumentNullException("name"); - if (output == null) - throw new ArgumentNullException("output"); - if (getStateDispatcher == null) - throw new ArgumentNullException("getStateDispatcher"); - if (getResultDispatcher == null) - throw new ArgumentNullException("getResultDispatcher"); - if (name == "") - throw new ArgumentException("name"); + ArgumentException.ThrowIfNullOrEmpty(name); + ArgumentNullException.ThrowIfNull(output); + ArgumentNullException.ThrowIfNull(getStateDispatcher); + ArgumentNullException.ThrowIfNull(getResultDispatcher); _workerId = workerId; - _id = id; + Id = Ensure.NotEmptyGuid(id); _projectionId = projectionId; _name = name; _enabledToRun = enabledToRun; @@ -182,103 +149,55 @@ public ManagedProjection( _projectionsQueryExpiry = projectionQueryExpiry; } - private string HandlerType { - get { return PersistedProjectionState.HandlerType; } - } + private string HandlerType => PersistedProjectionState.HandlerType; - private string Query { - get { return PersistedProjectionState.Query; } - } + private string Query => PersistedProjectionState.Query; private bool Enabled { - get { return PersistedProjectionState.Enabled; } - set { PersistedProjectionState.Enabled = value; } + get => PersistedProjectionState.Enabled; + set => PersistedProjectionState.Enabled = value; } - private bool IsMultiStream { - get { - return PersistedProjectionState.SourceDefinition != null && - PersistedProjectionState.SourceDefinition.Streams != null && - PersistedProjectionState.SourceDefinition.Streams.Length > 1; - } - } + private bool IsMultiStream => PersistedProjectionState.SourceDefinition is { Streams.Length: > 1 }; public bool Deleted { - get { return PersistedProjectionState.Deleted; } - private set { PersistedProjectionState.Deleted = value; } + get => PersistedProjectionState.Deleted; + private set => PersistedProjectionState.Deleted = value; } public bool Deleting { - get { return PersistedProjectionState.Deleting; } - private set { PersistedProjectionState.Deleting = value; } + get => PersistedProjectionState.Deleting; + private set => PersistedProjectionState.Deleting = value; } - public Guid Id { - get { return _id; } - } + public Guid Id { get; } - public ProjectionMode Mode { - get { return PersistedProjectionState.Mode; } - } + public ProjectionMode Mode => PersistedProjectionState.Mode; - public ClaimsPrincipal RunAs { - get { return _runAs; } - } + public ClaimsPrincipal RunAs { get; private set; } - public bool EnableContentTypeValidation { - get { - return PersistedProjectionState.ProjectionSubsystemVersion >= - ProjectionsSubsystem.CONTENT_TYPE_VALIDATION_VERSION; - } - } + public bool EnableContentTypeValidation => PersistedProjectionState.ProjectionSubsystemVersion >= + ProjectionsSubsystem.CONTENT_TYPE_VALIDATION_VERSION; internal void SetState(ManagedProjectionState value) { _state = value; - switch (value) { - case ManagedProjectionState.Aborted: - _stateHandler = new AbortedState(this); - break; - case ManagedProjectionState.Aborting: - _stateHandler = new AbortingState(this); - break; - case ManagedProjectionState.Completed: - _stateHandler = new CompletedState(this); - break; - case ManagedProjectionState.Creating: - case ManagedProjectionState.Loading: - case ManagedProjectionState.Loaded: - _stateHandler = new CreatingLoadingLoadedState(this); - break; - case ManagedProjectionState.Faulted: - _stateHandler = new FaultedState(this); - break; - case ManagedProjectionState.LoadingStopped: - _stateHandler = new LoadingStateState(this); - break; - case ManagedProjectionState.Prepared: - _stateHandler = new PreparedState(this); - break; - case ManagedProjectionState.Preparing: - _stateHandler = new PreparingState(this); - break; - case ManagedProjectionState.Running: - _stateHandler = new RunningState(this); - break; - case ManagedProjectionState.Starting: - _stateHandler = new StartingState(this); - break; - case ManagedProjectionState.Stopped: - _stateHandler = new StoppedState(this); - break; - case ManagedProjectionState.Stopping: - _stateHandler = new StoppingState(this); - break; - case ManagedProjectionState.Deleting: - _stateHandler = new DeletingState(this); - break; - default: - throw InvalidProjectionState(value); - } + _stateHandler = value switch { + ManagedProjectionState.Aborted => new Aborted(this), + ManagedProjectionState.Aborting => new AbortingState(this), + ManagedProjectionState.Completed => new CompletedState(this), + ManagedProjectionState.Creating or ManagedProjectionState.Loading or ManagedProjectionState.Loaded => + new CreatingLoadingLoadedState(this), + ManagedProjectionState.Faulted => new FaultedState(this), + ManagedProjectionState.LoadingStopped => new LoadingStateState(this), + ManagedProjectionState.Prepared => new PreparedState(this), + ManagedProjectionState.Preparing => new PreparingState(this), + ManagedProjectionState.Running => new RunningState(this), + ManagedProjectionState.Starting => new StartingState(this), + ManagedProjectionState.Stopped => new StoppedState(this), + ManagedProjectionState.Stopping => new StoppingState(this), + ManagedProjectionState.Deleting => new DeletingState(this), + _ => throw InvalidProjectionState(value) + }; } public void Dispose() { @@ -304,14 +223,14 @@ public ProjectionStatistics GetStatistics() { status.Name = _name; status.ProjectionId = _projectionId; var enabledSuffix = - ((_state == ManagedProjectionState.Stopped || _state == ManagedProjectionState.Faulted) && Enabled + _state is ManagedProjectionState.Stopped or ManagedProjectionState.Faulted && Enabled ? " (Enabled)" - : ""); + : ""; status.Status = (status.Status == "Stopped" && _state == ManagedProjectionState.Completed - ? _state.EnumValueName() - : (!status.Status.StartsWith(_state.EnumValueName()) - ? _state.EnumValueName() + "/" + status.Status - : status.Status)) + enabledSuffix; + ? _state.EnumValueName() + : (!status.Status.StartsWith(_state.EnumValueName()) + ? _state.EnumValueName() + "/" + status.Status + : status.Status)) + enabledSuffix; status.LeaderStatus = _state; } @@ -325,14 +244,12 @@ public void Handle(ProjectionManagementMessage.Command.GetState message) { _lastAccessed = _timeProvider.UtcNow; if (_state >= ManagedProjectionState.Running) { _getStateDispatcher.Publish( - new CoreProjectionManagementMessage.GetState(Guid.NewGuid(), Id, message.Partition, _workerId), - m => - message.Envelope.ReplyWith( - new ProjectionManagementMessage.ProjectionState(_name, m.Partition, m.State, m.Position))); + new(Guid.NewGuid(), Id, message.Partition, _workerId), + m => message.Envelope.ReplyWith( + new ProjectionManagementMessage.ProjectionState(_name, m.Partition, m.State, m.Position))); } else { message.Envelope.ReplyWith( - new ProjectionManagementMessage.ProjectionState( - message.Name, message.Partition, "", position: null)); + new ProjectionManagementMessage.ProjectionState(message.Name, message.Partition, "", position: null)); } } @@ -340,15 +257,12 @@ public void Handle(ProjectionManagementMessage.Command.GetResult message) { _lastAccessed = _timeProvider.UtcNow; if (_state >= ManagedProjectionState.Running) { _getResultDispatcher.Publish( - new CoreProjectionManagementMessage.GetResult(Guid.NewGuid(), Id, message.Partition, _workerId), - m => - message.Envelope.ReplyWith( - new ProjectionManagementMessage.ProjectionResult(_name, m.Partition, m.Result, - m.Position))); + new(Guid.NewGuid(), Id, message.Partition, _workerId), + m => message.Envelope.ReplyWith( + new ProjectionManagementMessage.ProjectionResult(_name, m.Partition, m.Result, m.Position))); } else { message.Envelope.ReplyWith( - new ProjectionManagementMessage.ProjectionResult( - message.Name, message.Partition, "", position: null)); + new ProjectionManagementMessage.ProjectionResult(message.Name, message.Partition, "", position: null)); } } @@ -361,8 +275,7 @@ public void Handle(ProjectionManagementMessage.Command.GetQuery message) { ResultStreamName = PersistedProjectionState.SourceDefinition == null ? "" - : new ProjectionNamesBuilder(_name, PersistedProjectionState.SourceDefinition) - .GetResultStreamName() + : new ProjectionNamesBuilder(_name, PersistedProjectionState.SourceDefinition).GetResultStreamName() }; message.Envelope.ReplyWith( @@ -406,11 +319,8 @@ public void Handle(ProjectionManagementMessage.Command.Abort message) { public void Handle(ProjectionManagementMessage.Command.Enable message) { _lastAccessed = _timeProvider.UtcNow; if (Enabled - && !(_state == ManagedProjectionState.Completed || _state == ManagedProjectionState.Faulted - || _state == ManagedProjectionState.Aborted || - _state == ManagedProjectionState.Loaded - || _state == ManagedProjectionState.Prepared || - _state == ManagedProjectionState.Stopped)) { + && _state is not (ManagedProjectionState.Completed or ManagedProjectionState.Faulted or ManagedProjectionState.Aborted + or ManagedProjectionState.Loaded or ManagedProjectionState.Prepared or ManagedProjectionState.Stopped)) { //Projection is probably Running message.Envelope.ReplyWith(new ProjectionManagementMessage.Updated(message.Name)); return; @@ -424,8 +334,9 @@ public void Handle(ProjectionManagementMessage.Command.Enable message) { } public void Handle(ProjectionManagementMessage.Command.Delete message) { - if ((_state != ManagedProjectionState.Stopped && _state != ManagedProjectionState.Faulted) && - Mode != ProjectionMode.Transient) + if (_state != ManagedProjectionState.Stopped + && _state != ManagedProjectionState.Faulted + && Mode != ProjectionMode.Transient) throw new InvalidOperationException("Cannot delete a projection that hasn't been stopped or faulted."); _lastAccessed = _timeProvider.UtcNow; @@ -437,13 +348,13 @@ public void Handle(ProjectionManagementMessage.Command.Delete message) { PersistedProjectionState.NumberOfPrequisitesMetForDeletion++; } - if ((PersistedProjectionState.EmitEnabled ?? false) && - ((PersistedProjectionState.TrackEmittedStreams ?? false) && - PersistedProjectionState.DeleteEmittedStreams)) { + if (PersistedProjectionState.EmitEnabled == true && + PersistedProjectionState.TrackEmittedStreams == true && + PersistedProjectionState.DeleteEmittedStreams) { PersistedProjectionState.NumberOfPrequisitesMetForDeletion++; } - if ((PersistedProjectionState.EmitEnabled ?? false) && IsMultiStream) { + if (PersistedProjectionState.EmitEnabled == true && IsMultiStream) { PersistedProjectionState.NumberOfPrequisitesMetForDeletion++; } @@ -472,10 +383,10 @@ public void Handle(ProjectionManagementMessage.Command.GetConfig message) { } public void Handle(ProjectionManagementMessage.Command.UpdateConfig message) { - if ((_state != ManagedProjectionState.Stopped && _state != ManagedProjectionState.Faulted) && - Mode != ProjectionMode.Transient) - throw new InvalidOperationException( - "Cannot update the config of a projection that hasn't been stopped or faulted."); + if (_state != ManagedProjectionState.Stopped + && _state != ManagedProjectionState.Faulted + && Mode != ProjectionMode.Transient) + throw new InvalidOperationException("Cannot update the config of a projection that hasn't been stopped or faulted."); _lastAccessed = _timeProvider.UtcNow; PersistedProjectionState.EmitEnabled = message.EmitEnabled; @@ -509,15 +420,17 @@ public void DeleteProjectionStreams() { } if (PersistedProjectionState.EmitStreamNeedsDeleted()) { - if (_emittedStreamsDeleter == null) { - _emittedStreamsDeleter = new EmittedStreamsDeleter(_ioDispatcher, projectionNamesBuilder.GetEmittedStreamsName(), projectionNamesBuilder.GetEmittedStreamsCheckpointName()); - } + _emittedStreamsDeleter ??= new EmittedStreamsDeleter( + _ioDispatcher, + projectionNamesBuilder.GetEmittedStreamsName(), + projectionNamesBuilder.GetEmittedStreamsCheckpointName() + ); _emittedStreamsDeleter.DeleteEmittedStreams(DeleteIfConditionsAreMet); } if (!PersistedProjectionState.CheckpointStreamNeedsDeleted() && - !PersistedProjectionState.EmitStreamNeedsDeleted()) { + !PersistedProjectionState.EmitStreamNeedsDeleted()) { DeleteIfConditionsAreMet(); } } @@ -540,7 +453,7 @@ public void Handle(ProjectionManagementMessage.Command.Reset message) { StopUnlessPreparedOrLoaded(); } - public void Handle(ProjectionManagementMessage.Internal.CleanupExpired message) { + public void Handle(ProjectionManagementMessage.Internal.CleanupExpired _) { if (IsExpiredProjection()) { if (_state == ManagedProjectionState.Creating) { // NOTE: workaround for stop not working on creating state (just ignore them) @@ -561,7 +474,7 @@ public void Handle(ProjectionManagementMessage.Internal.CleanupExpired message) } } - public void Handle(CoreProjectionStatusMessage.Started message) { + public void Handle(CoreProjectionStatusMessage.Started _) { _stateHandler.Started(); } @@ -585,9 +498,7 @@ public void Handle(CoreProjectionStatusMessage.StatisticsReport message) { } private void SetLastReplyEnvelope(IEnvelope envelope) { - if (_lastReplyEnvelope != null) - _lastReplyEnvelope.ReplyWith( - new ProjectionManagementMessage.OperationFailed("Aborted by subsequent operation")); + _lastReplyEnvelope?.ReplyWith(new ProjectionManagementMessage.OperationFailed("Aborted by subsequent operation")); _lastReplyEnvelope = envelope; } @@ -596,10 +507,10 @@ private void Reset() { PersistedProjectionState.Epoch = PersistedProjectionState.Version; } - private bool IsExpiredProjection() { - return Mode == ProjectionMode.Transient && - _lastAccessed.Add(_projectionsQueryExpiry) < _timeProvider.UtcNow && _persistedStateLoaded; - } + private bool IsExpiredProjection() + => Mode == ProjectionMode.Transient + && _lastAccessed.Add(_projectionsQueryExpiry) < _timeProvider.UtcNow + && _persistedStateLoaded; public void InitializeNew(PersistedState persistedState, IEnvelope replyEnvelope) { LoadPersistedState(persistedState); @@ -617,7 +528,7 @@ public void InitializeExisting(string name) { private void ReadPersistedState(string name) { var corrId = Guid.NewGuid(); _readDispatcher.Publish( - new ClientMessage.ReadStreamEventsBackward( + new ReadStreamEventsBackward( corrId, corrId, _readDispatcher.Envelope, ProjectionNamesBuilder.ProjectionsStreamPrefix + name, -1, 1, resolveLinkTos: false, requireLeader: false, validationStreamVersion: null, @@ -627,40 +538,38 @@ private void ReadPersistedState(string name) { new ReadStreamEventsBackwardHandlers.Optimistic(PersistedStateReadCompleted)); } - private void PersistedStateReadCompleted(ClientMessage.ReadStreamEventsBackwardCompleted completed) { - if (completed.Result == ReadStreamResult.Success && completed.Events.Count is 1) { - var persistedState = completed.Events[0].Event.Data.ParseJson(); - - _lastWrittenVersion = completed.Events[0].Event.EventNumber; - FixUpOldFormat(completed, persistedState); - FixupOldProjectionModes(persistedState); - FixUpOldProjectionRunAs(persistedState); + private void PersistedStateReadCompleted(ReadStreamEventsBackwardCompleted completed) { + if (completed.Result != ReadStreamResult.Success || completed.Events.Count is not 1) { + SetState(ManagedProjectionState.Creating); - LoadPersistedState(persistedState); - //TODO: encapsulate this into managed projection - SetState(ManagedProjectionState.Loaded); - _pendingWritePersistedState = false; - SetLastReplyEnvelope(null); - PrepareOrWriteStartOrLoadStopped(); - return; + _logger.Verbose( + "Projection manager did not find any projection configuration records in the {stream} stream. Projection stays in CREATING state", + completed.EventStreamId); } - SetState(ManagedProjectionState.Creating); + var persistedState = completed.Events[0].Event.Data.ParseJson(); + + _lastWrittenVersion = completed.Events[0].Event.EventNumber; + FixUpOldFormat(completed, persistedState); + FixupOldProjectionModes(persistedState); + FixUpOldProjectionRunAs(persistedState); - _logger.Verbose( - "Projection manager did not find any projection configuration records in the {stream} stream. Projection stays in CREATING state", - completed.EventStreamId); + LoadPersistedState(persistedState); + //TODO: encapsulate this into managed projection + SetState(ManagedProjectionState.Loaded); + _pendingWritePersistedState = false; + SetLastReplyEnvelope(null); + PrepareOrWriteStartOrLoadStopped(); } private void FixUpOldProjectionRunAs(PersistedState persistedState) { if (persistedState.RunAs == null || string.IsNullOrEmpty(persistedState.RunAs.Name)) { - _runAs = SystemAccounts.System; + RunAs = SystemAccounts.System; persistedState.RunAs = SerializedRunAs.SerializePrincipal(ProjectionManagementMessage.RunAs.System); } } - private void FixUpOldFormat(ClientMessage.ReadStreamEventsBackwardCompleted completed, - PersistedState persistedState) { + private void FixUpOldFormat(ReadStreamEventsBackwardCompleted completed, PersistedState persistedState) { if (persistedState.Version == null) { persistedState.Version = completed.Events[0].Event.EventNumber; persistedState.Epoch = -1; @@ -670,34 +579,27 @@ private void FixUpOldFormat(ClientMessage.ReadStreamEventsBackwardCompleted comp persistedState.Version = _lastWrittenVersion; } - private void FixupOldProjectionModes(PersistedState persistedState) { + private static void FixupOldProjectionModes(PersistedState persistedState) { switch ((int)persistedState.Mode) { case 2: // old continuous persistedState.Mode = ProjectionMode.Continuous; break; case 3: // old persistent persistedState.Mode = ProjectionMode.Continuous; - persistedState.EmitEnabled = persistedState.EmitEnabled ?? true; + persistedState.EmitEnabled ??= true; break; } } private void LoadPersistedState(PersistedState persistedState) { - var handlerType = persistedState.HandlerType; - var query = persistedState.Query; - - if (handlerType == null) - throw new ArgumentNullException("persistedState", "HandlerType"); - if (query == null) - throw new ArgumentNullException("persistedState", "Query"); - if (handlerType == "") - throw new ArgumentException("HandlerType", "persistedState"); + ArgumentException.ThrowIfNullOrEmpty(persistedState.HandlerType); + ArgumentException.ThrowIfNullOrEmpty(persistedState.Query); if (_state != ManagedProjectionState.Creating && _state != ManagedProjectionState.Loading) throw new InvalidOperationException("LoadPersistedState is now allowed in this state"); PersistedProjectionState = persistedState; - _runAs = SerializedRunAs.DeserializePrincipal(persistedState.RunAs); + RunAs = SerializedRunAs.DeserializePrincipal(persistedState.RunAs); _persistedStateLoaded = true; } @@ -713,14 +615,14 @@ internal void StartCompleted() { Reply(); } - private ClientMessage.WriteEvents CreatePersistedStateEvent(Guid correlationId, PersistedState persistedState, - string eventStreamId) { - return ClientMessage.WriteEvents.ForSingleEvent(correlationId, correlationId, _writeDispatcher.Envelope, true, eventStreamId, ExpectedVersion.Any, + private WriteEvents CreatePersistedStateEvent(Guid correlationId, PersistedState persistedState, string eventStreamId) { + return WriteEvents.ForSingleEvent(correlationId, correlationId, _writeDispatcher.Envelope, true, eventStreamId, + ExpectedVersion.Any, new Event(Guid.NewGuid(), ProjectionEventTypes.ProjectionUpdated, true, persistedState.ToJsonBytes()), SystemAccounts.System); } - private void WritePersistedState(ClientMessage.WriteEvents persistedStateEvent) { + private void WritePersistedState(WriteEvents persistedStateEvent) { if (Mode == ProjectionMode.Transient) { //TODO: move to common completion procedure _lastWrittenVersion = PersistedProjectionState.Version ?? -1; @@ -735,8 +637,7 @@ private void WritePersistedState(ClientMessage.WriteEvents persistedStateEvent) m => WritePersistedStateCompleted(m, persistedStateEvent, persistedStateEvent.EventStreamIds.Single)); } - private void WritePersistedStateCompleted(ClientMessage.WriteEventsCompleted message, - ClientMessage.WriteEvents eventToRetry, string eventStreamId) { + private void WritePersistedStateCompleted(WriteEventsCompleted message, WriteEvents eventToRetry, string eventStreamId) { if (!_writing) { _logger.Error("Projection definition write completed in non writing state. ({projection})", _name); } @@ -757,10 +658,8 @@ private void WritePersistedStateCompleted(ClientMessage.WriteEventsCompleted mes _name, eventStreamId, Enum.GetName(typeof(OperationResult), message.Result)); - if (message.Result == OperationResult.CommitTimeout || message.Result == OperationResult.ForwardTimeout - || message.Result == OperationResult.PrepareTimeout - || message.Result == - OperationResult.WrongExpectedVersion) { + if (message.Result is OperationResult.CommitTimeout or OperationResult.ForwardTimeout or OperationResult.PrepareTimeout + or OperationResult.WrongExpectedVersion) { _logger.Information("Retrying write projection source for {projection}", _name); WritePersistedState(eventToRetry); } else @@ -770,7 +669,7 @@ private void WritePersistedStateCompleted(ClientMessage.WriteEventsCompleted mes private void DeleteStream(string streamId, Action completed) { //delete checkpoint stream var correlationId = Guid.NewGuid(); - _streamDispatcher.Publish(new ClientMessage.DeleteStream( + _streamDispatcher.Publish(new DeleteStream( correlationId, correlationId, _writeDispatcher.Envelope, @@ -781,29 +680,28 @@ private void DeleteStream(string streamId, Action completed) { SystemAccounts.System), m => DeleteStreamCompleted(m, streamId, completed)); } - private void DeleteStreamCompleted(ClientMessage.DeleteStreamCompleted message, string streamId, - Action completed) { - // currently, WrongExpectedVersion is returned when deleting non-existing streams, even when specifying ExpectedVersion.Any. - // it is not too intuitive but changing the response would break the contract and compatibility with TCP/gRPC/web clients or require adding a new error code to all clients. - // note: we don't need to check if CurrentVersion == -1 here to make sure it's a non-existing stream since the deletion is done with ExpectedVersion.Any - if (message.Result == OperationResult.WrongExpectedVersion) { - // stream was never created - _logger.Information("PROJECTIONS: Projection Stream '{stream}' was not deleted since it does not exist", streamId); - completed(); - return; - } - if (message.Result == OperationResult.Success || message.Result == OperationResult.StreamDeleted) { - _logger.Information("PROJECTIONS: Projection Stream '{stream}' deleted", streamId); - completed(); - return; + private void DeleteStreamCompleted(DeleteStreamCompleted message, string streamId, Action completed) { + switch (message.Result) { + // currently, WrongExpectedVersion is returned when deleting non-existing streams, even when specifying ExpectedVersion.Any. + // it is not too intuitive but changing the response would break the contract and compatibility with TCP/gRPC/web clients or require adding a new error code to all clients. + // note: we don't need to check if CurrentVersion == -1 here to make sure it's a non-existing stream since the deletion is done with ExpectedVersion.Any + case OperationResult.WrongExpectedVersion: + // stream was never created + _logger.Information("PROJECTIONS: Projection Stream '{stream}' was not deleted since it does not exist", streamId); + completed(); + return; + case OperationResult.Success: + case OperationResult.StreamDeleted: + _logger.Information("PROJECTIONS: Projection Stream '{stream}' deleted", streamId); + completed(); + return; } _logger.Information( "PROJECTIONS: Projection stream '{stream}' could not be deleted. Error: {e}", streamId, Enum.GetName(typeof(OperationResult), message.Result)); - if (message.Result == OperationResult.CommitTimeout || - message.Result == OperationResult.ForwardTimeout) { + if (message.Result is OperationResult.CommitTimeout or OperationResult.ForwardTimeout) { DeleteStream(streamId, completed); } else throw new NotSupportedException("Unsupported error code received"); @@ -841,38 +739,34 @@ private void Enable() { Enabled = true; } - private Message CreateCreateAndPrepareMessage(ProjectionConfig config) { - return new CoreProjectionManagementMessage.CreateAndPrepare( - Id, - _workerId, - _name, - new ProjectionVersion(_projectionId, PersistedProjectionState.Epoch ?? 0, - PersistedProjectionState.Version ?? 0), - config, - HandlerType, - Query, - EnableContentTypeValidation); - } - - private CoreProjectionManagementMessage.CreatePrepared CreatePreparedMessage(ProjectionConfig config) { - if (PersistedProjectionState.SourceDefinition == null) - throw new Exception( - "The projection cannot be loaded as stopped as it was stored in the old format. Update the projection query text to force prepare"); - - var createProjectionMessage = new CoreProjectionManagementMessage.CreatePrepared( + private CoreProjectionManagementMessage.CreateAndPrepare CreateCreateAndPrepareMessage(ProjectionConfig config) { + return new( Id, _workerId, _name, - new ProjectionVersion(_projectionId, PersistedProjectionState.Epoch ?? 0, - PersistedProjectionState.Version ?? 1), + new(_projectionId, PersistedProjectionState.Epoch ?? 0, PersistedProjectionState.Version ?? 0), config, - QuerySourcesDefinition.From(PersistedProjectionState.SourceDefinition), HandlerType, Query, EnableContentTypeValidation); - return createProjectionMessage; } + private CoreProjectionManagementMessage.CreatePrepared CreatePreparedMessage(ProjectionConfig config) + => PersistedProjectionState.SourceDefinition == null + ? throw new Exception( + "The projection cannot be loaded as stopped as it was stored in the old format." + + " Update the projection query text to force prepare") + : new( + Id, + _workerId, + _name, + new(_projectionId, PersistedProjectionState.Epoch ?? 0, PersistedProjectionState.Version ?? 1), + config, + QuerySourcesDefinition.From(PersistedProjectionState.SourceDefinition), + HandlerType, + Query, + EnableContentTypeValidation); + private void StopUnlessPreparedOrLoaded() { switch (_state) { case ManagedProjectionState.Prepared: @@ -892,9 +786,7 @@ private void StopUnlessPreparedOrLoaded() { case ManagedProjectionState.Loading: case ManagedProjectionState.Creating: throw new InvalidOperationException( - string.Format( - "Cannot stop a projection in the '{0}' state", - Enum.GetName(typeof(ManagedProjectionState), _state))); + $"Cannot stop a projection in the '{Enum.GetName(typeof(ManagedProjectionState), _state)}' state"); case ManagedProjectionState.Stopping: case ManagedProjectionState.Aborting: return; @@ -920,9 +812,7 @@ private void Abort() { case ManagedProjectionState.Loading: case ManagedProjectionState.Creating: throw new InvalidOperationException( - string.Format( - "Cannot stop a projection in the '{0}' state", - Enum.GetName(typeof(ManagedProjectionState), _state))); + $"Cannot stop a projection in the '{Enum.GetName(typeof(ManagedProjectionState), _state)}' state"); case ManagedProjectionState.Stopping: SetState(ManagedProjectionState.Aborting); _output.Publish(new CoreProjectionManagementMessage.Kill(Id, _workerId)); @@ -939,7 +829,6 @@ private void Abort() { } } - public void Fault(string reason) { _logger.Error("The '{projection}' projection faulted due to '{e}'", _name, reason); SetState(ManagedProjectionState.Faulted); @@ -951,8 +840,7 @@ private ProjectionConfig CreateDefaultProjectionConfiguration() { var trackEmittedStreams = PersistedProjectionState.TrackEmittedStreams == true; var maximumCheckpointCount = checkpointsEnabled ? PersistedProjectionState.CheckpointHandledThreshold : 0; var checkpointAfterMs = checkpointsEnabled ? PersistedProjectionState.CheckpointAfterMs : 0; - var checkpointUnhandledBytesThreshold = - checkpointsEnabled ? PersistedProjectionState.CheckpointUnhandledBytesThreshold : 0; + var checkpointUnhandledBytesThreshold = checkpointsEnabled ? PersistedProjectionState.CheckpointUnhandledBytesThreshold : 0; var pendingEventsThreshold = PersistedProjectionState.PendingEventsThreshold; var maxWriteBatchLength = PersistedProjectionState.MaxWriteBatchLength; var maximumAllowedWritesInFlight = PersistedProjectionState.MaxAllowedWritesInFlight; @@ -962,7 +850,7 @@ private ProjectionConfig CreateDefaultProjectionConfiguration() { var projectionExecutionTimeout = PersistedProjectionState.ProjectionExecutionTimeout; var projectionConfig = new ProjectionConfig( - _runAs, + RunAs, maximumCheckpointCount, checkpointUnhandledBytesThreshold, pendingEventsThreshold, @@ -979,20 +867,23 @@ private ProjectionConfig CreateDefaultProjectionConfiguration() { } private void StartOrLoadStopped() { - if (_state == ManagedProjectionState.Prepared) { - if (Enabled && _enabledToRun) + switch (_state) { + case ManagedProjectionState.Prepared when Enabled && _enabledToRun: Start(); - else { + break; + case ManagedProjectionState.Prepared: LoadStopped(); - } - } else if (_state == ManagedProjectionState.Aborted || - _state == ManagedProjectionState.Completed || - _state == ManagedProjectionState.Faulted || - _state == ManagedProjectionState.Stopped || - _state == ManagedProjectionState.Deleting) - Reply(); - else - throw InvalidProjectionState(_state); + break; + case ManagedProjectionState.Aborted: + case ManagedProjectionState.Completed: + case ManagedProjectionState.Faulted: + case ManagedProjectionState.Stopped: + case ManagedProjectionState.Deleting: + Reply(); + break; + default: + throw InvalidProjectionState(_state); + } } private void UpdateQuery(ProjectionManagementMessage.Command.UpdateQuery message) { @@ -1010,19 +901,14 @@ private void Disable() { } public void PrepareOrWriteStartOrLoadStopped() { - if (_state == ManagedProjectionState.Prepared) { - WriteStartOrLoadStopped(); - return; - } - - if (Prepared && Created && !(Enabled && _enabledToRun)) { + if (_state == ManagedProjectionState.Prepared || Prepared && Created && !(Enabled && _enabledToRun)) { WriteStartOrLoadStopped(); return; } _projectionConfig = CreateDefaultProjectionConfiguration(); - var prepareMessage = !(Enabled && _enabledToRun) && !_pendingWritePersistedState + Message prepareMessage = !(Enabled && _enabledToRun) && !_pendingWritePersistedState ? CreatePreparedMessage(_projectionConfig) : CreateCreateAndPrepareMessage(_projectionConfig); @@ -1030,8 +916,7 @@ public void PrepareOrWriteStartOrLoadStopped() { } private void Reply() { - if (_lastReplyEnvelope != null) - _lastReplyEnvelope.ReplyWith(new ProjectionManagementMessage.Updated(_name)); + _lastReplyEnvelope?.ReplyWith(new ProjectionManagementMessage.Updated(_name)); _lastReplyEnvelope = null; if (Deleted && !_pendingWritePersistedState) { DisposeCoreProjection(); @@ -1051,28 +936,25 @@ private void UpdateProjectionVersion(bool force = false) { PersistedProjectionState.Version++; PersistedProjectionState.ProjectionSubsystemVersion = ProjectionsSubsystem.VERSION; } else if (force) - throw new ApplicationException( - "Internal error: projection definition must be saved before forced updating version"); + throw new ApplicationException("Internal error: projection definition must be saved before forced updating version"); } - private static InvalidOperationException InvalidProjectionState(ManagedProjectionState state) => - new InvalidOperationException($"Did not expect {nameof(ManagedProjectionState)} in state {state}."); + private static InvalidOperationException InvalidProjectionState(ManagedProjectionState state) + => new($"Did not expect {nameof(ManagedProjectionState)} in state {state}."); } public class SerializedRunAs { public string Name { get; set; } public string[] Roles { get; set; } - public static implicit operator SerializedRunAs(ProjectionManagementMessage.RunAs runAs) { - return runAs == null ? null : SerializePrincipal(runAs); - } + public static implicit operator SerializedRunAs(ProjectionManagementMessage.RunAs runAs) + => runAs == null ? null : SerializePrincipal(runAs); - public static implicit operator ProjectionManagementMessage.RunAs(SerializedRunAs runAs) { - return new ProjectionManagementMessage.RunAs(DeserializePrincipal(runAs)); - } + public static implicit operator ProjectionManagementMessage.RunAs(SerializedRunAs runAs) + => new(DeserializePrincipal(runAs)); public static SerializedRunAs SerializePrincipal(ProjectionManagementMessage.RunAs runAs) { - if (runAs?.Principal == null || runAs.Principal.HasClaim(x => x.Type == ClaimTypes.Anonymous)) + if (runAs?.Principal?.Identity == null || runAs.Principal.HasClaim(x => x.Type == ClaimTypes.Anonymous)) return null; // anonymous if (runAs.Principal == SystemAccounts.System) return new SerializedRunAs { Name = "$system" }; @@ -1085,17 +967,20 @@ public static SerializedRunAs SerializePrincipal(ProjectionManagementMessage.Run } public static ClaimsPrincipal DeserializePrincipal(SerializedRunAs runAs) { - if (runAs?.Name == null) - return SystemAccounts.Anonymous; - if (runAs.Name == "$system") //TODO: make sure nobody else uses it - return SystemAccounts.System; + switch (runAs?.Name) { + case null: + return SystemAccounts.Anonymous; + //TODO: make sure nobody else uses it + case "$system": + return SystemAccounts.System; + } + var claims = runAs.Roles.Select(x => x.Split("$$$")).Select(x => { return x.Length switch { 1 => new Claim(ClaimTypes.Role, x[0]), 2 => new Claim(x[0], x[1]), _ => throw new SerializationException("Could not deserialize run as") }; - }).ToList(); if (!claims.Exists(x => x.Type == ClaimTypes.Name)) { claims.Add(new Claim(ClaimTypes.Name, runAs.Name)); diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStateHandler.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStateHandler.cs new file mode 100644 index 00000000000..a65610816b1 --- /dev/null +++ b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStateHandler.cs @@ -0,0 +1,136 @@ +// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. +// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). + +using KurrentDB.Projections.Core.Messages; + +namespace KurrentDB.Projections.Core.Services.Management; + +public static class ManagedProjectionStateHandler { + internal abstract class BaseHandler(ManagedProjection managedProjection) { + protected readonly ManagedProjection ManagedProjection = managedProjection; + + private void Unexpected(string message) { + ManagedProjection.Fault($"{message} in {GetType().Name}"); + } + + protected void SetFaulted(string reason) { + ManagedProjection.Fault(reason); + } + + protected internal virtual void Started() { + Unexpected("Unexpected 'STARTED' message"); + } + + protected internal virtual void Stopped(CoreProjectionStatusMessage.Stopped message) { + Unexpected("Unexpected 'STOPPED' message"); + } + + protected internal virtual void Faulted(CoreProjectionStatusMessage.Faulted message) { + Unexpected("Unexpected 'FAULTED' message"); + } + + protected internal virtual void Prepared(CoreProjectionStatusMessage.Prepared message) { + Unexpected("Unexpected 'PREPARED' message"); + } + } + + internal class Aborted(ManagedProjection managedProjection) : BaseHandler(managedProjection); + + internal class AbortingState(ManagedProjection managedProjection) : BaseHandler(managedProjection) { + protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { + ManagedProjection.SetState(ManagedProjectionState.Aborted); + ManagedProjection.PrepareOrWriteStartOrLoadStopped(); + } + + protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { + ManagedProjection.SetState(ManagedProjectionState.Aborted); + ManagedProjection.PrepareOrWriteStartOrLoadStopped(); + } + } + + internal class CompletedState(ManagedProjection managedProjection) : BaseHandler(managedProjection); + + internal class CreatingLoadingLoadedState(ManagedProjection managedProjection) : BaseHandler(managedProjection); + + internal class DeletingState : BaseHandler { + public DeletingState(ManagedProjection managedProjection) : base(managedProjection) { + ManagedProjection.DeleteProjectionStreams(); + } + } + + internal class FaultedState(ManagedProjection managedProjection) : BaseHandler(managedProjection); + + internal class LoadingStateState(ManagedProjection managedProjection) : BaseHandler(managedProjection) { + protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { + ManagedProjection.SetState(ManagedProjectionState.Stopped); + ManagedProjection.PrepareOrWriteStartOrLoadStopped(); + } + + protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { + SetFaulted(message.FaultedReason); + ManagedProjection.PrepareOrWriteStartOrLoadStopped(); + } + } + + internal class PreparedState(ManagedProjection managedProjection) : BaseHandler(managedProjection); + + internal class PreparingState(ManagedProjection managedProjection) : BaseHandler(managedProjection) { + protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { + SetFaulted(message.FaultedReason); + ManagedProjection.PersistedProjectionState.SourceDefinition = null; + ManagedProjection.WriteStartOrLoadStopped(); + } + + protected internal override void Prepared(CoreProjectionStatusMessage.Prepared message) { + ManagedProjection.SetState(ManagedProjectionState.Prepared); + ManagedProjection.PersistedProjectionState.SourceDefinition = message.SourceDefinition; + ManagedProjection.Prepared = true; + ManagedProjection.Created = true; + ManagedProjection.WriteStartOrLoadStopped(); + } + } + + internal class RunningState(ManagedProjection managedProjection) : BaseHandler(managedProjection) { + protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { + SetFaulted(message.FaultedReason); + } + + protected internal override void Started() { + // do nothing - may mean second pahse started + //TODO: stop sending second Started + } + + protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { + if (message.Completed) + ManagedProjection.SetState(ManagedProjectionState.Completed); + else + base.Stopped(message); + } + } + + internal class StartingState(ManagedProjection managedProjection) : BaseHandler(managedProjection) { + protected internal override void Started() { + ManagedProjection.SetState(ManagedProjectionState.Running); + ManagedProjection.StartCompleted(); + } + + protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { + SetFaulted(message.FaultedReason); + ManagedProjection.StartCompleted(); + } + } + + internal class StoppedState(ManagedProjection managedProjection) : BaseHandler(managedProjection); + + internal class StoppingState(ManagedProjection managedProjection) : BaseHandler(managedProjection) { + protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { + ManagedProjection.SetState(message.Completed ? ManagedProjectionState.Completed : ManagedProjectionState.Stopped); + ManagedProjection.PrepareOrWriteStartOrLoadStopped(); + } + + protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { + SetFaulted(message.FaultedReason); + ManagedProjection.PrepareOrWriteStartOrLoadStopped(); + } + } +} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortedState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortedState.cs deleted file mode 100644 index fbb9e01e168..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortedState.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class AbortedState : ManagedProjectionStateBase { - public AbortedState(ManagedProjection managedProjection) - : base(managedProjection) { - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortingState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortingState.cs deleted file mode 100644 index 9ecaff5ecb3..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/AbortingState.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class AbortingState : ManagedProjectionStateBase { - public AbortingState(ManagedProjection managedProjection) - : base(managedProjection) { - } - - protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { - _managedProjection.SetState(ManagedProjectionState.Aborted); - _managedProjection.PrepareOrWriteStartOrLoadStopped(); - } - - protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { - _managedProjection.SetState(ManagedProjectionState.Aborted); - _managedProjection.PrepareOrWriteStartOrLoadStopped(); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CompletedState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CompletedState.cs deleted file mode 100644 index d9de9887b78..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CompletedState.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class CompletedState : ManagedProjectionStateBase { - public CompletedState(ManagedProjection managedProjection) - : base(managedProjection) { - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CreatingLoadingLoadedState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CreatingLoadingLoadedState.cs deleted file mode 100644 index 21e32e84042..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/CreatingLoadingLoadedState.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class CreatingLoadingLoadedState : ManagedProjectionStateBase { - public CreatingLoadingLoadedState(ManagedProjection managedProjection) - : base(managedProjection) { - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/DeletingState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/DeletingState.cs deleted file mode 100644 index 89d87f86a6a..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/DeletingState.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class DeletingState : ManagedProjectionStateBase { - public DeletingState(ManagedProjection managedProjection) - : base(managedProjection) { - managedProjection.DeleteProjectionStreams(); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/FaultedState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/FaultedState.cs deleted file mode 100644 index 512a9f69010..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/FaultedState.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class FaultedState : ManagedProjectionStateBase { - public FaultedState(ManagedProjection managedProjection) - : base(managedProjection) { - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/LoadingStateState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/LoadingStateState.cs deleted file mode 100644 index 1848a80cf3f..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/LoadingStateState.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class LoadingStateState : ManagedProjectionStateBase { - public LoadingStateState(ManagedProjection managedProjection) - : base(managedProjection) { - } - - protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { - _managedProjection.SetState(ManagedProjectionState.Stopped); - _managedProjection.PrepareOrWriteStartOrLoadStopped(); - } - - protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { - SetFaulted(message.FaultedReason); - _managedProjection.PrepareOrWriteStartOrLoadStopped(); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/ManagedProjectionStateBase.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/ManagedProjectionStateBase.cs deleted file mode 100644 index 4692b0a9e18..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/ManagedProjectionStateBase.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -internal abstract class ManagedProjectionStateBase { - protected readonly ManagedProjection _managedProjection; - - protected ManagedProjectionStateBase(ManagedProjection managedProjection) { - _managedProjection = managedProjection; - } - - private void Unexpected(string message) { - _managedProjection.Fault(message + " in " + this.GetType().Name); - } - - protected void SetFaulted(string reason) { - _managedProjection.Fault(reason); - } - - protected internal virtual void Started() { - Unexpected("Unexpected 'STARTED' message"); - } - - protected internal virtual void Stopped(CoreProjectionStatusMessage.Stopped message) { - Unexpected("Unexpected 'STOPPED' message"); - } - - protected internal virtual void Faulted(CoreProjectionStatusMessage.Faulted message) { - Unexpected("Unexpected 'FAULTED' message"); - } - - protected internal virtual void Prepared(CoreProjectionStatusMessage.Prepared message) { - Unexpected("Unexpected 'PREPARED' message"); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparedState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparedState.cs deleted file mode 100644 index 09673d4bf03..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparedState.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class PreparedState : ManagedProjectionStateBase { - public PreparedState(ManagedProjection managedProjection) - : base(managedProjection) { - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparingState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparingState.cs deleted file mode 100644 index cf3ce7b442e..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/PreparingState.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class PreparingState : ManagedProjectionStateBase { - public PreparingState(ManagedProjection managedProjection) - : base(managedProjection) { - } - - protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { - SetFaulted(message.FaultedReason); - _managedProjection.PersistedProjectionState.SourceDefinition = null; - _managedProjection.WriteStartOrLoadStopped(); - } - - protected internal override void Prepared(CoreProjectionStatusMessage.Prepared message) { - _managedProjection.SetState(ManagedProjectionState.Prepared); - _managedProjection.PersistedProjectionState.SourceDefinition = message.SourceDefinition; - _managedProjection.Prepared = true; - _managedProjection.Created = true; - _managedProjection.WriteStartOrLoadStopped(); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/RunningState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/RunningState.cs deleted file mode 100644 index bf51f00b283..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/RunningState.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class RunningState : ManagedProjectionStateBase { - public RunningState(ManagedProjection managedProjection) - : base(managedProjection) { - } - - protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { - SetFaulted(message.FaultedReason); - } - - protected internal override void Started() { - // do nothing - may mean second pahse started - //TODO: stop sending second Started - } - - protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { - if (message.Completed) - _managedProjection.SetState(ManagedProjectionState.Completed); - else - base.Stopped(message); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StartingState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StartingState.cs deleted file mode 100644 index c618f3335cc..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StartingState.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class StartingState : ManagedProjectionStateBase { - public StartingState(ManagedProjection managedProjection) - : base(managedProjection) { - } - - protected internal override void Started() { - _managedProjection.SetState(ManagedProjectionState.Running); - _managedProjection.StartCompleted(); - } - - protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { - SetFaulted(message.FaultedReason); - _managedProjection.StartCompleted(); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppedState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppedState.cs deleted file mode 100644 index 040d2498a64..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppedState.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class StoppedState : ManagedProjectionStateBase { - public StoppedState(ManagedProjection managedProjection) - : base(managedProjection) { - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppingState.cs b/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppingState.cs deleted file mode 100644 index 23a6670cd63..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Management/ManagedProjectionStates/StoppingState.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Messages; - -namespace KurrentDB.Projections.Core.Services.Management.ManagedProjectionStates; - -class StoppingState : ManagedProjectionStateBase { - public StoppingState(ManagedProjection managedProjection) - : base(managedProjection) { - } - - protected internal override void Stopped(CoreProjectionStatusMessage.Stopped message) { - _managedProjection.SetState( - message.Completed ? ManagedProjectionState.Completed : ManagedProjectionState.Stopped); - _managedProjection.PrepareOrWriteStartOrLoadStopped(); - } - - protected internal override void Faulted(CoreProjectionStatusMessage.Faulted message) { - SetFaulted(message.FaultedReason); - _managedProjection.PrepareOrWriteStartOrLoadStopped(); - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Management/ProjectionManager.cs b/src/KurrentDB.Projections.Core/Services/Management/ProjectionManager.cs index f94e8ab898a..efe72d27444 100644 --- a/src/KurrentDB.Projections.Core/Services/Management/ProjectionManager.cs +++ b/src/KurrentDB.Projections.Core/Services/Management/ProjectionManager.cs @@ -19,7 +19,9 @@ using KurrentDB.Projections.Core.Metrics; using KurrentDB.Projections.Core.Services.Processing; using KurrentDB.Projections.Core.Standard; +using KurrentDB.Projections.Core.Utils; using Serilog; +using static KurrentDB.Projections.Core.Messages.ProjectionManagementMessage; using ILogger = Serilog.ILogger; using OperationResult = KurrentDB.Core.Messages.OperationResult; using TelemetryMessage = KurrentDB.Core.Telemetry.TelemetryMessage; @@ -32,20 +34,20 @@ public class ProjectionManager IHandle, IHandle, IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, IHandle, IHandle, IHandle, @@ -66,15 +68,14 @@ public class ProjectionManager private readonly IPublisher _inputQueue; private readonly IPublisher _publisher; - private readonly Tuple[] _queues; private readonly Guid[] _workers; private readonly TimeSpan _projectionsQueryExpiry; private readonly ITimeProvider _timeProvider; private readonly ProjectionType _runProjections; private readonly bool _initializeSystemProjections; - private readonly Dictionary _projections; - private readonly Dictionary _projectionsMap; + private readonly Dictionary _projections = new(); + private readonly Dictionary _projectionsMap = new(); private readonly RequestResponseDispatcher _writeDispatcher; @@ -89,14 +90,14 @@ private readonly private readonly ReadDispatcher _readDispatcher; - private int _readEventsBatchSize = 100; + private const int _readEventsBatchSize = 100; - private int _lastUsedQueue = 0; + private int _lastUsedQueue; private bool _started; private bool _projectionsStarted; - private long _projectionsRegistrationExpectedVersion = 0; - private bool _isWritePending = false; - private HashSet _projectionsRegistrationState = new HashSet(); + private long _projectionsRegistrationExpectedVersion; + private bool _isWritePending; + private readonly HashSet _projectionsRegistrationState = []; private readonly IEnvelope _publishEnvelope; private readonly @@ -111,7 +112,7 @@ private readonly private readonly IODispatcher _ioDispatcher; private Guid _instanceCorrelationId = Guid.Empty; - private IProjectionTracker _projectionTracker; + private readonly IProjectionTracker _projectionTracker; private readonly TimeSpan _interval = TimeSpan.FromMilliseconds(2000); private readonly TimerMessage.Schedule _getStats; @@ -125,19 +126,16 @@ public ProjectionManager( TimeSpan projectionQueryExpiry, IProjectionTracker projectionTracker, bool initializeSystemProjections = true) { - if (inputQueue == null) - throw new ArgumentNullException("inputQueue"); - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (queueMap == null) - throw new ArgumentNullException("queueMap"); + ArgumentNullException.ThrowIfNull(inputQueue); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(queueMap); if (queueMap.Count == 0) - throw new ArgumentException("At least one queue is required", "queueMap"); + throw new ArgumentException("At least one queue is required", nameof(queueMap)); _inputQueue = inputQueue; _publisher = publisher; - _queues = queueMap.Select(v => Tuple.Create(v.Key, v.Value)).ToArray(); - _workers = _queues.Select(v => v.Item1).ToArray(); + var queues = queueMap.Select(v => Tuple.Create(v.Key, v.Value)).ToArray(); + _workers = queues.Select(v => v.Item1).ToArray(); _timeProvider = timeProvider; _runProjections = runProjections; @@ -146,57 +144,23 @@ public ProjectionManager( _projectionsQueryExpiry = projectionQueryExpiry; _projectionTracker = projectionTracker; - _writeDispatcher = - new RequestResponseDispatcher( - publisher, - v => v.CorrelationId, - v => v.CorrelationId, - _inputQueue); - _readDispatcher = new ReadDispatcher( - publisher, - v => v.CorrelationId, - v => v.CorrelationId, - v => v.CorrelationId, - _inputQueue); - _readForwardDispatcher = - new RequestResponseDispatcher - ( - publisher, - v => v.CorrelationId, - v => v.CorrelationId, - _inputQueue); - _streamDispatcher = - new RequestResponseDispatcher( - publisher, - v => v.CorrelationId, - v => v.CorrelationId, - _inputQueue); - - _projections = new Dictionary(); - _projectionsMap = new Dictionary(); + _writeDispatcher = new(publisher, v => v.CorrelationId, v => v.CorrelationId, _inputQueue); + _readDispatcher = new(publisher, v => v.CorrelationId, v => v.CorrelationId, v => v.CorrelationId, _inputQueue); + _readForwardDispatcher = new(publisher, v => v.CorrelationId, v => v.CorrelationId, _inputQueue); + _streamDispatcher = new(publisher, v => v.CorrelationId, v => v.CorrelationId, _inputQueue); _publishEnvelope = _inputQueue; - _getStateDispatcher = - new RequestResponseDispatcher - ( - _publisher, - v => v.CorrelationId, - v => v.CorrelationId, - _inputQueue); - _getResultDispatcher = - new RequestResponseDispatcher - ( - _publisher, - v => v.CorrelationId, - v => v.CorrelationId, - _inputQueue); + _getStateDispatcher = new(_publisher, v => v.CorrelationId, v => v.CorrelationId, _inputQueue); + _getResultDispatcher = new(_publisher, v => v.CorrelationId, v => v.CorrelationId, _inputQueue); _getStats = TimerMessage.Schedule.Create(_interval, _inputQueue, - new ProjectionManagementMessage.Command.GetStatistics(new CallbackEnvelope(PushStatsToProjectionTracker), ProjectionMode.AllNonTransient, null)); + new Command.GetStatistics(new CallbackEnvelope(PushStatsToProjectionTracker), + ProjectionMode.AllNonTransient, null)); } private void PushStatsToProjectionTracker(Message message) { - if (message is ProjectionManagementMessage.Statistics stats) { + if (message is Statistics stats) { _projectionTracker.OnNewStats(stats.Projections); } + _publisher.Publish(_getStats); } @@ -212,12 +176,11 @@ public void Handle(ProjectionSubsystemMessage.StartComponents message) { _started = true; if (_runProjections >= ProjectionType.System) - StartExistingProjections( - () => { - _projectionsStarted = true; - _publisher.Publish(_getStats); - ScheduleExpire(); - }); + StartExistingProjections(() => { + _projectionsStarted = true; + _publisher.Publish(_getStats); + ScheduleExpire(); + }); _publisher.Publish(new ProjectionSubsystemMessage.ComponentStarted(ServiceName, _instanceCorrelationId)); } @@ -230,9 +193,11 @@ public void Handle(ProjectionSubsystemMessage.StopComponents message) { if (_instanceCorrelationId != message.InstanceCorrelationId) { _logger.Debug("PROJECTIONS: Projection Manager received stop request for incorrect correlation id." + - "Current: {correlationId}. Requested: {requestedCorrelationId}", _instanceCorrelationId, message.InstanceCorrelationId); + "Current: {correlationId}. Requested: {requestedCorrelationId}", _instanceCorrelationId, + message.InstanceCorrelationId); return; } + _logger.Debug("PROJECTIONS: Stopping Projections Manager. Correlation {correlation}", _instanceCorrelationId); Stop(); } @@ -250,8 +215,7 @@ private void ScheduleExpire() { private void Stop() { _started = false; _projectionsStarted = false; - _ioDispatcher.StartDraining(() => - _publisher.Publish(new ProjectionSubsystemMessage.IODispatcherDrained(ServiceName))); + _ioDispatcher.StartDraining(() => _publisher.Publish(new ProjectionSubsystemMessage.IODispatcherDrained(ServiceName))); _projections.Clear(); _projectionsMap.Clear(); @@ -259,13 +223,13 @@ private void Stop() { _publisher.Publish(new ProjectionSubsystemMessage.ComponentStopped(ServiceName, _instanceCorrelationId)); } - public void Handle(ProjectionManagementMessage.Command.Post message) { + public void Handle(Command.Post message) { if (!_projectionsStarted) return; if (message.Mode == ProjectionMode.Transient) { var transientProjection = new PendingProjection(ProjectionQueryId, message); - if (!ValidateProjections(new[] { transientProjection }, message)) + if (!ValidateProjections([transientProjection], message)) return; PostNewTransientProjection(transientProjection, message.Envelope); @@ -275,7 +239,7 @@ public void Handle(ProjectionManagementMessage.Command.Post message) { } else { var expectedVersion = _projectionsRegistrationExpectedVersion; var pendingProjections = new Dictionary { - {message.Name, new PendingProjection(expectedVersion + 1, message)} + { message.Name, new PendingProjection(expectedVersion + 1, message) } }; if (!ValidateProjections(pendingProjections.Values.ToArray(), message)) return; @@ -285,16 +249,16 @@ public void Handle(ProjectionManagementMessage.Command.Post message) { } } - public void Handle(ProjectionManagementMessage.Command.PostBatch message) { - if (!_projectionsStarted || !message.Projections.Any()) + public void Handle(Command.PostBatch message) { + if (!_projectionsStarted || message.Projections.Length == 0) return; if (message.Projections.Any(p => p.Mode == ProjectionMode.Transient)) { message.Envelope.ReplyWith( - new ProjectionManagementMessage.OperationFailed( - "Transient projections in batches are not supported.")); + new OperationFailed("Transient projections in batches are not supported.")); return; } + if (_isWritePending) { DelayMessage(message); } else { @@ -316,26 +280,22 @@ public void Handle(ProjectionManagementMessage.Command.PostBatch message) { private bool ValidateProjections( PendingProjection[] projections, - ProjectionManagementMessage.Command.ControlMessage message) { + Command.ControlMessage message) { var duplicateNames = new List(); foreach (var projection in projections) { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs( - projection.Mode, - ReadWrite.Write, - null, - message, - replace: projection.EnableRunAs)) { - + if (!RunAs.ValidateRunAs( + projection.Mode, + ReadWrite.Write, + message, + replace: projection.EnableRunAs)) { _logger.Information("PROJECTIONS: Projections batch rejected due to invalid RunAs"); - message.Envelope.ReplyWith( - new ProjectionManagementMessage.OperationFailed("Invalid RunAs")); + message.Envelope.ReplyWith(new OperationFailed("Invalid RunAs")); return false; } if (string.IsNullOrWhiteSpace(projection.Name)) { - message.Envelope.ReplyWith( - new ProjectionManagementMessage.OperationFailed("Projection name is required")); + message.Envelope.ReplyWith(new OperationFailed("Projection name is required")); return false; } @@ -344,66 +304,63 @@ private bool ValidateProjections( } } - if (duplicateNames.Any()) { + if (duplicateNames.Count != 0) { var duplicatesMsg = $"Duplicate projection names : {string.Join(", ", duplicateNames)}"; _logger.Debug($"PROJECTIONS: Conflict. {duplicatesMsg}"); - message.Envelope.ReplyWith( - new ProjectionManagementMessage.Conflict(duplicatesMsg)); + message.Envelope.ReplyWith(new Conflict(duplicatesMsg)); return false; } return true; } - public void Handle(ProjectionManagementMessage.Command.Delete message) { + public void Handle(Command.Delete message) { if (!_projectionsStarted) return; var projection = GetProjection(message.Name); if (projection == null) { - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); return; } if (IsSystemProjection(message.Name)) { message.Envelope.ReplyWith( - new ProjectionManagementMessage.OperationFailed( + new OperationFailed( "We currently don't allow for the deletion of System Projections.")); return; } - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, message)) return; try { projection.Handle(message); } catch (InvalidOperationException ex) { - message.Envelope.ReplyWith(new ProjectionManagementMessage.OperationFailed(ex.Message)); + message.Envelope.ReplyWith(new OperationFailed(ex.Message)); } } - private bool IsSystemProjection(string name) { - return name == ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection || - name == ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection || - name == ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection || - name == ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection || - name == ProjectionNamesBuilder.StandardProjections.EventByCorrIdStandardProjection; + private static bool IsSystemProjection(string name) { + return name is ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection + or ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection + or ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection + or ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection + or ProjectionNamesBuilder.StandardProjections.EventByCorrIdStandardProjection; } - public void Handle(ProjectionManagementMessage.Command.GetQuery message) { + public void Handle(Command.GetQuery message) { if (!_projectionsStarted) return; var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Read, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Read, message)) return; projection.Handle(message); } } - public void Handle(ProjectionManagementMessage.Command.UpdateQuery message) { + public void Handle(Command.UpdateQuery message) { if (!_projectionsStarted) return; _logger.Information( @@ -413,32 +370,30 @@ public void Handle(ProjectionManagementMessage.Command.UpdateQuery message) { var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, message)) return; projection.Handle(message); // update query text } } - public void Handle(ProjectionManagementMessage.Command.Disable message) { + public void Handle(Command.Disable message) { if (!_projectionsStarted) return; _logger.Information("Disabling '{projection}' projection", message.Name); var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, message)) return; projection.Handle(message); } } - public void Handle(ProjectionManagementMessage.Command.Enable message) { + public void Handle(Command.Enable message) { if (!_projectionsStarted) return; _logger.Information("Enabling '{projection}' projection", message.Name); @@ -446,32 +401,30 @@ public void Handle(ProjectionManagementMessage.Command.Enable message) { var projection = GetProjection(message.Name); if (projection == null) { _logger.Error("DBG: PROJECTION *{projection}* NOT FOUND.", message.Name); - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); } else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, message)) return; projection.Handle(message); } } - public void Handle(ProjectionManagementMessage.Command.Abort message) { + public void Handle(Command.Abort message) { if (!_projectionsStarted) return; _logger.Information("Aborting '{projection}' projection", message.Name); var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, message)) return; projection.Handle(message); } } - public void Handle(ProjectionManagementMessage.Command.Reset message) { + public void Handle(Command.Reset message) { if (!_projectionsStarted) return; _logger.Information("Resetting '{projection}' projection", message.Name); @@ -479,89 +432,83 @@ public void Handle(ProjectionManagementMessage.Command.Reset message) { var projection = GetProjection(message.Name); if (projection == null) { _logger.Error("DBG: PROJECTION *{projection}* NOT FOUND.", message.Name); - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); } else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Write, message)) return; projection.Handle(message); } } - public void Handle(ProjectionManagementMessage.Command.GetStatistics message) { + public void Handle(Command.GetStatistics message) { if (!_projectionsStarted) return; if (!string.IsNullOrEmpty(message.Name)) { var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else - message.Envelope.ReplyWith( - new ProjectionManagementMessage.Statistics(new[] { projection.GetStatistics() })); - + message.Envelope.ReplyWith(new Statistics([projection.GetStatistics()])); } else { var statuses = (from projectionNameValue in _projections - let projection = projectionNameValue.Value - where !projection.Deleted - where - message.Mode == null || message.Mode == projection.Mode - || (message.Mode.GetValueOrDefault() == ProjectionMode.AllNonTransient - && projection.Mode != ProjectionMode.Transient) - let status = projection.GetStatistics() - select status).ToArray(); - message.Envelope.ReplyWith(new ProjectionManagementMessage.Statistics(statuses)); + let projection = projectionNameValue.Value + where !projection.Deleted + where + message.Mode == null || message.Mode == projection.Mode + || (message.Mode.GetValueOrDefault() == ProjectionMode.AllNonTransient + && projection.Mode != ProjectionMode.Transient) + let status = projection.GetStatistics() + select status).ToArray(); + message.Envelope.ReplyWith(new Statistics(statuses)); } } - public void Handle(ProjectionManagementMessage.Command.GetState message) { + public void Handle(Command.GetState message) { if (!_projectionsStarted) return; var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else projection.Handle(message); } - public void Handle(ProjectionManagementMessage.Command.GetResult message) { + public void Handle(Command.GetResult message) { if (!_projectionsStarted) return; var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else projection.Handle(message); } - public void Handle(ProjectionManagementMessage.Command.GetConfig message) { + public void Handle(Command.GetConfig message) { if (!_projectionsStarted) return; var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Read, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Read, message)) return; projection.Handle(message); } } - public void Handle(ProjectionManagementMessage.Command.UpdateConfig message) { + public void Handle(Command.UpdateConfig message) { if (!_projectionsStarted) return; var projection = GetProjection(message.Name); if (projection == null) - message.Envelope.ReplyWith(new ProjectionManagementMessage.NotFound()); + message.Envelope.ReplyWith(new NotFound()); else { - if (!ProjectionManagementMessage.RunAs.ValidateRunAs(projection.Mode, ReadWrite.Read, projection.RunAs, - message)) + if (!RunAs.ValidateRunAs(projection.Mode, ReadWrite.Read, message)) return; try { projection.Handle(message); } catch (InvalidOperationException ex) { - message.Envelope.ReplyWith(new ProjectionManagementMessage.OperationFailed(ex.Message)); - return; + message.Envelope.ReplyWith(new OperationFailed(ex.Message)); } } } @@ -578,32 +525,28 @@ private void CleanupExpired() { } public void Handle(CoreProjectionStatusMessage.Started message) { - string name; - if (_projectionsMap.TryGetValue(message.ProjectionId, out name)) { + if (_projectionsMap.TryGetValue(message.ProjectionId, out var name)) { var projection = _projections[name]; projection.Handle(message); } } public void Handle(CoreProjectionStatusMessage.Stopped message) { - string name; - if (_projectionsMap.TryGetValue(message.ProjectionId, out name)) { + if (_projectionsMap.TryGetValue(message.ProjectionId, out var name)) { var projection = _projections[name]; projection.Handle(message); } } public void Handle(CoreProjectionStatusMessage.Faulted message) { - string name; - if (_projectionsMap.TryGetValue(message.ProjectionId, out name)) { + if (_projectionsMap.TryGetValue(message.ProjectionId, out var name)) { var projection = _projections[name]; projection.Handle(message); } } public void Handle(CoreProjectionStatusMessage.Prepared message) { - string name; - if (_projectionsMap.TryGetValue(message.ProjectionId, out name)) { + if (_projectionsMap.TryGetValue(message.ProjectionId, out var name)) { var projection = _projections[name]; projection.Handle(message); } @@ -618,8 +561,7 @@ public void Handle(CoreProjectionStatusMessage.ResultReport message) { } public void Handle(CoreProjectionStatusMessage.StatisticsReport message) { - string name; - if (_projectionsMap.TryGetValue(message.ProjectionId, out name)) { + if (_projectionsMap.TryGetValue(message.ProjectionId, out var name)) { var projection = _projections[name]; projection.Handle(message); } @@ -669,20 +611,22 @@ public void Handle(ProjectionManagementMessage.Internal.Deleted message) { } private void DeleteProjection( - ProjectionManagementMessage.Internal.Deleted message, Action completed, int retryCount = ProjectionCreationRetryCount) { + ProjectionManagementMessage.Internal.Deleted message, + Action completed, + int retryCount = ProjectionCreationRetryCount) { var corrId = Guid.NewGuid(); var writeDelete = ClientMessage.WriteEvents.ForSingleEvent(corrId, - corrId, - _writeDispatcher.Envelope, - true, - ProjectionNamesBuilder.ProjectionsRegistrationStream, - _projectionsRegistrationExpectedVersion, - new Event( - Guid.NewGuid(), - ProjectionEventTypes.ProjectionDeleted, - false, - Helper.UTF8NoBom.GetBytes(message.Name)), - SystemAccounts.System); + corrId, + _writeDispatcher.Envelope, + true, + ProjectionNamesBuilder.ProjectionsRegistrationStream, + _projectionsRegistrationExpectedVersion, + new Event( + Guid.NewGuid(), + ProjectionEventTypes.ProjectionDeleted, + false, + Helper.UTF8NoBom.GetBytes(message.Name)), + SystemAccounts.System); _isWritePending = true; _writeDispatcher.Publish( @@ -697,22 +641,18 @@ public void Dispose() { } private ManagedProjection GetProjection(string name) { - ManagedProjection result; - return _projections.TryGetValue(name, out result) ? result : null; + return _projections.TryGetValue(name, out var result) ? result : null; } private void StartExistingProjections(Action completed) { var registeredProjections = new Dictionary(); - ReadProjectionsList( - registeredProjections, - r => StartRegisteredProjections(r, completed)); + ReadProjectionsList(registeredProjections, r => StartRegisteredProjections(r, completed)); } private void ReadProjectionsList( IDictionary registeredProjections, Action> completedAction, long from = 0) { - _logger.Debug("PROJECTIONS: Reading Existing Projections from {stream}", ProjectionNamesBuilder.ProjectionsRegistrationStream); var corrId = Guid.NewGuid(); _readForwardDispatcher.Publish( @@ -729,48 +669,48 @@ private void ReadProjectionsList( user: SystemAccounts.System, replyOnExpired: false, expires: DateTime.MaxValue), - m => OnProjectionsListReadCompleted(m, registeredProjections, from, completedAction)); + m => OnProjectionsListReadCompleted(m, registeredProjections, completedAction)); } private void OnProjectionsListReadCompleted( ClientMessage.ReadStreamEventsForwardCompleted msg, IDictionary registeredProjections, - long requestedFrom, Action> completedAction) { switch (msg.Result) { case ReadStreamResult.Success: foreach (var evnt in msg.Events) { var projectionId = evnt.Event.EventNumber; if (projectionId == 0) - projectionId = Int32.MaxValue - 1; + projectionId = int.MaxValue - 1; if (evnt.Event.EventType == ProjectionEventTypes.ProjectionsInitialized) { registeredProjections.Add(ProjectionEventTypes.ProjectionsInitialized, projectionId); continue; } - var projectionName = Helper.UTF8NoBom.GetString(evnt.Event.Data.Span); - if (string.IsNullOrEmpty(projectionName) - || _projections.ContainsKey(projectionName)) { + var projectionName = evnt.Event.Data.FromUtf8(); + if (string.IsNullOrEmpty(projectionName) || _projections.ContainsKey(projectionName)) { _logger.Warning( "PROJECTIONS: The following projection: {projection} has a duplicate registration event.", projectionName); continue; } - if (evnt.Event.EventType == ProjectionEventTypes.ProjectionCreated) { - if (registeredProjections.ContainsKey(projectionName)) { + switch (evnt.Event.EventType) { + case ProjectionEventTypes.ProjectionCreated when registeredProjections.ContainsKey(projectionName): registeredProjections[projectionName] = projectionId; _logger.Warning( "PROJECTIONS: The following projection: {projection} has a duplicate created event. Using projection Id {projectionId}", projectionName, projectionId); continue; - } - - registeredProjections.Add(projectionName, projectionId); - } else if (evnt.Event.EventType == ProjectionEventTypes.ProjectionDeleted) { - registeredProjections.Remove(projectionName); + case ProjectionEventTypes.ProjectionCreated: + registeredProjections.Add(projectionName, projectionId); + break; + case ProjectionEventTypes.ProjectionDeleted: + registeredProjections.Remove(projectionName); + break; } } + _projectionsRegistrationExpectedVersion = msg.LastEventNumber; if (!msg.IsEndOfStream) { @@ -787,11 +727,11 @@ private void OnProjectionsListReadCompleted( msg.Result); return; } + completedAction(registeredProjections); } - private void StartRegisteredProjections(IDictionary registeredProjections, - Action completedAction) { + private void StartRegisteredProjections(IDictionary registeredProjections, Action completedAction) { if (!registeredProjections.Any()) { _logger.Debug("PROJECTIONS: No projections were found in {stream}, starting from empty stream", ProjectionNamesBuilder.ProjectionsRegistrationStream); @@ -820,7 +760,7 @@ private void StartRegisteredProjections(IDictionary registeredProj CreateSystemProjections(registeredProjections.Select(x => x.Key).ToList()); foreach (var projectionRegistration in registeredProjections.Where(x => - x.Key != ProjectionEventTypes.ProjectionsInitialized)) { + x.Key != ProjectionEventTypes.ProjectionsInitialized)) { int queueIndex = GetNextWorkerIndex(); var managedProjection = CreateManagedProjectionInstance( projectionRegistration.Key, @@ -835,7 +775,7 @@ private void StartRegisteredProjections(IDictionary registeredProj private bool IsProjectionEnabledToRunByMode(string projectionName) { return _runProjections >= ProjectionType.All - || _runProjections == ProjectionType.System && projectionName.StartsWith("$"); + || _runProjections == ProjectionType.System && projectionName.StartsWith('$'); } private void WriteProjectionsInitialized(Action action, Guid registrationEventId) { @@ -853,7 +793,8 @@ private void WriteProjectionsInitialized(Action action, Guid registrationEventId } private void WriteProjectionsInitializedCompleted(ClientMessage.WriteEventsCompleted completed, - Guid registrationEventId, Action action) { + Guid registrationEventId, + Action action) { switch (completed.Result) { case OperationResult.Success: action(); @@ -870,64 +811,61 @@ private void WriteProjectionsInitializedCompleted(ClientMessage.WriteEventsCompl } private void CreateSystemProjections() { - CreateSystemProjections(new List()); + CreateSystemProjections([]); } private void CreateSystemProjections(List existingSystemProjections) { - var systemProjections = new List(); + var systemProjections = new List(); if (!_initializeSystemProjections) { return; } - if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections - .StreamsStandardProjection)) + if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection)) systemProjections.Add(CreateSystemProjectionPost( ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection, typeof(IndexStreams), "")); - if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections - .StreamByCategoryStandardProjection)) + if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection)) systemProjections.Add(CreateSystemProjectionPost( ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection, typeof(CategorizeStreamByPath), "first\r\n-")); - if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections - .EventByCategoryStandardProjection)) + if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection)) systemProjections.Add(CreateSystemProjectionPost( ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection, typeof(CategorizeEventsByStreamPath), "first\r\n-")); - if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections - .EventByTypeStandardProjection)) + if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection)) systemProjections.Add(CreateSystemProjectionPost( ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection, typeof(IndexEventsByEventType), "")); if (!existingSystemProjections.Contains(ProjectionNamesBuilder.StandardProjections - .EventByCorrIdStandardProjection)) + .EventByCorrIdStandardProjection)) systemProjections.Add(CreateSystemProjectionPost( ProjectionNamesBuilder.StandardProjections.EventByCorrIdStandardProjection, typeof(ByCorrelationId), "{\"correlationIdProperty\":\"$correlationId\"}")); IEnvelope envelope = new NoopEnvelope(); - var postBatchMessage = new ProjectionManagementMessage.Command.PostBatch( - envelope, ProjectionManagementMessage.RunAs.System, systemProjections.ToArray()); + var postBatchMessage = new Command.PostBatch( + envelope, RunAs.System, systemProjections.ToArray()); _publisher.Publish(postBatchMessage); } - private ProjectionManagementMessage.Command.PostBatch.ProjectionPost CreateSystemProjectionPost - (string name, Type handlerType, string config) { - return new ProjectionManagementMessage.Command.PostBatch.ProjectionPost( + private static Command.PostBatch.ProjectionPost CreateSystemProjectionPost(string name, + Type handlerType, + string config) { + return new Command.PostBatch.ProjectionPost( ProjectionMode.Continuous, - ProjectionManagementMessage.RunAs.System, + RunAs.System, name, - "native:" + handlerType.Namespace + "." + handlerType.Name, + $"native:{handlerType.Namespace}.{handlerType.Name}", config, enabled: false, checkpointsEnabled: true, @@ -938,13 +876,10 @@ private ProjectionManagementMessage.Command.PostBatch.ProjectionPost CreateSyste private void PostNewTransientProjection(PendingProjection projection, IEnvelope replyEnvelope) { var initializer = projection.CreateInitializer(replyEnvelope); - ReadProjectionPossibleStream(projection.Name, - m => ReadProjectionPossibleStreamCompleted - (m, initializer, replyEnvelope)); + ReadProjectionPossibleStream(projection.Name, m => ReadProjectionPossibleStreamCompleted(m, initializer, replyEnvelope)); } - private void PostNewProjections - (IDictionary newProjections, long expectedVersion, IEnvelope replyEnvelope) { + private void PostNewProjections(IDictionary newProjections, long expectedVersion, IEnvelope replyEnvelope) { var corrId = Guid.NewGuid(); var events = new List(); @@ -961,7 +896,7 @@ private void PostNewProjections } } - if (!events.Any()) + if (events.Count == 0) return; var writeEvents = ClientMessage.WriteEvents.ForSingleStream(corrId, @@ -976,16 +911,14 @@ private void PostNewProjections _isWritePending = true; _writeDispatcher.Publish( writeEvents, - m => WriteNewProjectionsCompleted - (m, writeEvents, newProjections, replyEnvelope)); + m => WriteNewProjectionsCompleted(m, writeEvents, newProjections, replyEnvelope)); } - private void WriteNewProjectionsCompleted(ClientMessage.WriteEventsCompleted completed, ClientMessage.WriteEvents write, IDictionary newProjections, - IEnvelope envelope, int retryCount = ProjectionCreationRetryCount) { - + IEnvelope envelope, + int retryCount = ProjectionCreationRetryCount) { _isWritePending = false; if (completed.Result == OperationResult.Success) { foreach (var name in newProjections.Keys) @@ -1002,34 +935,27 @@ private void WriteNewProjectionsCompleted(ClientMessage.WriteEventsCompleted com newProjections.Keys, Enum.GetName(typeof(OperationResult), completed.Result)); - if (completed.Result == OperationResult.ForwardTimeout || - completed.Result == OperationResult.PrepareTimeout || - completed.Result == OperationResult.CommitTimeout) { + if (completed.Result is OperationResult.ForwardTimeout or OperationResult.PrepareTimeout or OperationResult.CommitTimeout) { if (retryCount > 0) { - _logger.Information("PROJECTIONS: Retrying write projection creations for {projections}", - newProjections.Keys); + _logger.Information("PROJECTIONS: Retrying write projection creations for {projections}", newProjections.Keys); _isWritePending = true; _writeDispatcher.Publish( write, - m => WriteNewProjectionsCompleted - (m, write, newProjections, envelope, retryCount - 1)); + m => WriteNewProjectionsCompleted(m, write, newProjections, envelope, retryCount - 1)); return; } } - envelope.ReplyWith(new ProjectionManagementMessage.OperationFailed( - string.Format( - "The projections '{0}' could not be created because the registration could not be written due to {1}", - string.Join(", ", newProjections.Keys), completed.Result))); + envelope.ReplyWith(new OperationFailed( + $"The projections '{string.Join(", ", newProjections.Keys)}' could not be created because the registration could not be written due to {completed.Result}")); } - private void StartNewlyRegisteredProjections - (IDictionary newProjections, - Action completedAction, IEnvelope replyEnvelope) { - + private void StartNewlyRegisteredProjections( + IDictionary newProjections, + Action completedAction, + IEnvelope replyEnvelope) { if (!newProjections.Any()) { - replyEnvelope.ReplyWith( - new ProjectionManagementMessage.OperationFailed("Projections were invalid")); + replyEnvelope.ReplyWith(new OperationFailed("Projections were invalid")); return; } @@ -1042,11 +968,10 @@ private void StartNewlyRegisteredProjections foreach (var projection in newProjections.Values) { var initializer = projection.CreateInitializer(replyEnvelope); ReadProjectionPossibleStream(projection.Name, - m => ReadProjectionPossibleStreamCompleted - (m, initializer, replyEnvelope)); + m => ReadProjectionPossibleStreamCompleted(m, initializer, replyEnvelope)); } } catch (Exception ex) { - replyEnvelope.ReplyWith(new ProjectionManagementMessage.OperationFailed(ex.Message)); + replyEnvelope.ReplyWith(new OperationFailed(ex.Message)); } completedAction(); @@ -1077,7 +1002,6 @@ private void ReadProjectionPossibleStreamCompleted( ClientMessage.ReadStreamEventsBackwardCompleted completed, NewProjectionInitializer initializer, IEnvelope replyEnvelope) { - long version = -1; if (completed.Result == ReadStreamResult.Success) { version = completed.LastEventNumber + 1; @@ -1085,16 +1009,16 @@ private void ReadProjectionPossibleStreamCompleted( try { int queueIndex = GetNextWorkerIndex(); - initializer. - CreateAndInitializeNewProjection(this, Guid.NewGuid(), _workers[queueIndex], + initializer.CreateAndInitializeNewProjection(this, Guid.NewGuid(), _workers[queueIndex], version: version); } catch (Exception ex) { - replyEnvelope.ReplyWith(new ProjectionManagementMessage.OperationFailed(ex.Message)); + replyEnvelope.ReplyWith(new OperationFailed(ex.Message)); } } private void OnProjectionsRegistrationCaughtUp() { - _logger.Debug($"PROJECTIONS: Caught up with projections registration. Next expected version: {_projectionsRegistrationExpectedVersion}"); + _logger.Debug( + $"PROJECTIONS: Caught up with projections registration. Next expected version: {_projectionsRegistrationExpectedVersion}"); } private void WriteProjectionDeletedCompleted(ClientMessage.WriteEventsCompleted writeCompleted, @@ -1102,7 +1026,6 @@ private void WriteProjectionDeletedCompleted(ClientMessage.WriteEventsCompleted ProjectionManagementMessage.Internal.Deleted message, Action onCompleted, int retryCount) { - _isWritePending = false; if (writeCompleted.Result == OperationResult.Success) { onCompleted?.Invoke(writeCompleted.LastEventNumbers.Single); @@ -1115,22 +1038,25 @@ private void WriteProjectionDeletedCompleted(ClientMessage.WriteEventsCompleted ProjectionNamesBuilder.ProjectionsRegistrationStream, Enum.GetName(typeof(OperationResult), writeCompleted.Result)); - if (writeCompleted.Result == OperationResult.CommitTimeout || writeCompleted.Result == OperationResult.ForwardTimeout - || writeCompleted.Result == OperationResult.PrepareTimeout) { - if (retryCount > 0) { - _logger.Information("PROJECTIONS: Retrying write projection deletion for {projection}", message.Name); - _isWritePending = true; - _writeDispatcher.Publish( - writeDelete, - m => WriteProjectionDeletedCompleted(m, writeDelete, message, onCompleted, retryCount - 1)); - return; - } - } + switch (writeCompleted.Result) { + case OperationResult.CommitTimeout: + case OperationResult.ForwardTimeout: + case OperationResult.PrepareTimeout: { + if (retryCount > 0) { + _logger.Information("PROJECTIONS: Retrying write projection deletion for {projection}", message.Name); + _isWritePending = true; + _writeDispatcher.Publish( + writeDelete, + m => WriteProjectionDeletedCompleted(m, writeDelete, message, onCompleted, retryCount - 1)); + return; + } - if (writeCompleted.Result == OperationResult.WrongExpectedVersion) { - _logger.Error("PROJECTIONS: Got wrong expected version writing projection deletion for {projection}.", - message.Name); - return; + break; + } + case OperationResult.WrongExpectedVersion: + _logger.Error("PROJECTIONS: Got wrong expected version writing projection deletion for {projection}.", + message.Name); + return; } _logger.Error( @@ -1152,7 +1078,7 @@ public class NewProjectionInitializer { private readonly bool _checkpointsEnabled; private readonly bool _trackEmittedStreams; private readonly bool _enableRunAs; - private readonly ProjectionManagementMessage.RunAs _runAs; + private readonly RunAs _runAs; private readonly IEnvelope _replyEnvelope; private readonly string _name; @@ -1167,7 +1093,7 @@ public NewProjectionInitializer( bool checkpointsEnabled, bool enableRunAs, bool trackEmittedStreams, - ProjectionManagementMessage.RunAs runAs, + RunAs runAs, IEnvelope replyEnvelope) { if (projectionMode >= ProjectionMode.Continuous && !checkpointsEnabled) throw new InvalidOperationException("Continuous mode requires checkpoints"); @@ -1225,9 +1151,8 @@ private ManagedProjection CreateManagedProjectionInstance( string name, long projectionId, Guid projectionCorrelationId, - Guid workerID) { + Guid workerId) { var enabledToRun = IsProjectionEnabledToRunByMode(name); - var workerId = workerID; var managedProjectionInstance = new ManagedProjection( workerId, projectionCorrelationId, @@ -1247,8 +1172,7 @@ private ManagedProjection CreateManagedProjectionInstance( _projectionsMap.Add(projectionCorrelationId, name); _projections.Add(name, managedProjectionInstance); - _logger.Debug("Adding projection {projectionCorrelationId}@{projection} to list", projectionCorrelationId, - name); + _logger.Debug("Adding projection {projectionCorrelationId}@{projection} to list", projectionCorrelationId, name); return managedProjectionInstance; } @@ -1260,45 +1184,41 @@ private int GetNextWorkerIndex() { return queueIndex; } - public class PendingProjection { - public ProjectionMode Mode { get; } - public SerializedRunAs RunAs { get; } - public string Name { get; } - public string HandlerType { get; } - public string Query { get; } - public bool Enabled { get; } - public bool CheckpointsEnabled { get; } - public bool EmitEnabled { get; } - public bool EnableRunAs { get; } - public bool TrackEmittedStreams { get; } - public long ProjectionId { get; } - - public PendingProjection( - long projectionId, ProjectionMode mode, SerializedRunAs runAs, string name, string handlerType, string query, - bool enabled, bool checkpointsEnabled, bool emitEnabled, bool enableRunAs, - bool trackEmittedStreams) { - ProjectionId = projectionId; - Mode = mode; - RunAs = runAs; - Name = name; - HandlerType = handlerType; - Query = query; - Enabled = enabled; - CheckpointsEnabled = checkpointsEnabled; - EmitEnabled = emitEnabled; - EnableRunAs = enableRunAs; - TrackEmittedStreams = trackEmittedStreams; - } - - public PendingProjection(long projectionId, ProjectionManagementMessage.Command.PostBatch.ProjectionPost projection) + public class PendingProjection( + long projectionId, + ProjectionMode mode, + SerializedRunAs runAs, + string name, + string handlerType, + string query, + bool enabled, + bool checkpointsEnabled, + bool emitEnabled, + bool enableRunAs, + bool trackEmittedStreams) { + public ProjectionMode Mode { get; } = mode; + public SerializedRunAs RunAs { get; } = runAs; + public string Name { get; } = name; + public string HandlerType { get; } = handlerType; + public string Query { get; } = query; + public bool Enabled { get; } = enabled; + public bool CheckpointsEnabled { get; } = checkpointsEnabled; + public bool EmitEnabled { get; } = emitEnabled; + public bool EnableRunAs { get; } = enableRunAs; + public bool TrackEmittedStreams { get; } = trackEmittedStreams; + public long ProjectionId { get; } = projectionId; + + public PendingProjection(long projectionId, Command.PostBatch.ProjectionPost projection) : this(projectionId, projection.Mode, projection.RunAs, projection.Name, projection.HandlerType, projection.Query, projection.Enabled, projection.CheckpointsEnabled, - projection.EmitEnabled, projection.EnableRunAs, projection.TrackEmittedStreams) { } + projection.EmitEnabled, projection.EnableRunAs, projection.TrackEmittedStreams) { + } - public PendingProjection(long projectionId, ProjectionManagementMessage.Command.Post projection) + public PendingProjection(long projectionId, Command.Post projection) : this(projectionId, projection.Mode, projection.RunAs, projection.Name, projection.HandlerType, projection.Query, projection.Enabled, projection.CheckpointsEnabled, - projection.EmitEnabled, projection.EnableRunAs, projection.TrackEmittedStreams) { } + projection.EmitEnabled, projection.EnableRunAs, projection.TrackEmittedStreams) { + } public NewProjectionInitializer CreateInitializer(IEnvelope replyEnvelope) { return new NewProjectionInitializer( @@ -1329,7 +1249,7 @@ public void Handle(TelemetryMessage.Request message) { var stats = proj.GetStatistics(); - if (stats.Name.StartsWith("$")) { + if (stats.Name.StartsWith('$')) { standardProjectionCount += 1; if (stats.Status.Contains("Running")) diff --git a/src/KurrentDB.Projections.Core/Services/Management/ProjectionManagerMessageDispatcher.cs b/src/KurrentDB.Projections.Core/Services/Management/ProjectionManagerMessageDispatcher.cs index 3d1b4c76ff4..57e0a09e0ac 100644 --- a/src/KurrentDB.Projections.Core/Services/Management/ProjectionManagerMessageDispatcher.cs +++ b/src/KurrentDB.Projections.Core/Services/Management/ProjectionManagerMessageDispatcher.cs @@ -11,22 +11,16 @@ namespace KurrentDB.Projections.Core.Services.Management; -public class ProjectionManagerMessageDispatcher +public class ProjectionManagerMessageDispatcher(IDictionary queueMap) : IHandle { private readonly ILogger _logger = Log.ForContext(); - private readonly IDictionary _queueMap; - - public ProjectionManagerMessageDispatcher(IDictionary queueMap) { - _queueMap = queueMap; - } public void Handle(CoreProjectionManagementControlMessage message) { DispatchWorkerMessage(message, message.WorkerId); } private void DispatchWorkerMessage(Message message, Guid workerId) { - IPublisher worker; - if (_queueMap.TryGetValue(workerId, out worker)) + if (queueMap.TryGetValue(workerId, out var worker)) worker.Publish(message); else _logger.Information("Cannot find a worker with ID: {workerId}", workerId); diff --git a/src/KurrentDB.Projections.Core/Services/Management/ProjectionStateHandlerFactory.cs b/src/KurrentDB.Projections.Core/Services/Management/ProjectionStateHandlerFactory.cs index 456d240ad43..ad6c5633016 100644 --- a/src/KurrentDB.Projections.Core/Services/Management/ProjectionStateHandlerFactory.cs +++ b/src/KurrentDB.Projections.Core/Services/Management/ProjectionStateHandlerFactory.cs @@ -28,11 +28,11 @@ public IProjectionStateHandler Create( int? projectionExecutionTimeout, Action logger = null) { var colonPos = factoryType.IndexOf(':'); - string kind = null; + string kind; string rest = null; if (colonPos > 0) { - kind = factoryType.Substring(0, colonPos); - rest = factoryType.Substring(colonPos + 1); + kind = factoryType[..colonPos]; + rest = factoryType[(colonPos + 1)..]; } else { kind = factoryType; } @@ -52,13 +52,9 @@ public IProjectionStateHandler Create( // Allow loading native projections from previous versions rest = rest?.Replace("EventStore", "KurrentDB"); - var type = Type.GetType(rest); - if (type == null) { - type = - AppDomain.CurrentDomain.GetAssemblies() - .Select(v => v.GetType(rest)) - .FirstOrDefault(v => v != null); - } + var type = Type.GetType(rest) ?? AppDomain.CurrentDomain.GetAssemblies() + .Select(v => v.GetType(rest)) + .FirstOrDefault(v => v != null); if (type is null) { throw new NotSupportedException($"Could not find type \"{rest}\""); @@ -68,7 +64,7 @@ public IProjectionStateHandler Create( result = (IProjectionStateHandler)handler; break; default: - throw new NotSupportedException(string.Format("'{0}' handler type is not supported", factoryType)); + throw new NotSupportedException($"'{factoryType}' handler type is not supported"); } return result; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTag.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTag.cs index 755671c8795..3ffad4f7cab 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTag.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTag.cs @@ -15,7 +15,6 @@ namespace KurrentDB.Projections.Core.Services.Processing.Checkpointing; public class CheckpointTag : IComparable { public readonly int Phase; - public readonly TFPos Position; //TODO: rename to StreamsOrEventTypes or just Positions @@ -66,13 +65,13 @@ private CheckpointTag(int phase, IDictionary streams) { Phase = phase; foreach (var stream in streams) { if (stream.Key == "") - throw new ArgumentException("Empty stream name", "streams"); + throw new ArgumentException("Empty stream name", nameof(streams)); if (stream.Value < 0 && stream.Value != ExpectedVersion.NoStream) - throw new ArgumentException("Invalid sequence number", "streams"); + throw new ArgumentException("Invalid sequence number", nameof(streams)); } - Streams = new Dictionary(streams); // clone - Position = new TFPos(Int64.MinValue, Int64.MinValue); + Streams = new(streams); // clone + Position = new(long.MinValue, long.MinValue); Mode_ = CalculateMode(); } @@ -81,55 +80,55 @@ private CheckpointTag(int phase, IDictionary eventTypes, TFPos pos Position = position; foreach (var stream in eventTypes) { if (stream.Key == "") - throw new ArgumentException("Empty stream name", "eventTypes"); + throw new ArgumentException("Empty stream name", nameof(eventTypes)); if (stream.Value < 0 && stream.Value != ExpectedVersion.NoStream) - throw new ArgumentException("Invalid sequence number", "eventTypes"); + throw new ArgumentException("Invalid sequence number", nameof(eventTypes)); } - Streams = new Dictionary(eventTypes); // clone + Streams = new(eventTypes); // clone Mode_ = CalculateMode(); } private CheckpointTag(int phase, string stream, long sequenceNumber) { Phase = phase; - if (stream == null) - throw new ArgumentNullException("stream"); - if (stream == "") - throw new ArgumentException("stream"); + ArgumentException.ThrowIfNullOrEmpty(stream); if (sequenceNumber < 0 && sequenceNumber != ExpectedVersion.NoStream) - throw new ArgumentException("sequenceNumber"); - Position = new TFPos(Int64.MinValue, Int64.MinValue); - Streams = new Dictionary { { stream, sequenceNumber } }; + throw new ArgumentException(null, nameof(sequenceNumber)); + Position = new(long.MinValue, long.MinValue); + Streams = new() { { stream, sequenceNumber } }; Mode_ = CalculateMode(); } - private CheckpointTag( - int phase, string catalogStream, long catalogPosition, string dataStream, long dataPosition, + private CheckpointTag(int phase, + string catalogStream, + long catalogPosition, + string dataStream, + long dataPosition, long commitPosition) { Phase = phase; CatalogStream = catalogStream; CatalogPosition = catalogPosition; DataStream = dataStream; DataPosition = dataPosition; - Position = new TFPos(commitPosition, Int64.MinValue); + Position = new TFPos(commitPosition, long.MinValue); Mode_ = Mode.ByStream; } private Mode CalculateMode() { if (Streams == null || Streams.Count == 0) - if (Position.CommitPosition == Int64.MinValue && Position.PreparePosition == Int64.MinValue) - return Mode.Phase; - else if (Position.CommitPosition == Int64.MaxValue && Position.PreparePosition == Int64.MaxValue) - return Mode.Phase; - else if (Position.CommitPosition == Int64.MinValue && Position.PreparePosition != Int64.MinValue) - return Mode.PreparePosition; - else - return Mode.Position; - if (Position != new TFPos(Int64.MinValue, Int64.MinValue)) + switch (Position.CommitPosition) { + case long.MinValue when Position.PreparePosition == long.MinValue: + case long.MaxValue when Position.PreparePosition == long.MaxValue: + return Mode.Phase; + case long.MinValue when Position.PreparePosition != long.MinValue: + return Mode.PreparePosition; + default: + return Mode.Position; + } + + if (Position != new TFPos(long.MinValue, long.MinValue)) return Mode.EventTypeIndex; - if (Streams.Count == 1) - return Mode.Stream; - return Mode.MultiStream; + return Streams.Count == 1 ? Mode.Stream : Mode.MultiStream; } public static bool operator >(CheckpointTag left, CheckpointTag right) { @@ -152,9 +151,8 @@ private Mode CalculateMode() { case Mode.ByStream: CheckCatalogCompatibility(left, right); return left.CatalogPosition > right.CatalogPosition - || (left.CatalogPosition == right.CatalogPosition && left.DataPosition > right.DataPosition); + || (left.CatalogPosition == right.CatalogPosition && left.DataPosition > right.DataPosition); case Mode.Phase: - return left.Position > right.Position; case Mode.Position: case Mode.EventTypeIndex: return left.Position > right.Position; @@ -166,13 +164,8 @@ private Mode CalculateMode() { var result = left.Streams.Values.First() > right.Streams.Values.First(); return result; case Mode.MultiStream: - long rvalue; - bool anyLeftGreater = left.Streams.Any(l => - !right.Streams.TryGetValue(l.Key, out rvalue) || l.Value > rvalue); - - long lvalue; - bool anyRightGreater = right.Streams.Any(r => - !left.Streams.TryGetValue(r.Key, out lvalue) || r.Value > lvalue); + bool anyLeftGreater = left.Streams.Any(l => !right.Streams.TryGetValue(l.Key, out var rvalue) || l.Value > rvalue); + bool anyRightGreater = right.Streams.Any(r => !left.Streams.TryGetValue(r.Key, out var lvalue) || r.Value > lvalue); if (anyLeftGreater && anyRightGreater) ThrowIncomparable(left, right); @@ -188,8 +181,7 @@ private static void CheckCatalogCompatibility(CheckpointTag left, CheckpointTag } private static void ThrowIncomparable(CheckpointTag left, CheckpointTag right) { - throw new InvalidOperationException( - string.Format("Incomparable multi-stream checkpoint tags. '{0}' and '{1}'", left, right)); + throw new InvalidOperationException($"Incomparable multi-stream checkpoint tags. '{left}' and '{right}'"); } public static bool operator >=(CheckpointTag left, CheckpointTag right) { @@ -212,10 +204,9 @@ private static void ThrowIncomparable(CheckpointTag left, CheckpointTag right) { case Mode.ByStream: CheckCatalogCompatibility(left, right); return left.CatalogPosition > right.CatalogPosition - || (left.CatalogPosition == right.CatalogPosition && - left.DataPosition >= right.DataPosition); + || (left.CatalogPosition == right.CatalogPosition && + left.DataPosition >= right.DataPosition); case Mode.Phase: - return left.Position >= right.Position; case Mode.Position: case Mode.EventTypeIndex: return left.Position >= right.Position; @@ -227,13 +218,8 @@ private static void ThrowIncomparable(CheckpointTag left, CheckpointTag right) { var result = left.Streams.Values.First() >= right.Streams.Values.First(); return result; case Mode.MultiStream: - long rvalue; - bool anyLeftGreater = left.Streams.Any(l => - !right.Streams.TryGetValue(l.Key, out rvalue) || l.Value > rvalue); - - long lvalue; - bool anyRightGreater = right.Streams.Any(r => - !left.Streams.TryGetValue(r.Key, out lvalue) || r.Value > lvalue); + bool anyLeftGreater = left.Streams.Any(l => !right.Streams.TryGetValue(l.Key, out var rvalue) || l.Value > rvalue); + bool anyRightGreater = right.Streams.Any(r => !left.Streams.TryGetValue(r.Key, out var lvalue) || r.Value > lvalue); if (anyLeftGreater && anyRightGreater) ThrowIncomparable(left, right); @@ -243,21 +229,13 @@ private static void ThrowIncomparable(CheckpointTag left, CheckpointTag right) { } } - public static bool operator <(CheckpointTag left, CheckpointTag right) { - return !(left >= right); - } + public static bool operator <(CheckpointTag left, CheckpointTag right) => !(left >= right); - public static bool operator <=(CheckpointTag left, CheckpointTag right) { - return !(left > right); - } + public static bool operator <=(CheckpointTag left, CheckpointTag right) => !(left > right); - public static bool operator ==(CheckpointTag left, CheckpointTag right) { - return Equals(left, right); - } + public static bool operator ==(CheckpointTag left, CheckpointTag right) => Equals(left, right); - public static bool operator !=(CheckpointTag left, CheckpointTag right) { - return !(left == right); - } + public static bool operator !=(CheckpointTag left, CheckpointTag right) => !(left == right); protected bool Equals(CheckpointTag other) { if (Phase != other.Phase) @@ -270,9 +248,9 @@ protected bool Equals(CheckpointTag other) { switch (leftMode) { case Mode.ByStream: return CatalogStream == other.CatalogStream && CatalogPosition == other.CatalogPosition - && DataStream == other.DataStream && - DataPosition == other.DataPosition - && CommitPosition == other.CommitPosition; + && DataStream == other.DataStream && + DataPosition == other.DataPosition + && CommitPosition == other.CommitPosition; case Mode.Phase: return Position == other.Position; case Mode.EventTypeIndex: @@ -289,9 +267,8 @@ protected bool Equals(CheckpointTag other) { var result = Streams.Values.First() == other.Streams.Values.First(); return result; case Mode.MultiStream: - long rvalue = 0; return Streams.Count == other.Streams.Count - && Streams.All(l => other.Streams.TryGetValue(l.Key, out rvalue) && l.Value == rvalue); + && Streams.All(l => other.Streams.TryGetValue(l.Key, out var rvalue) && l.Value == rvalue); default: throw new NotSupportedException("Checkpoint tag mode is not supported in comparison"); } @@ -315,84 +292,54 @@ public override int GetHashCode() { public long? CommitPosition { get { var commitPosition = Position.CommitPosition; - switch (Mode_) { - case Mode.ByStream: - return commitPosition == long.MinValue ? (long?)null : commitPosition; - case Mode.Position: - case Mode.EventTypeIndex: - return commitPosition; - default: - return null; - } + return Mode_ switch { + Mode.ByStream => commitPosition == long.MinValue ? null : commitPosition, + Mode.Position or Mode.EventTypeIndex => commitPosition, + _ => null + }; } } - public long? PreparePosition { - get { - switch (Mode_) { - case Mode.Position: - case Mode.PreparePosition: - case Mode.EventTypeIndex: - return Position.PreparePosition; - default: - return null; - } - } - } + public long? PreparePosition => Mode_ switch { + Mode.Position or Mode.PreparePosition or Mode.EventTypeIndex => Position.PreparePosition, + _ => null + }; - public static CheckpointTag Empty { - get { return _empty; } - } + public static CheckpointTag Empty { get; } = new(-1, false); internal readonly Mode Mode_; - private static readonly CheckpointTag _empty = new CheckpointTag(-1, false); - public static CheckpointTag FromPhase(int phase, bool completed) { - return new CheckpointTag(phase, completed); - } + public static CheckpointTag FromPhase(int phase, bool completed) => new(phase, completed); - public static CheckpointTag FromPosition(int phase, long commitPosition, long preparePosition) { - return new CheckpointTag(phase, new TFPos(commitPosition, preparePosition)); - } + public static CheckpointTag FromPosition(int phase, long commitPosition, long preparePosition) + => new(phase, new TFPos(commitPosition, preparePosition)); - public static CheckpointTag FromPosition(int phase, TFPos position) { - return new CheckpointTag(phase, position); - } + public static CheckpointTag FromPosition(int phase, TFPos position) => new(phase, position); - public static CheckpointTag FromPreparePosition(int phase, long preparePosition) { - return new CheckpointTag(phase, preparePosition); - } + public static CheckpointTag FromPreparePosition(int phase, long preparePosition) => new(phase, preparePosition); - public static CheckpointTag FromStreamPosition(int phase, string stream, long sequenceNumber) { - return new CheckpointTag(phase, stream, sequenceNumber); - } + public static CheckpointTag FromStreamPosition(int phase, string stream, long sequenceNumber) => new(phase, stream, sequenceNumber); - public static CheckpointTag FromStreamPositions(int phase, IDictionary streams) { - // streams cloned inside - return new CheckpointTag(phase, streams); - } + public static CheckpointTag FromStreamPositions(int phase, IDictionary streams) => new(phase, streams); - public static CheckpointTag FromEventTypeIndexPositions(int phase, TFPos position, - IDictionary streams) { - // streams cloned inside - return new CheckpointTag(phase, streams, position); - } + public static CheckpointTag FromEventTypeIndexPositions(int phase, TFPos position, IDictionary streams) + => new(phase, streams, position); - public static CheckpointTag FromByStreamPosition( - int phase, string catalogStream, long catalogPosition, string dataStream, long dataPosition, - long commitPosition) { - return new CheckpointTag(phase, catalogStream, catalogPosition, dataStream, dataPosition, commitPosition); - } + public static CheckpointTag FromByStreamPosition(int phase, + string catalogStream, + long catalogPosition, + string dataStream, + long dataPosition, + long commitPosition) + => new(phase, catalogStream, catalogPosition, dataStream, dataPosition, commitPosition); - public int CompareTo(CheckpointTag other) { - return this < other ? -1 : (this > other ? 1 : 0); - } + public int CompareTo(CheckpointTag other) => this < other ? -1 : this > other ? 1 : 0; public override string ToString() { string result; switch (Mode_) { case Mode.Phase: - return "Phase: " + Phase + (Completed ? " (completed)" : ""); + return $"Phase: {Phase}{(Completed ? " (completed)" : "")}"; case Mode.Position: result = Position.ToString(); break; @@ -400,7 +347,7 @@ public override string ToString() { result = PreparePosition.ToString(); break; case Mode.Stream: - result = Streams.Keys.First() + ": " + Streams.Values.First(); + result = $"{Streams.Keys.First()}: {Streams.Values.First()}"; break; case Mode.MultiStream: case Mode.EventTypeIndex: @@ -411,50 +358,37 @@ public override string ToString() { } foreach (var stream in Streams) { - sb.AppendFormat("{0}: {1}; ", stream.Key, stream.Value); + sb.Append($"{stream.Key}: {stream.Value}; "); } result = sb.ToString(); break; case Mode.ByStream: - result = string.Format( - "{0}:{1}/{2}:{3}/{4}", CatalogStream, CatalogPosition, DataStream, DataPosition, - CommitPosition); + result = $"{CatalogStream}:{CatalogPosition}/{DataStream}:{DataPosition}/{CommitPosition}"; break; default: - return "Unsupported mode: " + Mode_.ToString(); + return $"Unsupported mode: {Mode_}"; } - if (Phase == 0) - return result; - else { - return "(" + Phase + ") " + result; - } + return Phase == 0 ? result : $"({Phase}) {result}"; } - public bool Completed { - get { return Position.CommitPosition == Int64.MaxValue; } - } + public bool Completed => Position.CommitPosition == long.MaxValue; private static void UpgradeModes(ref Mode leftMode, ref Mode rightMode) { - if (leftMode == Mode.Stream && rightMode == Mode.MultiStream) { - leftMode = Mode.MultiStream; - return; - } - - if (leftMode == Mode.MultiStream && rightMode == Mode.Stream) { - rightMode = Mode.MultiStream; - return; - } - - if (leftMode == Mode.Position && rightMode == Mode.EventTypeIndex) { - leftMode = Mode.EventTypeIndex; - return; - } - - if (leftMode == Mode.EventTypeIndex && rightMode == Mode.Position) { - rightMode = Mode.EventTypeIndex; - return; + switch (leftMode) { + case Mode.Stream when rightMode == Mode.MultiStream: + leftMode = Mode.MultiStream; + return; + case Mode.MultiStream when rightMode == Mode.Stream: + rightMode = Mode.MultiStream; + return; + case Mode.Position when rightMode == Mode.EventTypeIndex: + leftMode = Mode.EventTypeIndex; + return; + case Mode.EventTypeIndex when rightMode == Mode.Position: + rightMode = Mode.EventTypeIndex; + return; } } @@ -473,9 +407,9 @@ public CheckpointTag UpdateEventTypeIndexPosition(TFPos position, string eventTy } public CheckpointTag UpdateEventTypeIndexPosition(TFPos position) { - if (Mode_ != Mode.EventTypeIndex) - throw new ArgumentException("Invalid tag mode", "tag"); - return FromEventTypeIndexPositions(Phase, position, Streams); + return Mode_ != Mode.EventTypeIndex + ? throw new ArgumentException("Invalid tag mode", "tag") + : FromEventTypeIndexPositions(Phase, position, Streams); } private Dictionary PatchStreamsDictionary(string streamId, long eventSequenceNumber) { @@ -484,17 +418,14 @@ private Dictionary PatchStreamsDictionary(string streamId, long ev foreach (var stream in Streams) { if (stream.Key == streamId) { was = true; - if (eventSequenceNumber < stream.Value) - resultDictionary.Add(stream.Key, stream.Value); - else - resultDictionary.Add(stream.Key, eventSequenceNumber); + resultDictionary.Add(stream.Key, eventSequenceNumber < stream.Value ? stream.Value : eventSequenceNumber); } else { resultDictionary.Add(stream.Key, stream.Value); } } if (!was) - throw new ArgumentException("Key not found: " + streamId, "streamId"); + throw new ArgumentException("Key not found: " + streamId, nameof(streamId)); if (resultDictionary.Count < Streams.Count) resultDictionary.Add(streamId, eventSequenceNumber); return resultDictionary; @@ -503,54 +434,51 @@ private Dictionary PatchStreamsDictionary(string streamId, long ev public byte[] ToJsonBytes(ProjectionVersion projectionVersion, IEnumerable> extraMetaData = null) { if (projectionVersion.ProjectionId == -1) - throw new ArgumentException("projectionId is required", "projectionVersion"); - - using (var memoryStream = new MemoryStream()) { - using (var textWriter = new StreamWriter(memoryStream, Helper.UTF8NoBom)) - using (var jsonWriter = new JsonTextWriter(textWriter)) { - WriteTo(projectionVersion, extraMetaData, jsonWriter); - } + throw new ArgumentException("projectionId is required", nameof(projectionVersion)); - return memoryStream.ToArray(); + using var memoryStream = new MemoryStream(); + using (var textWriter = new StreamWriter(memoryStream, Helper.UTF8NoBom)) + using (var jsonWriter = new JsonTextWriter(textWriter)) { + WriteTo(projectionVersion, extraMetaData, jsonWriter); } + + return memoryStream.ToArray(); } public string ToJsonString(ProjectionVersion projectionVersion, IEnumerable> extraMetaData = null) { if (projectionVersion.ProjectionId == -1) - throw new ArgumentException("projectionId is required", "projectionVersion"); + throw new ArgumentException("projectionId is required", nameof(projectionVersion)); - using (var textWriter = new StringWriter()) { - using (var jsonWriter = new JsonTextWriter(textWriter)) { - WriteTo(projectionVersion, extraMetaData, jsonWriter); - } - - return textWriter.ToString(); + using var textWriter = new StringWriter(); + using (var jsonWriter = new JsonTextWriter(textWriter)) { + WriteTo(projectionVersion, extraMetaData, jsonWriter); } + + return textWriter.ToString(); } public string ToJsonString(IEnumerable> extraMetaData = null) { - using (var textWriter = new StringWriter()) { - using (var jsonWriter = new JsonTextWriter(textWriter)) { - WriteTo(default(ProjectionVersion), extraMetaData, jsonWriter); - } - - return textWriter.ToString(); + using var textWriter = new StringWriter(); + using (var jsonWriter = new JsonTextWriter(textWriter)) { + WriteTo(default, extraMetaData, jsonWriter); } + + return textWriter.ToString(); } public JRaw ToJsonRaw(IEnumerable> extraMetaData = null) { - using (var textWriter = new StringWriter()) { - using (var jsonWriter = new JsonTextWriter(textWriter)) { - WriteTo(default(ProjectionVersion), extraMetaData, jsonWriter); - } - - return new JRaw(textWriter.ToString()); + using var textWriter = new StringWriter(); + using (var jsonWriter = new JsonTextWriter(textWriter)) { + WriteTo(default, extraMetaData, jsonWriter); } + + return new(textWriter.ToString()); } - public void WriteTo(ProjectionVersion projectionVersion, - IEnumerable> extraMetaData, JsonWriter jsonWriter) { + private void WriteTo(ProjectionVersion projectionVersion, + IEnumerable> extraMetaData, + JsonTextWriter jsonWriter) { jsonWriter.WriteStartObject(); if (projectionVersion.ProjectionId > 0) { jsonWriter.WritePropertyName("$v"); @@ -623,16 +551,14 @@ public void WriteTo(ProjectionVersion projectionVersion, jsonWriter.WriteEndObject(); } - private static void WriteVersion(ProjectionVersion projectionVersion, JsonWriter jsonWriter) { + private static void WriteVersion(ProjectionVersion projectionVersion, JsonTextWriter jsonWriter) { jsonWriter.WriteValue( - projectionVersion.ProjectionId + ":" + projectionVersion.Epoch + ":" + projectionVersion.Version + ":" - + ProjectionsSubsystem.VERSION); + $"{projectionVersion.ProjectionId}:{projectionVersion.Epoch}:{projectionVersion.Version}:{ProjectionsSubsystem.VERSION}"); } - public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion current, - bool skipStartObject = false) { + public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion current, bool skipStartObject = false) { if (!skipStartObject) - Check(reader.Read(), reader); + Check(reader.Read()); Check(JsonToken.StartObject, reader); long? commitPosition = null; long? preparePosition = null; @@ -649,21 +575,21 @@ public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion var projectionSystemVersion = 0; int projectionPhase = 0; while (true) { - Check(reader.Read(), reader); + Check(reader.Read()); if (reader.TokenType == JsonToken.EndObject) break; Check(JsonToken.PropertyName, reader); var name = (string)reader.Value; switch (name) { case "$cp": - Check(reader.Read(), reader); + Check(reader.Read()); var completed = (bool)reader.Value; - commitPosition = completed ? Int64.MaxValue : Int64.MinValue; - preparePosition = completed ? Int64.MaxValue : Int64.MinValue; + commitPosition = completed ? long.MaxValue : long.MinValue; + preparePosition = completed ? long.MaxValue : long.MinValue; break; case "$v": case "v": - Check(reader.Read(), reader); + Check(reader.Read()); if (reader.ValueType == typeof(long)) { var v = (int)(long)reader.Value; if (v > 0) // TODO: remove this if with time @@ -673,13 +599,13 @@ public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion var v = (string)reader.Value; string[] parts = v.Split(':'); if (parts.Length == 2) { - projectionVersion = Int32.Parse(parts[1]); + projectionVersion = int.Parse(parts[1]); } else { - projectionId = Int32.Parse(parts[0]); - projectionEpoch = Int32.Parse(parts[1]); - projectionVersion = Int32.Parse(parts[2]); + projectionId = int.Parse(parts[0]); + projectionEpoch = int.Parse(parts[1]); + projectionVersion = int.Parse(parts[2]); if (parts.Length >= 4) - projectionSystemVersion = Int32.Parse(parts[3]); + projectionSystemVersion = int.Parse(parts[3]); } } @@ -687,53 +613,53 @@ public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion case "$c": case "c": case "commitPosition": - Check(reader.Read(), reader); + Check(reader.Read()); commitPosition = (long)reader.Value; break; case "$p": case "p": case "preparePosition": - Check(reader.Read(), reader); + Check(reader.Read()); preparePosition = (long)reader.Value; break; case "$s": case "s": case "streams": - Check(reader.Read(), reader); + Check(reader.Read()); if (reader.TokenType == JsonToken.StartArray) { - Check(reader.Read(), reader); + Check(reader.Read()); Check(JsonToken.StartObject, reader); - Check(reader.Read(), reader); + Check(reader.Read()); Check(JsonToken.PropertyName, reader); catalogStream = (string)reader.Value; - Check(reader.Read(), reader); + Check(reader.Read()); catalogPosition = (long)reader.Value; - Check(reader.Read(), reader); + Check(reader.Read()); Check(JsonToken.EndObject, reader); - Check(reader.Read(), reader); + Check(reader.Read()); if (reader.TokenType == JsonToken.StartObject) { - Check(reader.Read(), reader); + Check(reader.Read()); Check(JsonToken.PropertyName, reader); dataStream = (string)reader.Value; - Check(reader.Read(), reader); + Check(reader.Read()); dataPosition = (long)reader.Value; - Check(reader.Read(), reader); + Check(reader.Read()); Check(JsonToken.EndObject, reader); - Check(reader.Read(), reader); + Check(reader.Read()); } Check(JsonToken.EndArray, reader); } else { Check(JsonToken.StartObject, reader); - streams = new Dictionary(); + streams = new(); while (true) { - Check(reader.Read(), reader); + Check(reader.Read()); if (reader.TokenType == JsonToken.EndObject) break; Check(JsonToken.PropertyName, reader); var streamName = (string)reader.Value; - Check(reader.Read(), reader); + Check(reader.Read()); long position = (long)reader.Value; streams.Add(streamName, position); } @@ -741,27 +667,26 @@ public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion break; case "$ph": - Check(reader.Read(), reader); + Check(reader.Read()); projectionPhase = (int)(long)reader.Value; break; case "$m": - Check(reader.Read(), reader); + Check(reader.Read()); var readMode = (string)reader.Value; if (readMode != "bs") throw new ApplicationException("Unknown checkpoint tag mode: " + readMode); byStreamMode = true; break; default: - if (extra == null) - extra = new Dictionary(); - Check(reader.Read(), reader); + extra ??= new(); + Check(reader.Read()); var jToken = JToken.ReadFrom(reader); extra.Add(name, jToken); break; } } - return new CheckpointTagVersion { + return new() { Tag = byStreamMode ? new CheckpointTag( @@ -769,19 +694,19 @@ public static CheckpointTagVersion FromJson(JsonReader reader, ProjectionVersion dataPosition ?? -1, commitPosition.GetValueOrDefault()) : new CheckpointTag( projectionPhase, - new TFPos(commitPosition ?? Int64.MinValue, preparePosition ?? Int64.MinValue), streams), - Version = new ProjectionVersion(projectionId, projectionEpoch, projectionVersion), + new TFPos(commitPosition ?? long.MinValue, preparePosition ?? long.MinValue), streams), + Version = new(projectionId, projectionEpoch, projectionVersion), SystemVersion = projectionSystemVersion, ExtraMetadata = extra, }; } - public static void Check(JsonToken type, JsonReader reader) { + private static void Check(JsonToken type, JsonReader reader) { if (reader.TokenType != type) throw new Exception("Invalid JSON"); } - public static void Check(bool read, JsonReader reader) { + public static void Check(bool read) { if (!read) throw new Exception("Invalid JSON"); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagExtensions.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagExtensions.cs index 81f7657e366..f81cbac9085 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagExtensions.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagExtensions.cs @@ -14,43 +14,34 @@ public static CheckpointTag ParseCheckpointTagJson(this string source) { if (string.IsNullOrEmpty(source)) return null; var reader = new JsonTextReader(new StringReader(source)); - return CheckpointTag.FromJson(reader, default(ProjectionVersion)).Tag; + return CheckpointTag.FromJson(reader, default).Tag; } public static CheckpointTag ParseCheckpointTagJson(this byte[] source) { if (source == null || source.Length == 0) return null; var reader = new JsonTextReader(new StreamReader(new MemoryStream(source))); - return CheckpointTag.FromJson(reader, default(ProjectionVersion)).Tag; + return CheckpointTag.FromJson(reader, default).Tag; } public static CheckpointTag ParseCheckpointTagJson(this ReadOnlyMemory source) { - if (source.Length == 0) - return null; - var reader = new JsonTextReader(new StreamReader(new MemoryStream(source.ToArray()))); - return CheckpointTag.FromJson(reader, default(ProjectionVersion)).Tag; + return ParseCheckpointTagJson(source.ToArray()); } - public static CheckpointTagVersion ParseCheckpointTagVersionExtraJson(this byte[] source, - ProjectionVersion current) { + public static CheckpointTagVersion ParseCheckpointTagVersionExtraJson(this byte[] source, ProjectionVersion current) { if (source == null || source.Length == 0) - return new CheckpointTagVersion { Version = new ProjectionVersion(current.ProjectionId, 0, 0), Tag = null }; + return new() { Version = new(current.ProjectionId, 0, 0), Tag = null }; var reader = new JsonTextReader(new StreamReader(new MemoryStream(source))); return CheckpointTag.FromJson(reader, current); } - public static CheckpointTagVersion ParseCheckpointTagVersionExtraJson(this ReadOnlyMemory source, - ProjectionVersion current) { - if (source.Length == 0) - return new CheckpointTagVersion { Version = new ProjectionVersion(current.ProjectionId, 0, 0), Tag = null }; - var reader = new JsonTextReader(new StreamReader(new MemoryStream(source.ToArray()))); - return CheckpointTag.FromJson(reader, current); + public static CheckpointTagVersion ParseCheckpointTagVersionExtraJson(this ReadOnlyMemory source, ProjectionVersion current) { + return ParseCheckpointTagVersionExtraJson(source.ToArray(), current); } - public static CheckpointTagVersion ParseCheckpointTagVersionExtraJson(this string source, - ProjectionVersion current) { + public static CheckpointTagVersion ParseCheckpointTagVersionExtraJson(this string source, ProjectionVersion current) { if (string.IsNullOrEmpty(source)) - return new CheckpointTagVersion { Version = new ProjectionVersion(current.ProjectionId, 0, 0), Tag = null }; + return new() { Version = new(current.ProjectionId, 0, 0), Tag = null }; var reader = new JsonTextReader(new StringReader(source)); return CheckpointTag.FromJson(reader, current); } @@ -59,7 +50,7 @@ public static Dictionary ParseCheckpointExtraJson(this string so if (string.IsNullOrEmpty(source)) return null; var reader = new JsonTextReader(new StringReader(source)); - return CheckpointTag.FromJson(reader, default(ProjectionVersion)).ExtraMetadata; + return CheckpointTag.FromJson(reader, default).ExtraMetadata; } public static string ParseCheckpointTagCorrelationId(this string source) { @@ -72,7 +63,7 @@ public static string ParseCheckpointTagCorrelationId(this string source) { if (reader.TokenType != JsonToken.StartObject) return null; while (true) { - CheckpointTag.Check(reader.Read(), reader); + CheckpointTag.Check(reader.Read()); if (reader.TokenType == JsonToken.EndObject) break; if (reader.TokenType != JsonToken.PropertyName) diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagVersion.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagVersion.cs index ce07d201f5e..aa8a750ce5f 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagVersion.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CheckpointTagVersion.cs @@ -13,10 +13,10 @@ public struct CheckpointTagVersion { public Dictionary ExtraMetadata; public CheckpointTag AdjustBy(PositionTagger tagger, ProjectionVersion version) { - if (SystemVersion == ProjectionsSubsystem.VERSION && Version.Version == version.Version - && Version.ProjectionId == version.ProjectionId) - return Tag; - - return tagger.AdjustTag(Tag); + return SystemVersion == ProjectionsSubsystem.VERSION + && Version.Version == version.Version + && Version.ProjectionId == version.ProjectionId + ? Tag + : tagger.AdjustTag(Tag); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointManager.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointManager.cs index ed07c2c6fd2..bda626d9cfb 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointManager.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointManager.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Concurrent; -using System.Diagnostics.Contracts; using KurrentDB.Core.Bus; using KurrentDB.Projections.Core.Messages; using KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; @@ -18,17 +17,13 @@ public abstract class CoreProjectionCheckpointManager : IProjectionCheckpointMan IEmittedEventWriter { protected readonly ProjectionNamesBuilder _namingBuilder; protected readonly ProjectionConfig _projectionConfig; - protected readonly ILogger _logger; - protected readonly int _maxProjectionStateSize; + protected readonly ILogger _logger = Log.ForContext(); + private readonly int _maxProjectionStateSize; private readonly int _maxProjectionStateSizeThreshold; - private readonly bool _usePersistentCheckpoints; - protected readonly IPublisher _publisher; private readonly Guid _projectionCorrelationId; private readonly CheckpointTag _zeroTag; - - protected ProjectionCheckpoint _currentCheckpoint; private ProjectionCheckpoint _closingCheckpoint; internal CheckpointTag _requestedCheckpointPosition; @@ -50,82 +45,60 @@ protected CoreProjectionCheckpointManager( IPublisher publisher, Guid projectionCorrelationId, ProjectionConfig projectionConfig, - string name, PositionTagger positionTagger, ProjectionNamesBuilder namingBuilder, bool usePersistentCheckpoints, int maxProjectionStateSize) { - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (projectionConfig == null) - throw new ArgumentNullException("projectionConfig"); - if (name == null) - throw new ArgumentNullException("name"); - if (positionTagger == null) - throw new ArgumentNullException("positionTagger"); - if (namingBuilder == null) - throw new ArgumentNullException("namingBuilder"); - if (name == "") - throw new ArgumentException("name"); - if (maxProjectionStateSize <= 0) - throw new ArgumentException(nameof(maxProjectionStateSize)); - - _lastProcessedEventPosition = new PositionTracker(positionTagger); - _zeroTag = positionTagger.MakeZeroCheckpointTag(); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(projectionConfig); + ArgumentNullException.ThrowIfNull(positionTagger); + ArgumentNullException.ThrowIfNull(namingBuilder); + ArgumentOutOfRangeException.ThrowIfLessThan(maxProjectionStateSize, 1); + _lastProcessedEventPosition = new(positionTagger); + _zeroTag = positionTagger.MakeZeroCheckpointTag(); _publisher = publisher; _projectionCorrelationId = projectionCorrelationId; _projectionConfig = projectionConfig; - _logger = Log.ForContext(); _namingBuilder = namingBuilder; _usePersistentCheckpoints = usePersistentCheckpoints; - _requestedCheckpointState = new PartitionState("", null, _zeroTag); - _currentProjectionState = new PartitionState("", null, _zeroTag); + _requestedCheckpointState = new("", null, _zeroTag); + _currentProjectionState = new("", null, _zeroTag); _maxProjectionStateSize = maxProjectionStateSize; _maxProjectionStateSizeThreshold = (int)(0.50 * _maxProjectionStateSize); } protected abstract ProjectionCheckpoint CreateProjectionCheckpoint(CheckpointTag checkpointPosition); - protected abstract void BeginWriteCheckpoint( - CheckpointTag requestedCheckpointPosition, string requestedCheckpointState); + protected abstract void BeginWriteCheckpoint(CheckpointTag requestedCheckpointPosition, string requestedCheckpointState); - protected abstract void CapturePartitionStateUpdated(string partition, PartitionState oldState, - PartitionState newState); + protected abstract void CapturePartitionStateUpdated(string partition, PartitionState oldState, PartitionState newState); protected abstract void EmitPartitionCheckpoints(); - public abstract void RecordEventOrder(ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, - Action committed); - - public abstract void BeginLoadPartitionStateAt( - string statePartition, CheckpointTag requestedStateCheckpointTag, Action loadCompleted); + public abstract void RecordEventOrder(ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, Action committed); - public abstract void PartitionCompleted(string partition); + public abstract void BeginLoadPartitionStateAt(string statePartition, CheckpointTag requestedStateCheckpointTag, Action loadCompleted); public virtual void Initialize() { - if (_currentCheckpoint != null) - _currentCheckpoint.Dispose(); - if (_closingCheckpoint != null) - _closingCheckpoint.Dispose(); + _currentCheckpoint?.Dispose(); + _closingCheckpoint?.Dispose(); _currentCheckpoint = null; _closingCheckpoint = null; _requestedCheckpointPosition = null; _inCheckpoint = false; - _requestedCheckpointState = new PartitionState("", null, _zeroTag); + _requestedCheckpointState = new("", null, _zeroTag); _lastCompletedCheckpointPosition = null; _lastProcessedEventPosition.Initialize(); _lastProcessedEventProgress = -1; - _eventsProcessedAfterRestart = 0; _started = false; _stopping = false; _stopped = false; - _currentProjectionState = new PartitionState("", null, _zeroTag); + _currentProjectionState = new("", null, _zeroTag); } public virtual void Start(CheckpointTag checkpointTag, PartitionState rootPartitionState) { - Contract.Requires(_currentCheckpoint == null); if (_started) throw new InvalidOperationException("Already started"); _started = true; @@ -156,33 +129,24 @@ public void Stopped() { _started = false; _stopped = true; - if (_currentCheckpoint != null) - _currentCheckpoint.Dispose(); + _currentCheckpoint?.Dispose(); _currentCheckpoint = null; - if (_closingCheckpoint != null) - _closingCheckpoint.Dispose(); + _closingCheckpoint?.Dispose(); _closingCheckpoint = null; } public virtual void GetStatistics(ProjectionStatistics info) { - info.Position = (_lastProcessedEventPosition.LastTag ?? (object)"").ToString(); + info.Position = _lastProcessedEventPosition.LastTag?.ToString() ?? ""; info.Progress = _lastProcessedEventProgress; info.LastCheckpoint = _lastCompletedCheckpointPosition != null ? _lastCompletedCheckpointPosition.ToString() : ""; info.EventsProcessedAfterRestart = _eventsProcessedAfterRestart; - info.WritePendingEventsBeforeCheckpoint = _closingCheckpoint != null - ? _closingCheckpoint.GetWritePendingEvents() - : 0; - info.WritePendingEventsAfterCheckpoint = (_currentCheckpoint != null - ? _currentCheckpoint.GetWritePendingEvents() - : 0); - info.ReadsInProgress = /*_readDispatcher.ActiveRequestCount*/ - + +(_closingCheckpoint != null ? _closingCheckpoint.GetReadsInProgress() : 0) - + (_currentCheckpoint != null ? _currentCheckpoint.GetReadsInProgress() : 0); - info.WritesInProgress = (_closingCheckpoint != null ? _closingCheckpoint.GetWritesInProgress() : 0) - + (_currentCheckpoint != null ? _currentCheckpoint.GetWritesInProgress() : 0); + info.WritePendingEventsBeforeCheckpoint = _closingCheckpoint?.GetWritePendingEvents() ?? 0; + info.WritePendingEventsAfterCheckpoint = _currentCheckpoint?.GetWritePendingEvents() ?? 0; + info.ReadsInProgress = (_closingCheckpoint?.GetReadsInProgress() ?? 0) + (_currentCheckpoint?.GetReadsInProgress() ?? 0); + info.WritesInProgress = (_closingCheckpoint?.GetWritesInProgress() ?? 0) + (_currentCheckpoint?.GetWritesInProgress() ?? 0); info.CheckpointStatus = _inCheckpoint ? "Requested" : ""; foreach (var (partition, stateSize) in _stateSizeByPartition) { @@ -282,9 +246,7 @@ public void Progress(float progress) { _lastProcessedEventProgress = progress < -1 ? -1 : progress; } - public CheckpointTag LastProcessedEventPosition { - get { return _lastProcessedEventPosition.LastTag; } - } + public CheckpointTag LastProcessedEventPosition => _lastProcessedEventPosition.LastTag; public void Handle(CoreProjectionProcessingMessage.ReadyForCheckpoint message) { @@ -344,7 +306,7 @@ protected void EnsureStarted() { throw new InvalidOperationException("Not started"); } - /// true - if checkpoint has beem completed in-sync + /// true - if checkpoint has been completed in-sync private bool RequestCheckpoint(PositionTracker lastProcessedEventPosition, bool forcePrepareCheckpoint = false) { if (!forcePrepareCheckpoint && !_usePersistentCheckpoints) @@ -356,9 +318,7 @@ private bool RequestCheckpoint(PositionTracker lastProcessedEventPosition, /// true - if checkpoint has been completed in-sync private bool StartCheckpoint(PositionTracker lastProcessedEventPosition, PartitionState projectionState) { - Contract.Requires(_closingCheckpoint == null); - if (projectionState == null) - throw new ArgumentNullException("projectionState"); + ArgumentNullException.ThrowIfNull(projectionState); CheckpointTag requestedCheckpointPosition = lastProcessedEventPosition.LastTag; if (requestedCheckpointPosition == _lastCompletedCheckpointPosition) @@ -381,14 +341,13 @@ protected void SendPrerecordedEvent( KurrentDB.Core.Data.ResolvedEvent pair, CheckpointTag positionTag, long prerecordedEventMessageSequenceNumber) { var committedEvent = new ReaderSubscriptionMessage.CommittedEventDistributed( - Guid.Empty, new ResolvedEvent(pair, null), null, -1, source: this.GetType()); + Guid.Empty, new ResolvedEvent(pair, null), null, -1, source: GetType()); _publisher.Publish( EventReaderSubscriptionMessage.CommittedEventReceived.FromCommittedEventDistributed( committedEvent, positionTag, null, _projectionCorrelationId, prerecordedEventMessageSequenceNumber)); } - protected void RequestRestart(string reason) { _stopped = true; // ignore messages _publisher.Publish(new CoreProjectionProcessingMessage.RestartRequested(_projectionCorrelationId, reason)); @@ -400,7 +359,6 @@ private void Failed(string reason) { } protected void CheckpointWritten(CheckpointTag lastCompletedCheckpointPosition) { - Contract.Requires(_closingCheckpoint != null); _lastCompletedCheckpointPosition = lastCompletedCheckpointPosition; _closingCheckpoint.Dispose(); _closingCheckpoint = null; @@ -411,8 +369,7 @@ protected void CheckpointWritten(CheckpointTag lastCompletedCheckpointPosition) //NOTE: the next checkpoint will start by completing checkpoint work item _publisher.Publish( - new CoreProjectionProcessingMessage.CheckpointCompleted( - _projectionCorrelationId, _lastCompletedCheckpointPosition)); + new CoreProjectionProcessingMessage.CheckpointCompleted(_projectionCorrelationId, _lastCompletedCheckpointPosition)); } protected void UpdateStateSizeMetrics(string partition, int newStateSize) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointReader.cs index c0684641d63..e4dbd504bb4 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointReader.cs @@ -26,7 +26,7 @@ public class CoreProjectionCheckpointReader : ICoreProjectionCheckpointReader { private bool _stateRequested; private long _nextStateIndexToRequest; - private ProjectionVersion _projectionVersion; + private readonly ProjectionVersion _projectionVersion; private Guid _readRequestId; private long _lastWrittenCheckpointEventNumber; @@ -59,12 +59,12 @@ public void Initialize() { _stateRequested = false; } - protected void BeforeBeginLoadState() { + private void BeforeBeginLoadState() { _lastWrittenCheckpointEventNumber = ExpectedVersion.NoStream; _nextStateIndexToRequest = -1; // from the end } - protected void RequestLoadState() { + private void RequestLoadState() { const int recordsToRequest = 10; _readRequestId = Guid.NewGuid(); _ioDispatcher.ReadBackward( @@ -111,11 +111,9 @@ private void OnLoadStateReadRequestCompleted(ClientMessage.ReadStreamEventsBackw } - protected void CheckpointLoaded(CheckpointTag checkpointTag, string checkpointData) { + private void CheckpointLoaded(CheckpointTag checkpointTag, string checkpointData) { if (checkpointTag == null) // no checkpoint data found - { checkpointData = null; - } _publisher.Publish( new CoreProjectionProcessingMessage.CheckpointLoaded( diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointWriter.cs index 41aa68b9533..6db6a76d61a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointWriter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/CoreProjectionCheckpointWriter.cs @@ -15,12 +15,12 @@ namespace KurrentDB.Projections.Core.Services.Processing.Checkpointing; -public class CoreProjectionCheckpointWriter { - private readonly string _projectionCheckpointStreamId; - private readonly ILogger _logger; - private readonly IODispatcher _ioDispatcher; - private readonly ProjectionVersion _projectionVersion; - private readonly string _name; +public class CoreProjectionCheckpointWriter( + string projectionCheckpointStreamId, + IODispatcher ioDispatcher, + ProjectionVersion projectionVersion, + string name) { + private static readonly ILogger Logger = Log.ForContext(); private Guid _writeRequestId; private int _inCheckpointWriteAttempt; @@ -31,18 +31,8 @@ public class CoreProjectionCheckpointWriter { private const int MaxNumberOfRetries = 12; private const int MinAttemptWarnThreshold = 5; private bool _metaStreamWritten; - private Random _random = new Random(); - private bool _largeCheckpointWarningLogged = false; - - public CoreProjectionCheckpointWriter( - string projectionCheckpointStreamId, IODispatcher ioDispatcher, ProjectionVersion projectionVersion, - string name) { - _projectionCheckpointStreamId = projectionCheckpointStreamId; - _logger = Log.ForContext(); - _ioDispatcher = ioDispatcher; - _projectionVersion = projectionVersion; - _name = name; - } + private readonly Random _random = new(); + private bool _largeCheckpointWarningLogged; public void BeginWriteCheckpoint(IEnvelope envelope, CheckpointTag requestedCheckpointPosition, string requestedCheckpointState) { @@ -54,7 +44,7 @@ public void BeginWriteCheckpoint(IEnvelope envelope, Guid.NewGuid(), ProjectionEventTypes.ProjectionCheckpoint, true, requestedCheckpointState == null ? null : Helper.UTF8NoBom.GetBytes(requestedCheckpointState), isPropertyMetadata: false, - requestedCheckpointPosition.ToJsonBytes(projectionVersion: _projectionVersion)); + requestedCheckpointPosition.ToJsonBytes(projectionVersion: projectionVersion)); PublishWriteStreamMetadataAndCheckpointEventDelayed(); } @@ -63,22 +53,18 @@ private void WriteCheckpointEventCompleted( if (_inCheckpointWriteAttempt == 0) throw new InvalidOperationException(); if (operationResult == OperationResult.Success) { - if (_logger != null) - _logger.Verbose( - "Checkpoint has been written for projection {projection} at sequence number {firstWrittenEventNumber} (current)", - _name, - firstWrittenEventNumber); + Logger?.Verbose( + "Checkpoint has been written for projection {projection} at sequence number {firstWrittenEventNumber} (current)", + name, + firstWrittenEventNumber); _lastWrittenCheckpointEventNumber = firstWrittenEventNumber; _inCheckpointWriteAttempt = 0; - _envelope.ReplyWith( - new CoreProjectionCheckpointWriterMessage.CheckpointWritten(_requestedCheckpointPosition)); + _envelope.ReplyWith(new CoreProjectionCheckpointWriterMessage.CheckpointWritten(_requestedCheckpointPosition)); } else { - if (_logger != null) { - _logger.Information( - "Failed to write projection checkpoint to stream {stream}. Error: {e}", eventStreamId, - Enum.GetName(typeof(OperationResult), operationResult)); - } + Logger?.Information( + "Failed to write projection checkpoint to stream {stream}. Error: {e}", eventStreamId, + Enum.GetName(typeof(OperationResult), operationResult)); switch (operationResult) { case OperationResult.WrongExpectedVersion: @@ -92,10 +78,7 @@ private void WriteCheckpointEventCompleted( if (_inCheckpointWriteAttempt >= MaxNumberOfRetries) { //The first parameter is not needed in this case as the CoreProjectionCheckpointManager takes care of filling in the projection id when it reconstructs the message _envelope.ReplyWith(new CoreProjectionProcessingMessage.Failed(Guid.Empty, - string.Format( - "After retrying {0} times, we failed to write the checkpoint for {1} to {2} due to a {3}", - MaxNumberOfRetries, _name, eventStreamId, - Enum.GetName(typeof(OperationResult), operationResult)))); + $"After retrying {MaxNumberOfRetries} times, we failed to write the checkpoint for {name} to {eventStreamId} due to a {Enum.GetName(typeof(OperationResult), operationResult)}")); _inCheckpointWriteAttempt = 0; return; } @@ -115,25 +98,24 @@ private void PublishWriteStreamMetadataAndCheckpointEventDelayed() { if (delayInSeconds == 0) PublishWriteStreamMetadataAndCheckpointEvent(); else { - if (attempt >= MinAttemptWarnThreshold && _logger != null) { - _logger.Warning("Attempt: {attempt} to write checkpoint for {projection} at {requestedCheckpointPosition} with expected version number {lastWrittenCheckpointEventNumber}. Backing off for {time} second(s).", + if (attempt >= MinAttemptWarnThreshold && Logger != null) { + Logger.Warning("Attempt: {attempt} to write checkpoint for {projection} at {requestedCheckpointPosition} with expected version number {lastWrittenCheckpointEventNumber}. Backing off for {time} second(s).", attempt, - _name, + name, _requestedCheckpointPosition, _lastWrittenCheckpointEventNumber, delayInSeconds); } - _ioDispatcher.Delay( + ioDispatcher.Delay( TimeSpan.FromSeconds(delayInSeconds), _ => PublishWriteStreamMetadataAndCheckpointEvent()); } } private void PublishWriteStreamMetadataAndCheckpointEvent() { - if (_logger != null) - _logger.Verbose( - "Writing checkpoint for {projection} at {requestedCheckpointPosition} with expected version number {lastWrittenCheckpointEventNumber}", - _name, _requestedCheckpointPosition, _lastWrittenCheckpointEventNumber); + Logger?.Verbose( + "Writing checkpoint for {projection} at {requestedCheckpointPosition} with expected version number {lastWrittenCheckpointEventNumber}", + name, _requestedCheckpointPosition, _lastWrittenCheckpointEventNumber); if (!_metaStreamWritten) PublishWriteStreamMetadata(); else @@ -141,8 +123,8 @@ private void PublishWriteStreamMetadataAndCheckpointEvent() { } private void PublishWriteStreamMetadata() { - var metaStreamId = SystemStreams.MetastreamOf(_projectionCheckpointStreamId); - _writeRequestId = _ioDispatcher.WriteEvent( + var metaStreamId = SystemStreams.MetastreamOf(projectionCheckpointStreamId); + _writeRequestId = ioDispatcher.WriteEvent( metaStreamId, ExpectedVersion.Any, CreateStreamMetadataEvent(), SystemAccounts.System, msg => { switch (msg.Result) { case OperationResult.Success: @@ -156,30 +138,29 @@ private void PublishWriteStreamMetadata() { }); } - private Event CreateStreamMetadataEvent() { - var eventId = Guid.NewGuid(); + private static Event CreateStreamMetadataEvent() { var acl = new StreamAcl( readRole: SystemRoles.Admins, writeRole: SystemRoles.Admins, deleteRole: SystemRoles.Admins, metaReadRole: SystemRoles.All, metaWriteRole: SystemRoles.Admins); var metadata = new StreamMetadata(maxCount: 2, maxAge: null, cacheControl: null, acl: acl); var dataBytes = metadata.ToJsonBytes(); - return new Event(eventId, SystemEventTypes.StreamMetadata, isJson: true, data: dataBytes); + return new Event(Guid.NewGuid(), SystemEventTypes.StreamMetadata, isJson: true, data: dataBytes); } private void PublishWriteCheckpointEvent() { CheckpointSizeCheck(); - _writeRequestId = _ioDispatcher.WriteEvent( - _projectionCheckpointStreamId, _lastWrittenCheckpointEventNumber, _checkpointEventToBePublished, + _writeRequestId = ioDispatcher.WriteEvent( + projectionCheckpointStreamId, _lastWrittenCheckpointEventNumber, _checkpointEventToBePublished, SystemAccounts.System, - msg => WriteCheckpointEventCompleted(_projectionCheckpointStreamId, msg.Result, msg.FirstEventNumbers.Single)); + msg => WriteCheckpointEventCompleted(projectionCheckpointStreamId, msg.Result, msg.FirstEventNumbers.Single)); } private void CheckpointSizeCheck() { if (!_largeCheckpointWarningLogged && _checkpointEventToBePublished.Data.Length >= 8_000_000) { Log.Warning( "Checkpoint size for the Projection {projectionName} is greater than 8 MB. Checkpoint size for a projection should be less than 16 MB. Current checkpoint size for Projection {projectionName} is {stateSize} MB.", - _name, _name, + name, name, _checkpointEventToBePublished.Data.Length / Math.Pow(10, 6)); _largeCheckpointWarningLogged = true; } @@ -188,19 +169,19 @@ private void CheckpointSizeCheck() { public void Initialize() { _checkpointEventToBePublished = null; _inCheckpointWriteAttempt = 0; - _ioDispatcher.Writer.Cancel(_writeRequestId); + ioDispatcher.Writer.Cancel(_writeRequestId); _lastWrittenCheckpointEventNumber = ExpectedVersion.Invalid; _metaStreamWritten = false; } public void GetStatistics(ProjectionStatistics info) { - info.WritesInProgress = ((_inCheckpointWriteAttempt != 0) ? 1 : 0) + info.WritesInProgress; + info.WritesInProgress = (_inCheckpointWriteAttempt != 0 ? 1 : 0) + info.WritesInProgress; info.CheckpointStatus = _inCheckpointWriteAttempt > 0 - ? "Writing (" + _inCheckpointWriteAttempt + ")" + ? $"Writing ({_inCheckpointWriteAttempt})" : info.CheckpointStatus; } - public void StartFrom(CheckpointTag checkpointTag, long checkpointEventNumber) { + public void StartFrom(long checkpointEventNumber) { _lastWrittenCheckpointEventNumber = checkpointEventNumber; _metaStreamWritten = checkpointEventNumber != ExpectedVersion.NoStream; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/DefaultCheckpointManager.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/DefaultCheckpointManager.cs index fc49dad872c..27b20530fcf 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/DefaultCheckpointManager.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/DefaultCheckpointManager.cs @@ -16,13 +16,14 @@ namespace KurrentDB.Projections.Core.Services.Processing.Checkpointing; -public class DefaultCheckpointManager : CoreProjectionCheckpointManager, - IHandle, - IHandle { +public class DefaultCheckpointManager + : CoreProjectionCheckpointManager, + IHandle, + IHandle { private readonly ClaimsPrincipal _runAs; private readonly CheckpointTag _zeroTag; private int _readRequestsInProgress; - private readonly HashSet _loadStateRequests = new HashSet(); + private readonly HashSet _loadStateRequests = []; protected readonly ProjectionVersion _projectionVersion; protected readonly IODispatcher _ioDispatcher; @@ -31,16 +32,21 @@ public class DefaultCheckpointManager : CoreProjectionCheckpointManager, private PartitionStateUpdateManager _partitionStateUpdateManager; public DefaultCheckpointManager( - IPublisher publisher, Guid projectionCorrelationId, ProjectionVersion projectionVersion, ClaimsPrincipal runAs, - IODispatcher ioDispatcher, ProjectionConfig projectionConfig, string name, PositionTagger positionTagger, - ProjectionNamesBuilder namingBuilder, bool usePersistentCheckpoints, bool producesRunningResults, - bool definesFold, - CoreProjectionCheckpointWriter coreProjectionCheckpointWriter, int maxProjectionStateSize) + IPublisher publisher, + Guid projectionCorrelationId, + ProjectionVersion projectionVersion, + ClaimsPrincipal runAs, + IODispatcher ioDispatcher, + ProjectionConfig projectionConfig, + PositionTagger positionTagger, + ProjectionNamesBuilder namingBuilder, + bool usePersistentCheckpoints, + CoreProjectionCheckpointWriter coreProjectionCheckpointWriter, + int maxProjectionStateSize) : base( - publisher, projectionCorrelationId, projectionConfig, name, positionTagger, namingBuilder, + publisher, projectionCorrelationId, projectionConfig, positionTagger, namingBuilder, usePersistentCheckpoints, maxProjectionStateSize) { - if (ioDispatcher == null) - throw new ArgumentNullException("ioDispatcher"); + ArgumentNullException.ThrowIfNull(ioDispatcher); _projectionVersion = projectionVersion; _runAs = runAs; _ioDispatcher = ioDispatcher; @@ -50,21 +56,20 @@ public DefaultCheckpointManager( } protected override void BeginWriteCheckpoint( - CheckpointTag requestedCheckpointPosition, string requestedCheckpointState) { + CheckpointTag requestedCheckpointPosition, + string requestedCheckpointState) { _requestedCheckpointPosition = requestedCheckpointPosition; _coreProjectionCheckpointWriter.BeginWriteCheckpoint( new SendToThisEnvelope(this), requestedCheckpointPosition, requestedCheckpointState); } public override void RecordEventOrder( - ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, Action committed) { + ResolvedEvent resolvedEvent, + CheckpointTag orderCheckpointTag, + Action committed) { committed(); } - public override void PartitionCompleted(string partition) { - _partitionStateUpdateManager.PartitionCompleted(partition); - } - public override void Initialize() { base.Initialize(); _partitionStateUpdateManager = null; @@ -83,24 +88,26 @@ public override void GetStatistics(ProjectionStatistics info) { } public override void BeginLoadPartitionStateAt( - string statePartition, CheckpointTag requestedStateCheckpointTag, Action loadCompleted) { - var stateEventType = ProjectionEventTypes.PartitionCheckpoint; + string statePartition, + CheckpointTag requestedStateCheckpointTag, + Action loadCompleted) { + const string stateEventType = ProjectionEventTypes.PartitionCheckpoint; var partitionCheckpointStreamName = _namingBuilder.MakePartitionCheckpointStreamName(statePartition); - ReadPartitionStream(partitionCheckpointStreamName, -1, requestedStateCheckpointTag, loadCompleted, - stateEventType); + ReadPartitionStream(partitionCheckpointStreamName, -1, requestedStateCheckpointTag, loadCompleted, stateEventType); } - private void ReadPartitionStream(string partitionStreamName, long eventNumber, + private void ReadPartitionStream(string partitionStreamName, + long eventNumber, CheckpointTag requestedStateCheckpointTag, - Action loadCompleted, string stateEventType) { + Action loadCompleted, + string stateEventType) { _readRequestsInProgress++; var requestId = Guid.NewGuid(); _ioDispatcher.ReadBackward( partitionStreamName, eventNumber, 1, false, SystemAccounts.System, - m => - OnLoadPartitionStateReadStreamEventsBackwardCompleted( - m, requestedStateCheckpointTag, loadCompleted, partitionStreamName, stateEventType), + m => OnLoadPartitionStateReadStreamEventsBackwardCompleted( + m, requestedStateCheckpointTag, loadCompleted, partitionStreamName, stateEventType), () => { _logger.Warning("Read backward for stream {stream} timed out. Retrying", partitionStreamName); _loadStateRequests.Remove(requestId); @@ -113,8 +120,11 @@ private void ReadPartitionStream(string partitionStreamName, long eventNumber, } private void OnLoadPartitionStateReadStreamEventsBackwardCompleted( - ClientMessage.ReadStreamEventsBackwardCompleted message, CheckpointTag requestedStateCheckpointTag, - Action loadCompleted, string partitionStreamName, string stateEventType) { + ClientMessage.ReadStreamEventsBackwardCompleted message, + CheckpointTag requestedStateCheckpointTag, + Action loadCompleted, + string partitionStreamName, + string stateEventType) { _loadStateRequests.Remove(message.CorrelationId); _readRequestsInProgress--; @@ -123,17 +133,17 @@ private void OnLoadPartitionStateReadStreamEventsBackwardCompleted( if (@event.EventType == stateEventType) { var parsed = @event.Metadata.ParseCheckpointTagVersionExtraJson(_projectionVersion); if (parsed.Version.ProjectionId != _projectionVersion.ProjectionId - || _projectionVersion.Epoch > parsed.Version.Version) { + || _projectionVersion.Epoch > parsed.Version.Version) { var state = new PartitionState("", null, _zeroTag); loadCompleted(state); - return; } else { var loadedStateCheckpointTag = parsed.AdjustBy(_positionTagger, _projectionVersion); var state = PartitionState.Deserialize( Helper.UTF8NoBom.GetString(@event.Data.Span), loadedStateCheckpointTag); loadCompleted(state); - return; } + + return; } } @@ -148,7 +158,7 @@ private void OnLoadPartitionStateReadStreamEventsBackwardCompleted( } protected override ProjectionCheckpoint CreateProjectionCheckpoint(CheckpointTag checkpointPosition) { - return new ProjectionCheckpoint( + return new( _publisher, _ioDispatcher, _projectionVersion, _runAs, this, checkpointPosition, _positionTagger, _projectionConfig.MaxWriteBatchLength, _projectionConfig.MaximumAllowedWritesInFlight, _logger); } @@ -161,10 +171,8 @@ public void Handle(CoreProjectionCheckpointWriterMessage.RestartRequested messag RequestRestart(message.Reason); } - protected override void CapturePartitionStateUpdated(string partition, PartitionState oldState, - PartitionState newState) { - if (_partitionStateUpdateManager == null) - _partitionStateUpdateManager = new PartitionStateUpdateManager(_namingBuilder); + protected override void CapturePartitionStateUpdated(string partition, PartitionState oldState, PartitionState newState) { + _partitionStateUpdateManager ??= new(_namingBuilder); _partitionStateUpdateManager.StateUpdated(partition, newState, oldState.CausedBy); UpdateStateSizeMetrics(partition, newState.Size); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ICoreProjectionCheckpointManager.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ICoreProjectionCheckpointManager.cs index f56fad04cf3..ef3f185dbaf 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ICoreProjectionCheckpointManager.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ICoreProjectionCheckpointManager.cs @@ -12,10 +12,7 @@ public interface ICoreProjectionCheckpointManager { void Stopping(); void Stopped(); void GetStatistics(ProjectionStatistics info); - - void StateUpdated(string partition, PartitionState oldState, PartitionState newState); - void PartitionCompleted(string partition); void EventProcessed(CheckpointTag checkpointTag, float progress); /// diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTagger.cs index acc50aefa44..090af976310 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTagger.cs @@ -1,25 +1,18 @@ // Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). -using KurrentDB.Projections.Core.Messages; +using static KurrentDB.Projections.Core.Messages.ReaderSubscriptionMessage; namespace KurrentDB.Projections.Core.Services.Processing.Checkpointing; -public abstract class PositionTagger { - public readonly int Phase; +public abstract class PositionTagger(int phase) { + protected readonly int Phase = phase; - public PositionTagger(int phase) { - Phase = phase; - } + public abstract bool IsMessageAfterCheckpointTag(CheckpointTag previous, CommittedEventDistributed committedEvent); - public abstract bool IsMessageAfterCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent); + public abstract CheckpointTag MakeCheckpointTag(CheckpointTag previous, CommittedEventDistributed committedEvent); - public abstract CheckpointTag MakeCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent); - - public abstract CheckpointTag MakeCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.EventReaderPartitionDeleted partitionDeleted); + public abstract CheckpointTag MakeCheckpointTag(CheckpointTag previous, EventReaderPartitionDeleted partitionDeleted); public abstract CheckpointTag MakeZeroCheckpointTag(); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTracker.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTracker.cs index 8c6cd369e1e..966221d0a54 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTracker.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/PositionTracker.cs @@ -5,40 +5,30 @@ namespace KurrentDB.Projections.Core.Services.Processing.Checkpointing; -public class PositionTracker { - private readonly PositionTagger _positionTagger; - private CheckpointTag _lastTag = null; - - public PositionTracker(PositionTagger positionTagger) { - _positionTagger = positionTagger; - } - - public CheckpointTag LastTag { - get { return _lastTag; } - } +public class PositionTracker(PositionTagger positionTagger) { + public CheckpointTag LastTag { get; private set; } public void UpdateByCheckpointTagForward(CheckpointTag newTag) { - if (_lastTag == null) + if (LastTag == null) throw new InvalidOperationException("Initial position was not set"); - if (newTag <= _lastTag) - throw new InvalidOperationException( - string.Format("Event at checkpoint tag {0} has been already processed", newTag)); + if (newTag <= LastTag) + throw new InvalidOperationException($"Event at checkpoint tag {newTag} has been already processed"); InternalUpdate(newTag); } public void UpdateByCheckpointTagInitial(CheckpointTag checkpointTag) { - if (_lastTag != null) + if (LastTag != null) throw new InvalidOperationException("Position tagger has be already updated"); InternalUpdate(checkpointTag); } private void InternalUpdate(CheckpointTag newTag) { - if (!_positionTagger.IsCompatible(newTag)) + if (!positionTagger.IsCompatible(newTag)) throw new InvalidOperationException("Cannot update by incompatible checkpoint tag"); - _lastTag = newTag; + LastTag = newTag; } public void Initialize() { - _lastTag = null; + LastTag = null; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ProjectionCheckpoint.cs b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ProjectionCheckpoint.cs index 438f01f1976..10613fa8cf4 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ProjectionCheckpoint.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Checkpointing/ProjectionCheckpoint.cs @@ -27,9 +27,9 @@ public class ProjectionCheckpoint : IDisposable, IEmittedStreamContainer, IEvent private readonly IProjectionCheckpointManager _readyHandler; private readonly PositionTagger _positionTagger; - private bool _checkpointRequested = false; + private bool _checkpointRequested; private int _requestedCheckpoints; - private bool _started = false; + private bool _started; private readonly IODispatcher _ioDispatcher; private readonly IPublisher _publisher; @@ -38,8 +38,8 @@ public class ProjectionCheckpoint : IDisposable, IEmittedStreamContainer, IEvent private List _awaitingStreams; - private Guid[] _writeQueueIds; - private int _maximumAllowedWritesInFlight; + private readonly Guid[] _writeQueueIds; + private readonly int _maximumAllowedWritesInFlight; public ProjectionCheckpoint( IPublisher publisher, @@ -52,16 +52,12 @@ public ProjectionCheckpoint( int maxWriteBatchLength, int maximumAllowedWritesInFlight, ILogger logger = null) { - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (ioDispatcher == null) - throw new ArgumentNullException("ioDispatcher"); - if (readyHandler == null) - throw new ArgumentNullException("readyHandler"); - if (positionTagger == null) - throw new ArgumentNullException("positionTagger"); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(ioDispatcher); + ArgumentNullException.ThrowIfNull(readyHandler); + ArgumentNullException.ThrowIfNull(positionTagger); if (from.CommitPosition < from.PreparePosition) - throw new ArgumentException("from"); + throw new ArgumentException(null, nameof(from)); //NOTE: fromCommit can be equal fromPrepare on 0 position. Is it possible anytime later? Ignoring for now. _maximumAllowedWritesInFlight = maximumAllowedWritesInFlight; _publisher = publisher; @@ -73,7 +69,7 @@ public ProjectionCheckpoint( _from = _last = from; _maxWriteBatchLength = maxWriteBatchLength; _logger = logger; - _writeQueueIds = Enumerable.Range(0, _maximumAllowedWritesInFlight).Select(x => Guid.NewGuid()).ToArray(); + _writeQueueIds = Enumerable.Range(0, _maximumAllowedWritesInFlight).Select(_ => Guid.NewGuid()).ToArray(); } public void Start() { @@ -105,14 +101,10 @@ private void UpdateLastPosition(EmittedEventEnvelope[] events) { private void ValidateCheckpointPosition(CheckpointTag position) { if (position <= _from) throw new InvalidOperationException( - string.Format( - "Checkpoint position before or equal to the checkpoint start position. Requested: '{0}' Started: '{1}'", - position, _from)); + $"Checkpoint position before or equal to the checkpoint start position. Requested: '{position}' Started: '{_from}'"); if (position < _last) throw new InvalidOperationException( - string.Format( - "Checkpoint position before last handled position. Requested: '{0}' Last: '{1}'", position, - _last)); + $"Checkpoint position before last handled position. Requested: '{position}' Last: '{_last}'"); } public void Prepare(CheckpointTag position) { @@ -136,27 +128,22 @@ private void EnsureCheckpointNotRequested() { } private void EmitEventsToStream(string streamId, EmittedEventEnvelope[] emittedEvents) { - if (string.IsNullOrEmpty(streamId)) - throw new ArgumentNullException("streamId"); - EmittedStream stream; - if (!_emittedStreams.TryGetValue(streamId, out stream)) { + ArgumentException.ThrowIfNullOrEmpty(streamId); + if (!_emittedStreams.TryGetValue(streamId, out var stream)) { var streamMetadata = emittedEvents.Length > 0 ? emittedEvents[0].StreamMetadata : null; var writeQueueId = _maximumAllowedWritesInFlight == AllowedWritesInFlight.Unbounded ? (Guid?)null : _writeQueueIds[_emittedStreams.Count % _maximumAllowedWritesInFlight]; - IEmittedStreamsWriter writer; - if (writeQueueId == null) - writer = new EmittedStreamsWriter(_ioDispatcher); - else - writer = new QueuedEmittedStreamsWriter(_ioDispatcher, writeQueueId.Value); + IEmittedStreamsWriter writer = writeQueueId == null + ? new EmittedStreamsWriter(_ioDispatcher) + : new QueuedEmittedStreamsWriter(_ioDispatcher, writeQueueId.Value); var writerConfiguration = new EmittedStream.WriterConfiguration( writer, streamMetadata, _runAs, maxWriteBatchLength: _maxWriteBatchLength, logger: _logger); - stream = new EmittedStream(streamId, writerConfiguration, _projectionVersion, _positionTagger, _from, - _publisher, _ioDispatcher, this); + stream = new(streamId, writerConfiguration, _projectionVersion, _positionTagger, _from, _ioDispatcher, this); if (_started) stream.Start(); @@ -204,8 +191,7 @@ public void Dispose() { } public void Handle(CoreProjectionProcessingMessage.EmittedStreamAwaiting message) { - if (_awaitingStreams == null) - _awaitingStreams = new List(); + _awaitingStreams ??= []; _awaitingStreams.Add(message.Envelope); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/CoreProjection.cs b/src/KurrentDB.Projections.Core/Services/Processing/CoreProjection.cs index f6a3369ed4c..7dfe7299eac 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/CoreProjection.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/CoreProjection.cs @@ -18,7 +18,6 @@ namespace KurrentDB.Projections.Core.Services.Processing; - public class CoreProjection : IDisposable, ICoreProjection, ICoreProjectionForProcessingPhase, @@ -87,19 +86,14 @@ public CoreProjection( ClaimsPrincipal runAs, IPublisher publisher, IODispatcher ioDispatcher, - ReaderSubscriptionDispatcher subscriptionDispatcher, ILogger logger, ProjectionNamesBuilder namingBuilder, CoreProjectionCheckpointWriter coreProjectionCheckpointWriter, PartitionStateCache partitionStateCache, string effectiveProjectionName, ITimeProvider timeProvider) { - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (ioDispatcher == null) - throw new ArgumentNullException("ioDispatcher"); - if (subscriptionDispatcher == null) - throw new ArgumentNullException("subscriptionDispatcher"); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(ioDispatcher); _projectionProcessingStrategy = projectionProcessingStrategy; _projectionCorrelationId = projectionCorrelationId; @@ -130,7 +124,6 @@ public CoreProjection( ioDispatcher, coreProjectionCheckpointWriter); - //NOTE: currently assuming the first checkpoint manager to be able to load any state _checkpointReader = new CoreProjectionCheckpointReader( publisher, @@ -143,12 +136,10 @@ public CoreProjection( GoToState(State.Initial); } - private void BeginPhase(IProjectionProcessingPhase processingPhase, CheckpointTag startFrom, - PartitionState rootPartitionState) { + private void BeginPhase(IProjectionProcessingPhase processingPhase, CheckpointTag startFrom, PartitionState rootPartitionState) { _projectionProcessingPhase = processingPhase; _projectionProcessingPhase.SetProjectionState(PhaseState.Starting); _checkpointManager = processingPhase.CheckpointManager; - _projectionProcessingPhase.InitializeFromCheckpoint(startFrom); _checkpointManager.Start(startFrom, rootPartitionState); } @@ -159,8 +150,7 @@ private void UpdateStatistics() { int sequentialNumber = _statisticsSequentialNumber++; var info = new ProjectionStatistics(); GetStatistics(info); - _publisher.Publish( - new CoreProjectionStatusMessage.StatisticsReport(_projectionCorrelationId, info, sequentialNumber)); + _publisher.Publish(new CoreProjectionStatusMessage.StatisticsReport(_projectionCorrelationId, info, sequentialNumber)); } public void Start() { @@ -180,10 +170,7 @@ public void Stop() { State.LoadStateRequested | State.StateLoaded | State.Subscribed | State.Running | State.PhaseCompleted | State.CompletingPhase); try { - if (_state == State.LoadStateRequested || _state == State.PhaseCompleted) - GoToState(State.Stopped); - else - GoToState(State.Stopping); + GoToState(_state is State.LoadStateRequested or State.PhaseCompleted ? State.Stopped : State.Stopping); } catch (Exception ex) { SetFaulted(ex); } @@ -195,7 +182,7 @@ public void Kill() { } public bool Suspend() { - if (_state == State.Stopped || _state == State.Suspended) + if (_state is State.Stopped or State.Suspended) return false; GoToState(State.Suspended); @@ -224,8 +211,7 @@ private void GetStatistics(ProjectionStatistics info) { info.BufferedEvents = 0; info.PartitionsCached = _partitionStateCache.CachedItemCount; _enrichStatistics(info); - if (_projectionProcessingPhase != null) - _projectionProcessingPhase.GetStatistics(info); + _projectionProcessingPhase?.GetStatistics(info); } public void CompletePhase() { @@ -288,7 +274,7 @@ public void Handle(CoreProjectionProcessingMessage.CheckpointLoaded message) { //TODO: initialize projection state here (test it) //TODO: write test to ensure projection state is correctly loaded from a checkpoint and posted back when enough empty records processed //TODO: handle errors - _coreProjectionCheckpointWriter.StartFrom(checkpointTag, message.CheckpointEventNumber); + _coreProjectionCheckpointWriter.StartFrom(message.CheckpointEventNumber); PartitionState rootPartitionState = null; if (_requiresRootPartition) { @@ -323,9 +309,7 @@ public void Handle(CoreProjectionProcessingMessage.RestartRequested message) { message.Reason); if (_state != State.Running) { SetFaulted( - string.Format( - "A concurrency violation was detected, but the projection is not running. Current state is: {0}. The reason for the restart is: '{1}' ", - _state, message.Reason)); + $"A concurrency violation was detected, but the projection is not running. Current state is: {_state}. The reason for the restart is: '{message.Reason}' "); return; } @@ -339,9 +323,8 @@ public void Handle(CoreProjectionProcessingMessage.Failed message) { SetFaulted(message.Reason); } - public void EnsureUnsubscribed() { - if (_projectionProcessingPhase != null) - _projectionProcessingPhase.EnsureUnsubscribed(); + private void EnsureUnsubscribed() { + _projectionProcessingPhase?.EnsureUnsubscribed(); } private void GoToState(State state) { @@ -349,14 +332,10 @@ private void GoToState(State state) { _logger.Debug($"Projection {_name} has been suspended for a subsystem restart. Cannot go to state {state}"); return; } - // _logger.Trace("CP: {projection} {stateFrom} => {stateTo}", _name, _state, state); - var wasStopped = _state == State.Stopped || _state == State.Faulted || _state == State.PhaseCompleted; - var wasStopping = _state == State.Stopping || _state == State.FaultedStopping - || _state == State.CompletingPhase; - var wasStarting = _state == State.LoadStateRequested || _state == State.StateLoaded - || _state == State.Subscribed; - var wasStarted = _state == State.Subscribed || _state == State.Running || _state == State.Stopping - || _state == State.FaultedStopping || _state == State.CompletingPhase; + var wasStopped = _state is State.Stopped or State.Faulted or State.PhaseCompleted; + var wasStopping = _state is State.Stopping or State.FaultedStopping or State.CompletingPhase; + var wasStarting = _state is State.LoadStateRequested or State.StateLoaded or State.Subscribed; + var wasStarted = _state is State.Subscribed or State.Running or State.Stopping or State.FaultedStopping or State.CompletingPhase; var wasRunning = _state == State.Running; var stateChanged = _state != state; _state = state; // set state before transition to allow further state change @@ -390,9 +369,6 @@ private void GoToState(State state) { break; case State.Faulted: case State.FaultedStopping: - if (wasRunning) - _projectionProcessingPhase.SetProjectionState(PhaseState.Stopped); - break; case State.Stopped: case State.Stopping: case State.CompletingPhase: @@ -433,7 +409,6 @@ private void GoToState(State state) { EnterFaulted(); break; case State.CompletingPhase: - EnterCompletingPhase(); break; case State.PhaseCompleted: EnterPhaseCompleted(); @@ -469,20 +444,16 @@ private void EnterLoadStateRequested() { _checkpointReader.BeginLoadState(); } - private void EnterStateLoaded() { + private static void EnterStateLoaded() { } private void EnterSubscribed() { - if (_startOnLoad) { - GoToState(State.Running); - } else - GoToState(State.Stopped); + GoToState(_startOnLoad ? State.Running : State.Stopped); } private void EnterRunning() { try { - _publisher.Publish( - new CoreProjectionStatusMessage.Started(_projectionCorrelationId, _name)); + _publisher.Publish(new CoreProjectionStatusMessage.Started(_projectionCorrelationId, _name)); _projectionProcessingPhase.ProcessEvent(); } catch (Exception ex) { SetFaulted(ex); @@ -508,9 +479,6 @@ private void EnterFaulted() { new CoreProjectionStatusMessage.Faulted(_projectionCorrelationId, _faultedReason)); } - private void EnterCompletingPhase() { - } - private void EnterPhaseCompleted() { var completedPhaseIndex = _checkpointManager.LastProcessedEventPosition.Phase; if (completedPhaseIndex == _projectionProcessingPhases.Length - 1) { @@ -525,8 +493,7 @@ private void EnterPhaseCompleted() { private void EnsureState(State expectedStates) { if ((_state & expectedStates) == 0) { - throw new Exception( - string.Format("Current state is {0}. Expected states are: {1}", _state, expectedStates)); + throw new Exception($"Current state is {_state}. Expected states are: {expectedStates}"); } } @@ -556,8 +523,7 @@ private void Tick() { public void Dispose() { _disposed = true; EnsureUnsubscribed(); - if (_projectionProcessingPhase != null) - _projectionProcessingPhase.Dispose(); + _projectionProcessingPhase?.Dispose(); } public void EnsureTickPending() { @@ -571,7 +537,7 @@ public void EnsureTickPending() { } public void SetFaulted(Exception ex) { - SetFaulted(ex.Message + "\r\n" + (ex.StackTrace ?? "").ToString()); + SetFaulted($"{ex.Message}\r\n{(ex.StackTrace ?? "")}"); } public void SetFaulted(string reason) { @@ -625,9 +591,7 @@ private void CompleteCheckpointSuggestedWorkItem() { } - public CheckpointTag LastProcessedEventPosition { - get { return _checkpointManager.LastProcessedEventPosition; } - } + public CheckpointTag LastProcessedEventPosition => _checkpointManager.LastProcessedEventPosition; public void Subscribed() { GoToState(State.Subscribed); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/CoreProjectionQueue.cs b/src/KurrentDB.Projections.Core/Services/Processing/CoreProjectionQueue.cs index 942180bbdb0..8f64b8e86c2 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/CoreProjectionQueue.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/CoreProjectionQueue.cs @@ -9,11 +9,19 @@ namespace KurrentDB.Projections.Core.Services.Processing; -public class CoreProjectionQueue { - private readonly StagedProcessingQueue _queuePendingEvents; - - private readonly IPublisher _publisher; - private readonly int _pendingEventsThreshold; +public class CoreProjectionQueue(IPublisher publisher, int pendingEventsThreshold, bool orderedPartitionProcessing) { + private readonly StagedProcessingQueue _queuePendingEvents = new( + [ + true /* record event order - async with ordered output*/, true + /* get state partition - ordered as it may change correlation id - sync */, + false + /* load foreach state - async- unordered completion*/, + orderedPartitionProcessing + /* process Js - unordered/ordered - inherently unordered/ordered completion*/, + true + /* write emits - ordered - async ordered completion*/, + false /* complete item */ + ]); private CheckpointTag _lastEnqueuedEventTag; private bool _justInitialized; @@ -24,42 +32,30 @@ public event Action EnsureTickPending { remove { _queuePendingEvents.EnsureTickPending -= value; } } - public CoreProjectionQueue(IPublisher publisher, int pendingEventsThreshold, bool orderedPartitionProcessing) { - _queuePendingEvents = - new StagedProcessingQueue( - new[] { - true /* record event order - async with ordered output*/, true - /* get state partition - ordered as it may change correlation id - sync */, - false - /* load foreach state - async- unordered completion*/, - orderedPartitionProcessing - /* process Js - unordered/ordered - inherently unordered/ordered completion*/, - true - /* write emits - ordered - async ordered completion*/, - false /* complete item */ - }); - _publisher = publisher; - _pendingEventsThreshold = pendingEventsThreshold; - } + /* record event order - async with ordered output*/ + /* get state partition - ordered as it may change correlation id - sync */ + /* load foreach state - async- unordered completion*/ + /* process Js - unordered/ordered - inherently unordered/ordered completion*/ + /* write emits - ordered - async ordered completion*/ + /* complete item */ - public bool IsRunning { - get { return _isRunning; } - } + public bool IsRunning { get; private set; } public bool ProcessEvent() { var processed = false; - if (_queuePendingEvents.Count > 0) { - processed = ProcessOneEventBatch(); - } else if (_queuePendingEvents.Count == 0 && _subscriptionPaused && !_unsubscribed) { - ResumeSubscription(); + switch (_queuePendingEvents.Count) { + case > 0: + processed = ProcessOneEventBatch(); + break; + case 0 when _subscriptionPaused && !_unsubscribed: + ResumeSubscription(); + break; } return processed; } - public int GetBufferedEventCount() { - return _queuePendingEvents.Count; - } + public int GetBufferedEventCount() => _queuePendingEvents.Count; public void EnqueueTask(WorkItem workItem, CheckpointTag workItemCheckpointTag, bool allowCurrentPosition = false) { @@ -89,17 +85,13 @@ public void InitializeQueue(CheckpointTag startingPosition) { _justInitialized = true; } - public string GetStatus() { - return (_subscriptionPaused ? "/Paused" : ""); - } + public string GetStatus() => _subscriptionPaused ? "/Paused" : ""; private void ValidateQueueingOrder(CheckpointTag eventTag, bool allowCurrentPosition = false) { if (eventTag < _lastEnqueuedEventTag || (!(allowCurrentPosition || _justInitialized) && eventTag <= _lastEnqueuedEventTag)) throw new InvalidOperationException( - string.Format( - "Invalid order. Last known tag is: '{0}'. Current tag is: '{1}'", _lastEnqueuedEventTag, - eventTag)); + $"Invalid order. Last known tag is: '{_lastEnqueuedEventTag}'. Current tag is: '{eventTag}'"); _justInitialized = _justInitialized && (eventTag == _lastEnqueuedEventTag); _lastEnqueuedEventTag = eventTag; } @@ -109,8 +101,7 @@ private void PauseSubscription() { throw new InvalidOperationException("Not subscribed"); if (!_subscriptionPaused && !_unsubscribed) { _subscriptionPaused = true; - _publisher.Publish( - new ReaderSubscriptionManagement.Pause(_subscriptionId)); + publisher.Publish(new ReaderSubscriptionManagement.Pause(_subscriptionId)); } } @@ -119,20 +110,18 @@ private void ResumeSubscription() { throw new InvalidOperationException("Not subscribed"); if (_subscriptionPaused && !_unsubscribed) { _subscriptionPaused = false; - _publisher.Publish( - new ReaderSubscriptionManagement.Resume(_subscriptionId)); + publisher.Publish(new ReaderSubscriptionManagement.Resume(_subscriptionId)); } } private bool _unsubscribed; private Guid _subscriptionId; - private bool _isRunning; private bool ProcessOneEventBatch() { - if (_queuePendingEvents.Count > _pendingEventsThreshold) + if (_queuePendingEvents.Count > pendingEventsThreshold) PauseSubscription(); var processed = _queuePendingEvents.Process(max: 30); - if (_subscriptionPaused && _queuePendingEvents.Count < _pendingEventsThreshold / 2) + if (_subscriptionPaused && _queuePendingEvents.Count < pendingEventsThreshold / 2) ResumeSubscription(); return processed; @@ -151,6 +140,6 @@ public void Subscribed(Guid currentSubscriptionId) { } public void SetIsRunning(bool isRunning) { - _isRunning = isRunning; + IsRunning = isRunning; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedDataEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedDataEvent.cs index 384b5c7d109..03a825b489f 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedDataEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedDataEvent.cs @@ -7,43 +7,26 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -public class EmittedDataEvent : EmittedEvent { - private readonly string _data; - private readonly ExtraMetaData _metadata; - private readonly bool _isJson; - - public EmittedDataEvent( - string streamId, Guid eventId, - string eventType, bool isJson, string data, ExtraMetaData metadata, CheckpointTag causedByTag, - CheckpointTag expectedTag, - Action onCommitted = null) - : base(streamId, eventId, eventType, causedByTag, expectedTag, onCommitted) { - _isJson = isJson; - _data = data; - _metadata = metadata; - } - - public override string Data { - get { return _data; } - } - - public ExtraMetaData Metadata { - get { return _metadata; } - } - - public override bool IsJson { - get { return _isJson; } - } - - public override bool IsReady() { - return true; - } - - public override IEnumerable> ExtraMetaData() { - return _metadata == null ? null : _metadata.Metadata; - } - - public override string ToString() { - return string.Format("Event Id: {0}, Event Type: {1}, Data: {2}", EventId, EventType, Data); - } +public class EmittedDataEvent( + string streamId, + Guid eventId, + string eventType, + bool isJson, + string data, + ExtraMetaData metadata, + CheckpointTag causedByTag, + CheckpointTag expectedTag, + Action onCommitted = null) + : EmittedEvent(streamId, eventId, eventType, causedByTag, expectedTag, onCommitted) { + public override string Data { get; } = data; + + public ExtraMetaData Metadata { get; } = metadata; + + public override bool IsJson { get; } = isJson; + + public override bool IsReady() => true; + + public override IEnumerable> ExtraMetaData() => Metadata?.Metadata; + + public override string ToString() => $"Event Id: {EventId}, Event Type: {EventType}, Data: {Data}"; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEvent.cs index 098cdce743a..c757c7af24f 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEvent.cs @@ -3,68 +3,45 @@ using System; using System.Collections.Generic; +using KurrentDB.Common.Utils; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -public abstract class EmittedEvent { - public readonly string StreamId; - public readonly Guid EventId; - public readonly string EventType; - private readonly CheckpointTag _causedByTag; - private readonly CheckpointTag _expectedTag; - private readonly Action _onCommitted; - private Guid _causedBy; - private string _correlationId; - - protected EmittedEvent( - string streamId, Guid eventId, - string eventType, CheckpointTag causedByTag, CheckpointTag expectedTag, Action onCommitted = null) { - if (causedByTag == null) - throw new ArgumentNullException("causedByTag"); - StreamId = streamId; - EventId = eventId; - EventType = eventType; - _causedByTag = causedByTag; - _expectedTag = expectedTag; - _onCommitted = onCommitted; - } +public abstract class EmittedEvent( + string streamId, + Guid eventId, + string eventType, + CheckpointTag causedByTag, + CheckpointTag expectedTag, + Action onCommitted = null) { + public readonly string StreamId = streamId; + public readonly Guid EventId = eventId; + public readonly string EventType = eventType; public abstract string Data { get; } - public CheckpointTag CausedByTag { - get { return _causedByTag; } - } + public CheckpointTag CausedByTag { get; } = Ensure.NotNull(causedByTag); - public CheckpointTag ExpectedTag { - get { return _expectedTag; } - } + public CheckpointTag ExpectedTag { get; } = expectedTag; - public Action OnCommitted { - get { return _onCommitted; } - } + public Action OnCommitted { get; } = onCommitted; - public Guid CausedBy { - get { return _causedBy; } - } + public Guid CausedBy { get; private set; } - public string CorrelationId { - get { return _correlationId; } - } + public string CorrelationId { get; private set; } public abstract bool IsJson { get; } public abstract bool IsReady(); - public virtual IEnumerable> ExtraMetaData() { - return null; - } + public virtual IEnumerable> ExtraMetaData() => null; public void SetCausedBy(Guid causedBy) { - _causedBy = causedBy; + CausedBy = causedBy; } public void SetCorrelationId(string correlationId) { - _correlationId = correlationId; + CorrelationId = correlationId; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventEnvelope.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventEnvelope.cs index 45bdad32e12..789d545503c 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventEnvelope.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventEnvelope.cs @@ -3,13 +3,7 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -public sealed class EmittedEventEnvelope { - public readonly EmittedEvent Event; - public readonly EmittedStream.WriterConfiguration.StreamMetadata StreamMetadata; - - public EmittedEventEnvelope( - EmittedEvent @event, EmittedStream.WriterConfiguration.StreamMetadata streamMetadata = null) { - Event = @event; - StreamMetadata = streamMetadata; - } +public sealed class EmittedEventEnvelope(EmittedEvent @event, EmittedStream.WriterConfiguration.StreamMetadata streamMetadata = null) { + public readonly EmittedEvent Event = @event; + public readonly EmittedStream.WriterConfiguration.StreamMetadata StreamMetadata = streamMetadata; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventResolutionNeeded.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventResolutionNeeded.cs index 75726198643..b96d3848b92 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventResolutionNeeded.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedEventResolutionNeeded.cs @@ -1,19 +1,13 @@ // Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). -using System; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -sealed class EmittedEventResolutionNeeded : IValidatedEmittedEvent { - public string StreamId { get; } - public long Revision { get; } - public Tuple TopCommitted { get; } - - public EmittedEventResolutionNeeded(string streamId, long revision, Tuple topCommitted) { - StreamId = streamId; - Revision = revision; - TopCommitted = topCommitted; - } +internal sealed class EmittedEventResolutionNeeded(string streamId, long revision, (CheckpointTag, string, long) topCommitted) + : IValidatedEmittedEvent { + public string StreamId { get; } = streamId; + public long Revision { get; } = revision; + public (CheckpointTag, string, long) TopCommitted { get; } = topCommitted; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkTo.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkTo.cs index e0f47d5f364..4300c879f22 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkTo.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkTo.cs @@ -2,48 +2,30 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -public class EmittedLinkTo : EmittedEvent { - private readonly string _targetStreamId; +public class EmittedLinkTo( + string streamId, + Guid eventId, + string targetStreamId, + CheckpointTag causedByTag, + CheckpointTag expectedTag, + Action onCommitted = null) + : EmittedEvent(streamId, eventId, "$>", causedByTag, expectedTag, onCommitted) { private long? _eventNumber; - public EmittedLinkTo( - string streamId, Guid eventId, - string targetStreamId, CheckpointTag causedByTag, CheckpointTag expectedTag, - Action onCommitted = null) - : base(streamId, eventId, "$>", causedByTag, expectedTag, onCommitted) { - _targetStreamId = targetStreamId; - } - - public EmittedLinkTo( - string streamId, Guid eventId, - string targetStreamId, int targetEventNumber, CheckpointTag causedByTag, CheckpointTag expectedTag, - string originalStreamId = null) - : base(streamId, eventId, "$>", causedByTag, expectedTag, null) { - _eventNumber = targetEventNumber; - _targetStreamId = targetStreamId; - } + public override string Data => !IsReady() + ? throw new InvalidOperationException("Link target has not been yet committed") + : $"{_eventNumber.Value.ToString(CultureInfo.InvariantCulture)}@{targetStreamId}"; - public override string Data { - get { - if (!IsReady()) - throw new InvalidOperationException("Link target has not been yet committed"); - return - _eventNumber.Value.ToString(CultureInfo.InvariantCulture) + "@" + _targetStreamId; - } - } + public override bool IsJson => false; - public override bool IsJson { - get { return false; } - } - - public override bool IsReady() { - return _eventNumber != null; - } + [MemberNotNullWhen(true, nameof(_eventNumber))] + public override bool IsReady() => _eventNumber != null; public void SetTargetEventNumber(long eventNumber) { if (_eventNumber != null) diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkToWithRecategorization.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkToWithRecategorization.cs index 2a7876287ca..967b1930b25 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkToWithRecategorization.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/EmittedLinkToWithRecategorization.cs @@ -8,36 +8,25 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -public class EmittedLinkToWithRecategorization : EmittedEvent { - private readonly string _target; - private readonly string _originalStreamId; - private readonly int? _streamDeletedAt; +public class EmittedLinkToWithRecategorization( + string streamId, + Guid eventId, + string target, + CheckpointTag causedByTag, + CheckpointTag expectedTag, + string originalStreamId, + int? streamDeletedAt) + : EmittedEvent(streamId, eventId, "$>", causedByTag, expectedTag) { + public override string Data { get; } = target; - public EmittedLinkToWithRecategorization( - string streamId, Guid eventId, string target, CheckpointTag causedByTag, CheckpointTag expectedTag, - string originalStreamId, int? streamDeletedAt) - : base(streamId, eventId, "$>", causedByTag, expectedTag, null) { - _target = target; - _originalStreamId = originalStreamId; - _streamDeletedAt = streamDeletedAt; - } - - public override string Data { - get { return _target; } - } + public override bool IsJson => false; - public override bool IsJson { - get { return false; } - } - - public override bool IsReady() { - return true; - } + public override bool IsReady() => true; public override IEnumerable> ExtraMetaData() { - if (!string.IsNullOrEmpty(_originalStreamId)) - yield return new KeyValuePair("$o", JsonConvert.ToString(_originalStreamId)); - if (_streamDeletedAt != null) - yield return new KeyValuePair("$deleted", JsonConvert.ToString(_streamDeletedAt.Value)); + if (!string.IsNullOrEmpty(originalStreamId)) + yield return new("$o", JsonConvert.ToString(originalStreamId)); + if (streamDeletedAt != null) + yield return new("$deleted", JsonConvert.ToString(streamDeletedAt.Value)); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ErroredEmittedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ErroredEmittedEvent.cs index 0ce9c9b3636..2e96123adb5 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ErroredEmittedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ErroredEmittedEvent.cs @@ -5,10 +5,6 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -sealed class ErroredEmittedEvent : IValidatedEmittedEvent { - public Exception Exception { get; private set; } - - public ErroredEmittedEvent(InvalidEmittedEventSequenceException exception) { - Exception = exception; - } +internal sealed class ErroredEmittedEvent(InvalidEmittedEventSequenceException exception) : IValidatedEmittedEvent { + public Exception Exception { get; } = exception; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ExtraMetaData.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ExtraMetaData.cs index 9c8771ba24a..727417789d7 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ExtraMetaData.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ExtraMetaData.cs @@ -8,17 +8,13 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; public class ExtraMetaData { - private readonly Dictionary _metadata; - public ExtraMetaData(Dictionary metadata) { - _metadata = metadata.ToDictionary(v => v.Key, v => v.Value.ToString()); + Metadata = metadata.ToDictionary(v => v.Key, v => v.Value.ToString()); } public ExtraMetaData(Dictionary metadata) { - _metadata = metadata.ToDictionary(v => v.Key, v => v.Value); + Metadata = metadata.ToDictionary(v => v.Key, v => v.Value); } - public Dictionary Metadata { - get { return _metadata; } - } + public Dictionary Metadata { get; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IValidatedEmittedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IValidatedEmittedEvent.cs index d4c7942129a..9aca9ed5984 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IValidatedEmittedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IValidatedEmittedEvent.cs @@ -3,4 +3,4 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -interface IValidatedEmittedEvent { } +internal interface IValidatedEmittedEvent; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IgnoredEmittedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IgnoredEmittedEvent.cs index 1077a378cf3..21e6dd905b6 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IgnoredEmittedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/IgnoredEmittedEvent.cs @@ -3,5 +3,4 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -sealed class IgnoredEmittedEvent : IValidatedEmittedEvent { -} +internal sealed class IgnoredEmittedEvent : IValidatedEmittedEvent; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ValidEmittedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ValidEmittedEvent.cs index e6581a73e9f..6b4afccada8 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ValidEmittedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedEvents/ValidEmittedEvent.cs @@ -1,18 +1,8 @@ // Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). -using KurrentDB.Projections.Core.Services.Processing.Checkpointing; - namespace KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; -sealed class ValidEmittedEvent : IValidatedEmittedEvent { - public CheckpointTag Checkpoint { get; private set; } - public string EventType { get; private set; } - public long Revision { get; private set; } - - public ValidEmittedEvent(CheckpointTag checkpoint, string eventType, long revision) { - Checkpoint = checkpoint; - EventType = eventType; - Revision = revision; - } +internal sealed class ValidEmittedEvent(long revision) : IValidatedEmittedEvent { + public long Revision { get; } = revision; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.WriterConfiguration.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.WriterConfiguration.cs index 94565f08797..180290094d5 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.WriterConfiguration.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.WriterConfiguration.cs @@ -9,68 +9,28 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; public partial class EmittedStream { public class WriterConfiguration { - private readonly ClaimsPrincipal _writeAs; - private readonly int _maxWriteBatchLength; - private readonly ILogger _logger; - - private readonly int? maxCount; - private readonly TimeSpan? maxAge; - - private readonly IEmittedStreamsWriter _writer; - - public class StreamMetadata { - private readonly int? _maxCount; - private readonly TimeSpan? _maxAge; - - public StreamMetadata(int? maxCount = null, TimeSpan? maxAge = null) { - _maxCount = maxCount; - _maxAge = maxAge; - } - - public int? MaxCount { - get { return _maxCount; } - } - - public TimeSpan? MaxAge { - get { return _maxAge; } - } + public class StreamMetadata(int? maxCount = null, TimeSpan? maxAge = null) { + public int? MaxCount { get; } = maxCount; + public TimeSpan? MaxAge { get; } = maxAge; } - public WriterConfiguration( - IEmittedStreamsWriter writer, StreamMetadata streamMetadata, ClaimsPrincipal writeAs, + public WriterConfiguration(IEmittedStreamsWriter writer, StreamMetadata streamMetadata, ClaimsPrincipal writeAs, int maxWriteBatchLength, ILogger logger = null) { - _writer = writer; - _writeAs = writeAs; - _maxWriteBatchLength = maxWriteBatchLength; - _logger = logger; + Writer = writer; + WriteAs = writeAs; + MaxWriteBatchLength = maxWriteBatchLength; + Logger = logger; if (streamMetadata != null) { - this.maxCount = streamMetadata.MaxCount; - this.maxAge = streamMetadata.MaxAge; + MaxCount = streamMetadata.MaxCount; + MaxAge = streamMetadata.MaxAge; } } - public ClaimsPrincipal WriteAs { - get { return _writeAs; } - } - - public int MaxWriteBatchLength { - get { return _maxWriteBatchLength; } - } - - public ILogger Logger { - get { return _logger; } - } - - public int? MaxCount { - get { return maxCount; } - } - - public TimeSpan? MaxAge { - get { return maxAge; } - } - - public IEmittedStreamsWriter Writer { - get { return _writer; } - } + public ClaimsPrincipal WriteAs { get; } + public int MaxWriteBatchLength { get; } + public ILogger Logger { get; } + public int? MaxCount { get; } + public TimeSpan? MaxAge { get; } + public IEmittedStreamsWriter Writer { get; } } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.cs index f6e80c1559f..fbcf31a9ad3 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStream.cs @@ -21,11 +21,9 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; -public partial class EmittedStream : IDisposable, - IHandle { +public partial class EmittedStream + : IDisposable, IHandle { private readonly IODispatcher _ioDispatcher; - private readonly IPublisher _publisher; - private readonly ILogger _logger; private readonly string _streamId; private readonly string _metadataStreamId; @@ -36,13 +34,10 @@ public partial class EmittedStream : IDisposable, private readonly CheckpointTag _zeroPosition; private readonly CheckpointTag _fromCheckpointPosition; private readonly IEmittedStreamContainer _readyHandler; - private static readonly string LinkEventType = "$>"; - private static readonly char[] LinkToSeparator = new[] { '@' }; - - private readonly Stack> _alreadyCommittedEvents = - new Stack>(); - - private readonly Queue _pendingWrites = new Queue(); + private const string LinkEventType = "$>"; + private static readonly char[] LinkToSeparator = ['@']; + private readonly Stack<(CheckpointTag, string, long)> _alreadyCommittedEvents = new(); + private readonly Queue _pendingWrites = new(); private bool _checkpointRequested; private bool _awaitingWriteCompleted; @@ -67,27 +62,23 @@ public partial class EmittedStream : IDisposable, private const int MaxRetryCount = 12; private const int MinAttemptWarnThreshold = 5; private Guid _pendingRequestCorrelationId; - private Random _random = new Random(); + private readonly Random _random = new(); public EmittedStream( - string streamId, WriterConfiguration writerConfiguration, ProjectionVersion projectionVersion, - PositionTagger positionTagger, CheckpointTag fromCheckpointPosition, IPublisher publisher, + string streamId, + WriterConfiguration writerConfiguration, + ProjectionVersion projectionVersion, + PositionTagger positionTagger, + CheckpointTag fromCheckpointPosition, IODispatcher ioDispatcher, - IEmittedStreamContainer readyHandler, bool noCheckpoints = false) { - if (string.IsNullOrEmpty(streamId)) - throw new ArgumentNullException("streamId"); - if (writerConfiguration == null) - throw new ArgumentNullException("writerConfiguration"); - if (positionTagger == null) - throw new ArgumentNullException("positionTagger"); - if (fromCheckpointPosition == null) - throw new ArgumentNullException("fromCheckpointPosition"); - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (ioDispatcher == null) - throw new ArgumentNullException("ioDispatcher"); - if (readyHandler == null) - throw new ArgumentNullException("readyHandler"); + IEmittedStreamContainer readyHandler, + bool noCheckpoints = false) { + ArgumentException.ThrowIfNullOrEmpty(streamId); + ArgumentNullException.ThrowIfNull(writerConfiguration); + ArgumentNullException.ThrowIfNull(positionTagger); + ArgumentNullException.ThrowIfNull(fromCheckpointPosition); + ArgumentNullException.ThrowIfNull(ioDispatcher); + ArgumentNullException.ThrowIfNull(readyHandler); _streamId = streamId; _metadataStreamId = SystemStreams.MetastreamOf(streamId); _writerConfiguration = writerConfiguration; @@ -97,7 +88,6 @@ public EmittedStream( _zeroPosition = positionTagger.MakeZeroCheckpointTag(); _fromCheckpointPosition = fromCheckpointPosition; _lastQueuedEventPosition = null; - _publisher = publisher; _ioDispatcher = ioDispatcher; _readyHandler = readyHandler; _maxWriteBatchLength = writerConfiguration.MaxWriteBatchLength; @@ -106,23 +96,21 @@ public EmittedStream( } public void EmitEvents(EmittedEvent[] events) { - if (events == null) - throw new ArgumentNullException("events"); + ArgumentNullException.ThrowIfNull(events); CheckpointTag groupCausedBy = null; foreach (var @event in events) { if (groupCausedBy == null) { groupCausedBy = @event.CausedByTag; if (!(_lastQueuedEventPosition != null && groupCausedBy > _lastQueuedEventPosition) && - !(_lastQueuedEventPosition == null && groupCausedBy >= _fromCheckpointPosition)) + !(_lastQueuedEventPosition == null && groupCausedBy >= _fromCheckpointPosition)) throw new InvalidOperationException( - string.Format("Invalid event order. '{0}' goes after '{1}'", @event.CausedByTag, - _lastQueuedEventPosition)); + $"Invalid event order. '{@event.CausedByTag}' goes after '{_lastQueuedEventPosition}'"); _lastQueuedEventPosition = groupCausedBy; } else if (@event.CausedByTag != groupCausedBy) throw new ArgumentException("events must share the same CausedByTag"); if (@event.StreamId != _streamId) - throw new ArgumentException("Invalid streamId", "events"); + throw new ArgumentException("Invalid streamId", nameof(events)); } EnsureCheckpointNotRequested(); @@ -172,17 +160,14 @@ private void HandleWriteEventsCompleted(ClientMessage.WriteEventsCompleted messa return; } - if (_logger != null) { - _logger.Information("Failed to write events to stream {stream}. Error: {e}", - _streamId, - Enum.GetName(typeof(OperationResult), message.Result)); - } + _logger?.Information("Failed to write events to stream {stream}. Error: {e}", + _streamId, + Enum.GetName(typeof(OperationResult), message.Result)); switch (message.Result) { case OperationResult.WrongExpectedVersion: - RequestRestart(string.Format( - "The '{0}' stream has been written to from the outside. Expected Version: {1}, Current Version: {2}. Checkpoint: {3}.", - _streamId, _lastKnownEventNumber, message.FailureCurrentVersions.Single, _fromCheckpointPosition)); + RequestRestart( + $"The '{_streamId}' stream has been written to from the outside. Expected Version: {_lastKnownEventNumber}, Current Version: {message.FailureCurrentVersions.Single}. Checkpoint: {_fromCheckpointPosition}."); break; case OperationResult.PrepareTimeout: case OperationResult.ForwardTimeout: @@ -190,9 +175,8 @@ private void HandleWriteEventsCompleted(ClientMessage.WriteEventsCompleted messa if (retryCount > 0) { PublishWriteEvents(--retryCount); } else { - Failed(string.Format( - "Failed to write events to {0}. Retry limit of {1} reached. Reason: {2}. Checkpoint: {3}.", - _streamId, MaxRetryCount, message.Result, _fromCheckpointPosition)); + Failed( + $"Failed to write events to {_streamId}. Retry limit of {MaxRetryCount} reached. Reason: {message.Result}. Checkpoint: {_fromCheckpointPosition}."); } break; @@ -235,24 +219,21 @@ private void ReadStreamEventsBackwardCompleted(ClientMessage.ReadStreamEventsBac if (!newPhysicalStream && message.Events is not []) { parsed = message.Events[0].Event.Metadata.ParseCheckpointTagVersionExtraJson(_projectionVersion); if (parsed.Tag == null) { - Failed(string.Format( - "The '{0}' stream managed by projection {1} has been written to from the outside.", - _streamId, _projectionVersion.ProjectionId)); + Failed( + $"The '{_streamId}' stream managed by projection {_projectionVersion.ProjectionId} has been written to from the outside."); return; } if (_projectionVersion.ProjectionId != parsed.Version.ProjectionId) { Failed( - string.Format( - "Multiple projections emitting to the same stream detected. Stream: '{0}'. Last event projection: '{1}'. Emitting projection: '{2}'", - _streamId, parsed.Version.ProjectionId, _projectionVersion.ProjectionId)); + $"Multiple projections emitting to the same stream detected. Stream: '{_streamId}'. Last event projection: '{parsed.Version.ProjectionId}'. Emitting projection: '{_projectionVersion.ProjectionId}'"); return; } } var newLogicalStream = newPhysicalStream - || (_projectionVersion.ProjectionId != parsed.Version.ProjectionId || - _projectionVersion.Epoch > parsed.Version.Version); + || _projectionVersion.ProjectionId != parsed.Version.ProjectionId + || _projectionVersion.Epoch > parsed.Version.Version; _lastKnownEventNumber = newPhysicalStream ? ExpectedVersion.NoStream : message.LastEventNumber; @@ -283,21 +264,20 @@ private void ReadStreamEventsBackwardCompleted(ClientMessage.ReadStreamEventsBac } private bool CollectAlreadyCommittedEvents( - ClientMessage.ReadStreamEventsBackwardCompleted message, CheckpointTag lastCheckpointPosition) { + ClientMessage.ReadStreamEventsBackwardCompleted message, + CheckpointTag lastCheckpointPosition) { var stop = false; foreach (var e in message.Events) { var checkpointTagVersion = e.Event.Metadata.ParseCheckpointTagVersionExtraJson(_projectionVersion); var ourEpoch = checkpointTagVersion.Version.ProjectionId == _projectionVersion.ProjectionId - && checkpointTagVersion.Version.Version >= _projectionVersion.Epoch; + && checkpointTagVersion.Version.Version >= _projectionVersion.Epoch; if (IsV1StreamCreatedEvent(e)) continue; if (checkpointTagVersion.Tag == null) { Failed( - string.Format( - "A unstamped event found. Stream: '{0}'. EventNumber: '{1}'", message.EventStreamId, - e.OriginalEventNumber)); + $"A unstamped event found. Stream: '{message.EventStreamId}'. EventNumber: '{e.OriginalEventNumber}'"); return true; } @@ -310,14 +290,14 @@ private bool CollectAlreadyCommittedEvents( } if (doStop) - // ignore any events prior to the requested lastCheckpointPosition (== first emitted event position) + // ignore any events prior to the requested lastCheckpointPosition (== first emitted event position) { stop = true; break; } var eventType = e.Event.EventType; - _alreadyCommittedEvents.Push(Tuple.Create(checkpointTagVersion.Tag, eventType, e.Event.EventNumber)); + _alreadyCommittedEvents.Push((checkpointTagVersion.Tag, eventType, e.Event.EventNumber)); } return stop || message.IsEndOfStream; @@ -325,13 +305,12 @@ private bool CollectAlreadyCommittedEvents( private static bool IsV1StreamCreatedEvent(KurrentDB.Core.Data.ResolvedEvent e) { return e.Link == null && e.OriginalEventNumber == 0 - && (e.OriginalEvent.EventType == SystemEventTypes.V1__StreamCreatedImplicit__ - || e.OriginalEvent.EventType == SystemEventTypes.V1__StreamCreated__); + && e.OriginalEvent.EventType is SystemEventTypes.V1__StreamCreatedImplicit__ or SystemEventTypes.V1__StreamCreated__; } private void ProcessWrites() { if (_started && !_awaitingListEventsCompleted && !_awaitingWriteCompleted - && !_awaitingMetadataWriteCompleted && _pendingWrites.Count > 0) { + && !_awaitingMetadataWriteCompleted && _pendingWrites.Count > 0) { if (_lastCommittedOrSubmittedEventPosition == null) SubmitListEvents(_fromCheckpointPosition); else @@ -364,13 +343,13 @@ private Action CreateReadTimeoutAction(Guid correlationId, CheckpointTag upTo, l private void SubmitWriteMetadata() { if (_awaitingWriteCompleted || _awaitingMetadataWriteCompleted || _awaitingListEventsCompleted) throw new Exception(); - var streamAcl = _streamId.StartsWith("$") + var streamAcl = _streamId.StartsWith('$') ? new StreamAcl(SystemRoles.All, null, null, SystemRoles.All, null) : new StreamAcl((string)null, null, null, null, null); var streamMetadata = new StreamMetadata( _writerConfiguration.MaxCount, _writerConfiguration.MaxAge, acl: streamAcl, - truncateBefore: _retrievedNextEventNumber == 0 ? (long?)null : _retrievedNextEventNumber); + truncateBefore: _retrievedNextEventNumber == 0 ? null : _retrievedNextEventNumber); _submittedWriteMetaStreamEvent = new Event( Guid.NewGuid(), SystemEventTypes.StreamMetadata, true, streamMetadata.ToJsonBytes()); @@ -392,12 +371,12 @@ private void PublishWriteMetaStream(int retryCount) { if (delayInSeconds == 0) { _writerConfiguration.Writer.WriteEvents( - _metadataStreamId, ExpectedVersion.Any, new Event[] { _submittedWriteMetaStreamEvent }, _writeAs, + _metadataStreamId, ExpectedVersion.Any, [_submittedWriteMetaStreamEvent], _writeAs, m => HandleMetadataWriteCompleted(m, retryCount)); } else { _ioDispatcher.Delay(TimeSpan.FromSeconds(delayInSeconds), _ => _writerConfiguration.Writer.WriteEvents( - _metadataStreamId, ExpectedVersion.Any, new Event[] { _submittedWriteMetaStreamEvent }, _writeAs, + _metadataStreamId, ExpectedVersion.Any, [_submittedWriteMetaStreamEvent], _writeAs, m => HandleMetadataWriteCompleted(m, retryCount))); } } @@ -414,16 +393,13 @@ private void HandleMetadataWriteCompleted(ClientMessage.WriteEventsCompleted mes return; } - if (_logger != null) { - _logger.Information("Failed to write events to stream {stream}. Error: {e}", - _metadataStreamId, - Enum.GetName(typeof(OperationResult), message.Result)); - } + _logger?.Information("Failed to write events to stream {stream}. Error: {e}", + _metadataStreamId, + Enum.GetName(typeof(OperationResult), message.Result)); switch (message.Result) { case OperationResult.WrongExpectedVersion: - RequestRestart(string.Format("The '{0}' stream has been written to from the outside", - _metadataStreamId)); + RequestRestart($"The '{_metadataStreamId}' stream has been written to from the outside"); break; case OperationResult.PrepareTimeout: case OperationResult.ForwardTimeout: @@ -431,9 +407,8 @@ private void HandleMetadataWriteCompleted(ClientMessage.WriteEventsCompleted mes if (retryCount > 0) { PublishWriteMetaStream(--retryCount); } else { - Failed(string.Format( - "Failed to write an events to {0}. Retry limit of {1} reached. Reason: {2}", - _metadataStreamId, MaxRetryCount, message.Result)); + Failed( + $"Failed to write an events to {_metadataStreamId}. Retry limit of {MaxRetryCount} reached. Reason: {message.Result}"); } break; @@ -454,8 +429,7 @@ private void SubmitWriteEvents() { var e = _pendingWrites.Peek(); if (!e.IsReady()) { _readyHandler.Handle( - new CoreProjectionProcessingMessage.EmittedStreamAwaiting( - _streamId, new SendToThisEnvelope(this))); + new CoreProjectionProcessingMessage.EmittedStreamAwaiting(_streamId, new SendToThisEnvelope(this))); _awaitingReady = true; break; } @@ -467,9 +441,7 @@ private void SubmitWriteEvents() { if (expectedTag != null) if (DetectConcurrencyViolations(expectedTag)) { RequestRestart( - string.Format( - "Wrong expected tag while submitting write event request to the '{0}' stream. The last known stream tag is: '{1}' the expected tag is: '{2}'", - _streamId, _lastCommittedOrSubmittedEventPosition, expectedTag)); + $"Wrong expected tag while submitting write event request to the '{_streamId}' stream. The last known stream tag is: '{_lastCommittedOrSubmittedEventPosition}' the expected tag is: '{expectedTag}'"); return; } @@ -481,8 +453,7 @@ private void SubmitWriteEvents() { isPropertyMetadata: false, e.CausedByTag.ToJsonBytes(_projectionVersion, MetadataWithCausedByAndCorrelationId(e)))); } catch (ArgumentException ex) { - Failed(string.Format("Failed to write the event: {0} to stream: {1} failed. Reason: {2}.", e, - _streamId, ex.Message)); + Failed($"Failed to write the event: {e} to stream: {_streamId} failed. Reason: {ex.Message}."); return; } @@ -496,26 +467,23 @@ private void SubmitWriteEvents() { PublishWriteEvents(MaxRetryCount); } - private IEnumerable> MetadataWithCausedByAndCorrelationId( - EmittedEvent emittedEvent) { + private static IEnumerable> MetadataWithCausedByAndCorrelationId(EmittedEvent emittedEvent) { var extraMetaData = emittedEvent.ExtraMetaData(); var correlationIdFound = false; if (extraMetaData != null) foreach (var valuePair in from pair in extraMetaData - where pair.Key != "$causedBy" - select pair) { + where pair.Key != "$causedBy" + select pair) { if (valuePair.Key == "$correlationId") correlationIdFound = true; - yield return new KeyValuePair(valuePair.Key, new JRaw(valuePair.Value)); + yield return new(valuePair.Key, new JRaw(valuePair.Value)); } if (emittedEvent.CausedBy != Guid.Empty) yield return - new KeyValuePair( - "$causedBy", JValue.CreateString(emittedEvent.CausedBy.ToString("D"))); + new("$causedBy", JValue.CreateString(emittedEvent.CausedBy.ToString("D"))); if (!correlationIdFound && !string.IsNullOrEmpty(emittedEvent.CorrelationId)) - yield return - new KeyValuePair("$correlationId", JValue.CreateString(emittedEvent.CorrelationId)); + yield return new("$correlationId", JValue.CreateString(emittedEvent.CorrelationId)); } private bool DetectConcurrencyViolations(CheckpointTag expectedTag) { @@ -543,9 +511,7 @@ private void PublishWriteEvents(int retryCount) { var delayInSeconds = CalculateBackoffTimeSecs(attempt); if (attempt >= MinAttemptWarnThreshold && _logger != null) { _logger.Warning("Attempt: {attempt} to write events to stream {stream}. Backing off for {time} second(s).", - attempt, - _streamId, - delayInSeconds); + attempt, _streamId, delayInSeconds); } if (delayInSeconds == 0) { @@ -590,7 +556,7 @@ private void NotifyWriteCompleted() { private void ProcessRequestedCheckpoint() { if (_checkpointRequested && !_awaitingWriteCompleted && !_awaitingMetadataWriteCompleted - && _pendingWrites.Count == 0) { + && _pendingWrites.Count == 0) { EnsureCheckpointsEnabled(); _readyHandler.Handle(new CoreProjectionProcessingMessage.ReadyForCheckpoint(this)); } @@ -612,7 +578,7 @@ private void SubmitWriteEventsInRecoveryLoop(bool anyFound) { while (_pendingWrites.Count > 0) { var eventToWrite = _pendingWrites.Peek(); if (eventToWrite.CausedByTag > _lastCommittedOrSubmittedEventPosition || - _alreadyCommittedEvents.Count == 0) + _alreadyCommittedEvents.Count == 0) RecoveryCompleted(); if (_recoveryCompleted) { if (anyFound) @@ -623,18 +589,18 @@ private void SubmitWriteEventsInRecoveryLoop(bool anyFound) { var report = ValidateEmittedEventInRecoveryMode(eventToWrite); - if (report is IgnoredEmittedEvent) { - Log.Verbose($"Emitted event ignored because it links to an event that no longer exists: eventId: {eventToWrite.EventId}, eventType: {eventToWrite.EventId}, checkpoint: {eventToWrite.CorrelationId}, causedBy: {eventToWrite.CausedBy}"); - continue; - } - - if (report is ErroredEmittedEvent error) - throw error.Exception; - - if (report is ValidEmittedEvent valid) { - anyFound = true; - NotifyEventCommitted(eventToWrite, valid.Revision); - _pendingWrites.Dequeue(); + switch (report) { + case IgnoredEmittedEvent: + Log.Verbose( + $"Emitted event ignored because it links to an event that no longer exists: eventId: {eventToWrite.EventId}, eventType: {eventToWrite.EventId}, checkpoint: {eventToWrite.CorrelationId}, causedBy: {eventToWrite.CausedBy}"); + continue; + case ErroredEmittedEvent error: + throw error.Exception; + case ValidEmittedEvent valid: + anyFound = true; + NotifyEventCommitted(eventToWrite, valid.Revision); + _pendingWrites.Dequeue(); + break; } if (report is EmittedEventResolutionNeeded resolution) { @@ -661,30 +627,35 @@ private IValidatedEmittedEvent ValidateEmittedEventInRecoveryMode(EmittedEvent e return new IgnoredEmittedEvent(); var failed = topAlreadyCommitted.Item1 != eventToWrite.CausedByTag || - topAlreadyCommitted.Item2 != eventToWrite.EventType; - - if (failed && eventToWrite.EventType.Equals(LinkEventType)) { - // We check if the linked event still exists. If not, we skip that emitted event. - var parts = eventToWrite.Data.Split(LinkToSeparator, 2); - var streamId = parts[1]; - if (!long.TryParse(parts[0], out long eventNumber)) - throw new Exception($"Unexpected exception: Emitted event is an invalid link event: Body ({eventToWrite.Data}) CausedByTag ({eventToWrite.CausedByTag}) StreamId ({eventToWrite.StreamId})"); - - return new EmittedEventResolutionNeeded(streamId, eventNumber, topAlreadyCommitted); - } - - if (failed) { - var error = CreateSequenceException(topAlreadyCommitted, eventToWrite); - return new ErroredEmittedEvent(error); + topAlreadyCommitted.Item2 != eventToWrite.EventType; + + switch (failed) { + case true when eventToWrite.EventType.Equals(LinkEventType): { + // We check if the linked event still exists. If not, we skip that emitted event. + var parts = eventToWrite.Data.Split(LinkToSeparator, 2); + var streamId = parts[1]; + return !long.TryParse(parts[0], out long eventNumber) + ? throw new Exception( + $"Unexpected exception: Emitted event is an invalid link event: Body ({eventToWrite.Data}) CausedByTag ({eventToWrite.CausedByTag}) StreamId ({eventToWrite.StreamId})") + : new EmittedEventResolutionNeeded(streamId, eventNumber, topAlreadyCommitted); + } + case true: { + var error = CreateSequenceException(topAlreadyCommitted, eventToWrite); + return new ErroredEmittedEvent(error); + } + default: + return new ValidEmittedEvent(topAlreadyCommitted.Item3); } - - return new ValidEmittedEvent(topAlreadyCommitted.Item1, topAlreadyCommitted.Item2, topAlreadyCommitted.Item3); } // Used when we need to resolve a link event to see if it points to an event that no longer exists. If that // event no longer exists, we skip it and resume the recovery process. - private void OnEmittedLinkEventResolved(bool anyFound, EmittedEvent eventToWrite, Tuple topAlreadyCommitted, ClientMessage.ReadEventCompleted resp) { - if (resp.Result != ReadEventResult.StreamDeleted && resp.Result != ReadEventResult.NotFound && resp.Result != ReadEventResult.NoStream && resp.Result != ReadEventResult.Success) { + private void OnEmittedLinkEventResolved(bool anyFound, + EmittedEvent eventToWrite, + (CheckpointTag, string, long) topAlreadyCommitted, + ClientMessage.ReadEventCompleted resp) { + if (resp.Result != ReadEventResult.StreamDeleted && resp.Result != ReadEventResult.NotFound && + resp.Result != ReadEventResult.NoStream && resp.Result != ReadEventResult.Success) { throw CreateSequenceException(topAlreadyCommitted, eventToWrite); } @@ -692,16 +663,18 @@ private void OnEmittedLinkEventResolved(bool anyFound, EmittedEvent eventToWrite anyFound = true; NotifyEventCommitted(eventToWrite, topAlreadyCommitted.Item3); } else { - Log.Verbose($"Emitted event ignored after resolution because it links to an event that no longer exists: eventId: {eventToWrite.EventId}, eventType: {eventToWrite.EventId}, checkpoint: {eventToWrite.CorrelationId}, causedBy: {eventToWrite.CausedBy}"); + Log.Verbose( + $"Emitted event ignored after resolution because it links to an event that no longer exists: eventId: {eventToWrite.EventId}, eventType: {eventToWrite.EventId}, checkpoint: {eventToWrite.CorrelationId}, causedBy: {eventToWrite.CausedBy}"); } + _pendingWrites.Dequeue(); _awaitingLinkToResolution = false; SubmitWriteEventsInRecoveryLoop(anyFound); } - private InvalidEmittedEventSequenceException CreateSequenceException( - Tuple committed, EmittedEvent eventToWrite) { - return new InvalidEmittedEventSequenceException( + private InvalidEmittedEventSequenceException + CreateSequenceException((CheckpointTag, string, long) committed, EmittedEvent eventToWrite) { + return new( $"An event emitted in recovery for stream {_streamId} differs from the originally emitted event. Existing('{committed.Item2}', '{committed.Item1}'). New('{eventToWrite.EventType}', '{eventToWrite.CausedByTag}')" ); } @@ -717,8 +690,7 @@ private static void NotifyEventsCommitted(EmittedEvent[] events, long firstEvent } private static void NotifyEventCommitted(EmittedEvent @event, long eventNumber) { - if (@event.OnCommitted != null) - @event.OnCommitted(eventNumber); + @event.OnCommitted?.Invoke(eventNumber); } public void Dispose() { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs index be9926db7c9..c274651317d 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs @@ -12,25 +12,19 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; -public class EmittedStreamsDeleter : IEmittedStreamsDeleter { +public class EmittedStreamsDeleter( + IODispatcher ioDispatcher, + string emittedStreamsId, + string emittedStreamsCheckpointStreamId) + : IEmittedStreamsDeleter { private static readonly ILogger Log = Serilog.Log.ForContext(); - private readonly IODispatcher _ioDispatcher; - private readonly int _checkPointThreshold = 4000; - private int _numberOfEventsProcessed = 0; + private const int CheckPointThreshold = 4000; + private int _numberOfEventsProcessed; private const int RetryLimit = 3; private int _retryCount = RetryLimit; - private readonly string _emittedStreamsId; - private readonly string _emittedStreamsCheckpointStreamId; - - public EmittedStreamsDeleter(IODispatcher ioDispatcher, string emittedStreamsId, - string emittedStreamsCheckpointStreamId) { - _ioDispatcher = ioDispatcher; - _emittedStreamsId = emittedStreamsId; - _emittedStreamsCheckpointStreamId = emittedStreamsCheckpointStreamId; - } public void DeleteEmittedStreams(Action onEmittedStreamsDeleted) { - _ioDispatcher.ReadBackward(_emittedStreamsCheckpointStreamId, -1, 1, false, SystemAccounts.System, + ioDispatcher.ReadBackward(emittedStreamsCheckpointStreamId, -1, 1, false, SystemAccounts.System, result => { var deleteFromPosition = GetPositionToDeleteFrom(result); DeleteEmittedStreamsFrom(deleteFromPosition, onEmittedStreamsDeleted); @@ -39,7 +33,7 @@ public void DeleteEmittedStreams(Action onEmittedStreamsDeleted) { Guid.NewGuid()); } - private int GetPositionToDeleteFrom(ClientMessage.ReadStreamEventsBackwardCompleted onReadCompleted) { + private static int GetPositionToDeleteFrom(ClientMessage.ReadStreamEventsBackwardCompleted onReadCompleted) { int deleteFromPosition = 0; if (onReadCompleted.Result is ReadStreamResult.Success) { if (onReadCompleted.Events is not []) { @@ -56,7 +50,7 @@ private int GetPositionToDeleteFrom(ClientMessage.ReadStreamEventsBackwardComple } private void DeleteEmittedStreamsFrom(long fromPosition, Action onEmittedStreamsDeleted) { - _ioDispatcher.ReadForward(_emittedStreamsId, fromPosition, 1, false, SystemAccounts.System, + ioDispatcher.ReadForward(emittedStreamsId, fromPosition, 1, false, SystemAccounts.System, x => ReadCompleted(x, onEmittedStreamsDeleted), () => DeleteEmittedStreamsFrom(fromPosition, onEmittedStreamsDeleted), Guid.NewGuid()); @@ -65,65 +59,70 @@ private void DeleteEmittedStreamsFrom(long fromPosition, Action onEmittedStreams private void ReadCompleted(ClientMessage.ReadStreamEventsForwardCompleted onReadCompleted, Action onEmittedStreamsDeleted) { if (onReadCompleted.Result is ReadStreamResult.Success or ReadStreamResult.NoStream) { - if (onReadCompleted.Events is [] && !onReadCompleted.IsEndOfStream) { - DeleteEmittedStreamsFrom(onReadCompleted.NextEventNumber, onEmittedStreamsDeleted); - return; - } - - if (onReadCompleted.Events is []) { - _ioDispatcher.DeleteStream(_emittedStreamsCheckpointStreamId, ExpectedVersion.Any, false, - SystemAccounts.System, x => { - // currently, WrongExpectedVersion is returned when deleting non-existing streams, even when specifying ExpectedVersion.Any. - // it is not too intuitive but changing the response would break the contract and compatibility with TCP/gRPC/web clients or require adding a new error code to all clients. - // note: we don't need to check if CurrentVersion == -1 here to make sure it's a non-existing stream since the deletion is done with ExpectedVersion.Any - if (x.Result == OperationResult.WrongExpectedVersion) { - // stream was never created - Log.Information("PROJECTIONS: Projection Stream '{stream}' was not deleted since it does not exist", _emittedStreamsCheckpointStreamId); - } else if (x.Result == OperationResult.Success || x.Result == OperationResult.StreamDeleted) { - Log.Information("PROJECTIONS: Projection Stream '{stream}' deleted", - _emittedStreamsCheckpointStreamId); - } else { - Log.Error("PROJECTIONS: Failed to delete projection stream '{stream}'. Reason: {e}", - _emittedStreamsCheckpointStreamId, x.Result); - } - - _ioDispatcher.DeleteStream(_emittedStreamsId, ExpectedVersion.Any, false, - SystemAccounts.System, y => { + switch (onReadCompleted.Events) { + case [] when !onReadCompleted.IsEndOfStream: + DeleteEmittedStreamsFrom(onReadCompleted.NextEventNumber, onEmittedStreamsDeleted); + return; + case []: + ioDispatcher.DeleteStream(emittedStreamsCheckpointStreamId, ExpectedVersion.Any, false, + SystemAccounts.System, x => { + switch (x.Result) { // currently, WrongExpectedVersion is returned when deleting non-existing streams, even when specifying ExpectedVersion.Any. // it is not too intuitive but changing the response would break the contract and compatibility with TCP/gRPC/web clients or require adding a new error code to all clients. // note: we don't need to check if CurrentVersion == -1 here to make sure it's a non-existing stream since the deletion is done with ExpectedVersion.Any - if (x.Result == OperationResult.WrongExpectedVersion) { + case OperationResult.WrongExpectedVersion: // stream was never created - Log.Information("PROJECTIONS: Projection Stream '{stream}' was not deleted since it does not exist", _emittedStreamsId); - } else if (y.Result == OperationResult.Success || - y.Result == OperationResult.StreamDeleted) { + Log.Information("PROJECTIONS: Projection Stream '{stream}' was not deleted since it does not exist", emittedStreamsCheckpointStreamId); + break; + case OperationResult.Success: + case OperationResult.StreamDeleted: Log.Information("PROJECTIONS: Projection Stream '{stream}' deleted", - _emittedStreamsId); - } else { - Log.Error( - "PROJECTIONS: Failed to delete projection stream '{stream}'. Reason: {e}", - _emittedStreamsId, y.Result); - } - - onEmittedStreamsDeleted(); - }); - }); - } else { - var streamId = Helper.UTF8NoBom.GetString(onReadCompleted.Events[0].Event.Data.Span); - _ioDispatcher.DeleteStream(streamId, ExpectedVersion.Any, false, SystemAccounts.System, - x => DeleteStreamCompleted(x, onEmittedStreamsDeleted, streamId, - onReadCompleted.Events[0].OriginalEventNumber)); + emittedStreamsCheckpointStreamId); + break; + default: + Log.Error("PROJECTIONS: Failed to delete projection stream '{stream}'. Reason: {e}", + emittedStreamsCheckpointStreamId, x.Result); + break; + } + + ioDispatcher.DeleteStream(emittedStreamsId, ExpectedVersion.Any, false, + SystemAccounts.System, y => { + // currently, WrongExpectedVersion is returned when deleting non-existing streams, even when specifying ExpectedVersion.Any. + // it is not too intuitive but changing the response would break the contract and compatibility with TCP/gRPC/web clients or require adding a new error code to all clients. + // note: we don't need to check if CurrentVersion == -1 here to make sure it's a non-existing stream since the deletion is done with ExpectedVersion.Any + if (x.Result == OperationResult.WrongExpectedVersion) { + // stream was never created + Log.Information("PROJECTIONS: Projection Stream '{stream}' was not deleted since it does not exist", emittedStreamsId); + } else if (y.Result is OperationResult.Success or OperationResult.StreamDeleted) { + Log.Information("PROJECTIONS: Projection Stream '{stream}' deleted", + emittedStreamsId); + } else { + Log.Error( + "PROJECTIONS: Failed to delete projection stream '{stream}'. Reason: {e}", + emittedStreamsId, y.Result); + } + + onEmittedStreamsDeleted(); + }); + }); + break; + default: { + var streamId = Helper.UTF8NoBom.GetString(onReadCompleted.Events[0].Event.Data.Span); + ioDispatcher.DeleteStream(streamId, ExpectedVersion.Any, false, SystemAccounts.System, + x => DeleteStreamCompleted(x, onEmittedStreamsDeleted, streamId, + onReadCompleted.Events[0].OriginalEventNumber)); + break; + } } } } private void DeleteStreamCompleted(ClientMessage.DeleteStreamCompleted deleteStreamCompleted, Action onEmittedStreamsDeleted, string streamId, long eventNumber) { - if (deleteStreamCompleted.Result == OperationResult.Success || - deleteStreamCompleted.Result == OperationResult.StreamDeleted) { + if (deleteStreamCompleted.Result is OperationResult.Success or OperationResult.StreamDeleted) { _retryCount = RetryLimit; _numberOfEventsProcessed++; - if (_numberOfEventsProcessed >= _checkPointThreshold) { + if (_numberOfEventsProcessed >= CheckPointThreshold) { _numberOfEventsProcessed = 0; TryMarkCheckpoint(eventNumber); } @@ -141,14 +140,14 @@ private void DeleteStreamCompleted(ClientMessage.DeleteStreamCompleted deleteStr Log.Error( "PROJECTIONS: Failed to delete emitted stream {stream}, Retrying ({retryCount}/{maxRetryCount}). Reason: {reason}", - streamId, (RetryLimit - _retryCount) + 1, RetryLimit, deleteStreamCompleted.Result); + streamId, RetryLimit - _retryCount + 1, RetryLimit, deleteStreamCompleted.Result); _retryCount--; DeleteEmittedStreamsFrom(eventNumber, onEmittedStreamsDeleted); } } private void TryMarkCheckpoint(long eventNumber) { - _ioDispatcher.WriteEvent(_emittedStreamsCheckpointStreamId, ExpectedVersion.Any, + ioDispatcher.WriteEvent(emittedStreamsCheckpointStreamId, ExpectedVersion.Any, new Event(Guid.NewGuid(), ProjectionEventTypes.PartitionCheckpoint, true, eventNumber.ToJson(), null), SystemAccounts.System, x => { if (x.Result == OperationResult.Success) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsTracker.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsTracker.cs index d4a2ef08e42..041bacbe265 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsTracker.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsTracker.cs @@ -14,31 +14,25 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; -public class EmittedStreamsTracker : IEmittedStreamsTracker { +public class EmittedStreamsTracker( + IODispatcher ioDispatcher, + ProjectionConfig projectionConfig, + ProjectionNamesBuilder projectionNamesBuilder) + : IEmittedStreamsTracker { private static readonly ILogger Log = Serilog.Log.ForContext(); - private readonly IODispatcher _ioDispatcher; - private readonly ProjectionConfig _projectionConfig; - private readonly ProjectionNamesBuilder _projectionNamesBuilder; - private readonly BoundedCache _streamIdCache = new BoundedCache(int.MaxValue, + private readonly BoundedCache _streamIdCache = new(int.MaxValue, ESConsts.CommittedEventsMemCacheLimit, x => 16 + 4 + IntPtr.Size + 2 * x.Length); private const int MaxRetryCount = 3; - private readonly object _locker = new object(); - - public EmittedStreamsTracker(IODispatcher ioDispatcher, ProjectionConfig projectionConfig, - ProjectionNamesBuilder projectionNamesBuilder) { - _ioDispatcher = ioDispatcher; - _projectionConfig = projectionConfig; - _projectionNamesBuilder = projectionNamesBuilder; - } + private readonly object _locker = new(); public void Initialize() { ReadEmittedStreamStreamIdsIntoCache(0); //start from the beginning } private void ReadEmittedStreamStreamIdsIntoCache(long position) { - _ioDispatcher.ReadForward(_projectionNamesBuilder.GetEmittedStreamsName(), position, 1, false, + ioDispatcher.ReadForward(projectionNamesBuilder.GetEmittedStreamsName(), position, 1, false, SystemAccounts.System, x => { if (x.Events is not []) { for (int i = 0; i < x.Events.Count; i++) { @@ -55,16 +49,15 @@ private void ReadEmittedStreamStreamIdsIntoCache(long position) { }, () => { Log.Error( "Timed out reading emitted stream ids into cache from {streamName} at position {position}.", - _projectionNamesBuilder.GetEmittedStreamsName(), position); + projectionNamesBuilder.GetEmittedStreamsName(), position); }, Guid.NewGuid()); } public void TrackEmittedStream(EmittedEvent[] emittedEvents) { - if (!_projectionConfig.TrackEmittedStreams) + if (!projectionConfig.TrackEmittedStreams) return; foreach (var emittedEvent in emittedEvents) { - string streamId; - if (!_streamIdCache.TryGetRecord(emittedEvent.StreamId, out streamId)) { + if (!_streamIdCache.TryGetRecord(emittedEvent.StreamId, out _)) { var trackEvent = new Event(Guid.NewGuid(), ProjectionEventTypes.StreamTracked, false, Helper.UTF8NoBom.GetBytes(emittedEvent.StreamId)); lock (_locker) { @@ -77,7 +70,7 @@ public void TrackEmittedStream(EmittedEvent[] emittedEvents) { } private void WriteEvent(Event evnt, int retryCount) { - _ioDispatcher.WriteEvent(_projectionNamesBuilder.GetEmittedStreamsName(), ExpectedVersion.Any, evnt, + ioDispatcher.WriteEvent(projectionNamesBuilder.GetEmittedStreamsName(), ExpectedVersion.Any, evnt, SystemAccounts.System, x => OnWriteComplete(x, evnt, Helper.UTF8NoBom.GetString(evnt.Data), retryCount)); } @@ -90,7 +83,7 @@ private void OnWriteComplete(ClientMessage.WriteEventsCompleted completed, Event } else { Log.Error( "PROJECTIONS: Failed to write a tracked stream id of {stream} to the {emittedStream} stream. Retry limit of {maxRetryCount} reached. Reason: {e}", - streamId, _projectionNamesBuilder.GetEmittedStreamsName(), MaxRetryCount, completed.Result); + streamId, projectionNamesBuilder.GetEmittedStreamsName(), MaxRetryCount, completed.Result); } } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsWriter.cs index 32a9f7b1723..f350bc8c69a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsWriter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/EmittedStreamsWriter.cs @@ -9,15 +9,9 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; -public class EmittedStreamsWriter : IEmittedStreamsWriter { - private IODispatcher _ioDispatcher; - - public EmittedStreamsWriter(IODispatcher ioDispatcher) { - _ioDispatcher = ioDispatcher; - } - +public class EmittedStreamsWriter(IODispatcher ioDispatcher) : IEmittedStreamsWriter { public void WriteEvents(string streamId, long expectedVersion, Event[] events, ClaimsPrincipal writeAs, Action complete) { - _ioDispatcher.WriteEvents(streamId, expectedVersion, events, writeAs, complete); + ioDispatcher.WriteEvents(streamId, expectedVersion, events, writeAs, complete); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/InvalidEmittedEventSequenceException.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/InvalidEmittedEventSequenceException.cs index b77f5100747..93b4ad684e2 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/InvalidEmittedEventSequenceException.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/InvalidEmittedEventSequenceException.cs @@ -5,8 +5,4 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; -class InvalidEmittedEventSequenceException : Exception { - public InvalidEmittedEventSequenceException(string message) - : base(message) { - } -} +public class InvalidEmittedEventSequenceException(string message) : Exception(message); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/QueuedEmittedStreamsWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/QueuedEmittedStreamsWriter.cs index 25ba1495661..c29d38a0cae 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/QueuedEmittedStreamsWriter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/QueuedEmittedStreamsWriter.cs @@ -9,17 +9,9 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; -public class QueuedEmittedStreamsWriter : IEmittedStreamsWriter { - private IODispatcher _ioDispatcher; - private Guid _writeQueueId; - - public QueuedEmittedStreamsWriter(IODispatcher ioDispatcher, Guid writeQueueId) { - _ioDispatcher = ioDispatcher; - _writeQueueId = writeQueueId; - } - +public class QueuedEmittedStreamsWriter(IODispatcher ioDispatcher, Guid writeQueueId) : IEmittedStreamsWriter { public void WriteEvents(string streamId, long expectedVersion, Event[] events, ClaimsPrincipal writeAs, Action complete) { - _ioDispatcher.QueueWriteEvents(_writeQueueId, streamId, expectedVersion, events, writeAs, complete); + ioDispatcher.QueueWriteEvents(writeQueueId, streamId, expectedVersion, events, writeAs, complete); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/ResultEventEmitter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/ResultEventEmitter.cs index 79e525782f5..ee592899942 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Emitting/ResultEventEmitter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Emitting/ResultEventEmitter.cs @@ -9,13 +9,10 @@ namespace KurrentDB.Projections.Core.Services.Processing.Emitting; public class ResultEventEmitter : IResultEventEmitter { private readonly ProjectionNamesBuilder _namesBuilder; - - private readonly EmittedStream.WriterConfiguration.StreamMetadata _resultStreamMetadata = - new EmittedStream.WriterConfiguration.StreamMetadata( /* TBD */); + private readonly EmittedStream.WriterConfiguration.StreamMetadata _resultStreamMetadata = new( /* TBD */); public ResultEventEmitter(ProjectionNamesBuilder namesBuilder) { - if (namesBuilder == null) - throw new ArgumentNullException("namesBuilder"); + ArgumentNullException.ThrowIfNull(namesBuilder); _namesBuilder = namesBuilder; } @@ -23,34 +20,29 @@ public EmittedEventEnvelope[] ResultUpdated(string partition, string result, Che return CreateResultUpdatedEvents(partition, result, at); } - private EmittedEventEnvelope[] CreateResultUpdatedEvents(string partition, string projectionResult, - CheckpointTag at) { + private EmittedEventEnvelope[] CreateResultUpdatedEvents(string partition, string projectionResult, CheckpointTag at) { var streamId = _namesBuilder.MakePartitionResultStreamName(partition); - var allResultsStreamId = _namesBuilder.GetResultStreamName(); if (string.IsNullOrEmpty(partition)) { var result = new EmittedEventEnvelope( projectionResult == null - ? new EmittedDataEvent( - streamId, Guid.NewGuid(), "ResultRemoved", true, null, null, at, null) - : new EmittedDataEvent( - streamId, Guid.NewGuid(), "Result", true, projectionResult, null, at, null), + ? new EmittedDataEvent(streamId, Guid.NewGuid(), "ResultRemoved", true, null, null, at, null) + : new(streamId, Guid.NewGuid(), "Result", true, projectionResult, null, at, null), _resultStreamMetadata); - return new[] { result }; + return [result]; } else { + var allResultsStreamId = _namesBuilder.GetResultStreamName(); var linkTo = new EmittedLinkTo(allResultsStreamId, Guid.NewGuid(), streamId, at, null); var linkToEnvelope = new EmittedEventEnvelope(linkTo, _resultStreamMetadata); var result = new EmittedEventEnvelope( projectionResult == null ? new EmittedDataEvent( - streamId, Guid.NewGuid(), "ResultRemoved", true, null, null, at, null, - linkTo.SetTargetEventNumber) - : new EmittedDataEvent( - streamId, Guid.NewGuid(), "Result", true, projectionResult, null, at, null, - linkTo.SetTargetEventNumber), _resultStreamMetadata); - return new[] { result, linkToEnvelope }; + streamId, Guid.NewGuid(), "ResultRemoved", true, null, null, at, null, linkTo.SetTargetEventNumber) + : new(streamId, Guid.NewGuid(), "Result", true, projectionResult, null, at, null, linkTo.SetTargetEventNumber), + _resultStreamMetadata); + return [result, linkToEnvelope]; } } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventFilter.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventFilter.cs index 496f4d5150a..51e0b73449f 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventFilter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventFilter.cs @@ -10,23 +10,14 @@ namespace KurrentDB.Projections.Core.Services.Processing.EventByType; public class EventByTypeIndexEventFilter : EventFilter { private readonly HashSet _streams; - public EventByTypeIndexEventFilter(HashSet events) - : base(false, false, events) { - _streams = new HashSet(from eventType in events - select "$et-" + eventType); + public EventByTypeIndexEventFilter(HashSet events) : base(false, false, events) { + _streams = new(events.Select(eventType => $"$et-{eventType}")); } - protected override bool DeletedNotificationPasses(string positionStreamId) { - return true; - } + protected override bool DeletedNotificationPasses(string positionStreamId) => true; - public override bool PassesSource(bool resolvedFromLinkTo, string positionStreamId, string eventType) { - if (_streams.Contains(positionStreamId)) - return true; - return !resolvedFromLinkTo && !SystemStreams.IsSystemStream(positionStreamId); - } + public override bool PassesSource(bool resolvedFromLinkTo, string positionStreamId, string eventType) + => _streams.Contains(positionStreamId) || !resolvedFromLinkTo && !SystemStreams.IsSystemStream(positionStreamId); - public override string GetCategory(string positionStreamId) { - return null; - } + public override string GetCategory(string positionStreamId) => null; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.IndexBased.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.IndexBased.cs index 019afe467ce..665a5c1e947 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.IndexBased.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.IndexBased.cs @@ -18,48 +18,42 @@ namespace KurrentDB.Projections.Core.Services.Processing.EventByType; public partial class EventByTypeIndexEventReader { - private class IndexBased : State, - IHandle, - IHandle, - IHandle { + private class IndexBased + : State, + IHandle, + IHandle, + IHandle { private readonly Dictionary _streamToEventType; - private readonly HashSet _eventsRequested = new HashSet(); - private readonly HashSet _validRequests = new HashSet(); + private readonly HashSet _eventsRequested = []; + private readonly HashSet _validRequests = []; private bool _indexCheckpointStreamRequested; private long _lastKnownIndexCheckpointEventNumber = -1; - private TFPos? _lastKnownIndexCheckpointPosition = null; - - private readonly Dictionary> _buffers = - new Dictionary>(); + private TFPos? _lastKnownIndexCheckpointPosition; + private readonly Dictionary> _buffers = new(); private readonly Dictionary _eofs; private bool _disposed; - private bool _indexStreamEof = false; + private bool _indexStreamEof; private readonly IPublisher _publisher; private readonly Dictionary _pendingRequests; - private readonly object _lock = new object(); + private readonly object _lock = new(); public IndexBased(HashSet eventTypes, EventByTypeIndexEventReader reader, ClaimsPrincipal readAs) : base(reader, readAs) { - _streamToEventType = eventTypes.ToDictionary(v => "$et-" + v, v => v); - _eofs = _streamToEventType.Keys.ToDictionary(v => v, v => false); + _streamToEventType = eventTypes.ToDictionary(v => $"$et-{v}", v => v); + _lastKnownIndexCheckpointPosition = null; + _eofs = _streamToEventType.Keys.ToDictionary(v => v, _ => false); // whatever the first event returned is (even if we start from the same position as the last processed event // let subscription handle this _publisher = _reader._publisher; - _pendingRequests = new Dictionary(); - _pendingRequests.Add("$et", Guid.Empty); + _pendingRequests = new() { { "$et", Guid.Empty } }; foreach (var stream in _streamToEventType.Keys) { _pendingRequests.Add(stream, Guid.Empty); } } - - private TFPos GetTargetEventPosition(PendingEvent head) { - return head.TfPosition; - } - public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { if (_disposed) return; @@ -80,13 +74,12 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { return; lock (_lock) { - if (!_pendingRequests.Values.Any(x => x == message.CorrelationId)) + if (_pendingRequests.Values.All(x => x != message.CorrelationId)) return; } if (!_streamToEventType.ContainsKey(message.EventStreamId)) - throw new InvalidOperationException( - String.Format("Invalid stream name: {0}", message.EventStreamId)); + throw new InvalidOperationException($"Invalid stream name: {message.EventStreamId}"); if (!_eventsRequested.Contains(message.EventStreamId)) throw new InvalidOperationException("Read events has not been requested"); if (_reader.Paused) @@ -94,18 +87,17 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { switch (message.Result) { case ReadStreamResult.NoStream: _eofs[message.EventStreamId] = true; - ProcessBuffersAndContinue(eventStreamId: message.EventStreamId); break; case ReadStreamResult.Success: _reader.UpdateNextStreamPosition(message.EventStreamId, message.NextEventNumber); _eofs[message.EventStreamId] = message.Events is [] && message.IsEndOfStream; EnqueueEvents(message); - ProcessBuffersAndContinue(eventStreamId: message.EventStreamId); break; default: - throw new NotSupportedException( - String.Format("ReadEvents result code was not recognized. Code: {0}", message.Result)); + throw new NotSupportedException($"ReadEvents result code was not recognized. Code: {message.Result}"); } + + ProcessBuffersAndContinue(eventStreamId: message.EventStreamId); } public void Handle(ProjectionManagementMessage.Internal.ReadTimeout message) { @@ -114,7 +106,7 @@ public void Handle(ProjectionManagementMessage.Internal.ReadTimeout message) { if (_reader.Paused) return; lock (_lock) { - if (!_pendingRequests.Values.Any(x => x == message.CorrelationId)) + if (_pendingRequests.Values.All(x => x != message.CorrelationId)) return; } @@ -158,8 +150,8 @@ public void Handle(ClientMessage.ReadStreamEventsBackwardCompleted message) { private void EnqueueEvents(ClientMessage.ReadStreamEventsForwardCompleted message) { for (int index = 0; index < message.Events.Count; index++) { var @event = message.Events[index].Event; - var @link = message.Events[index].Link; - EventRecord positionEvent = (link ?? @event); + var link = message.Events[index].Link; + EventRecord positionEvent = link ?? @event; var queue = GetStreamQueue(positionEvent); //TODO: progress calculation below is incorrect. sum(current)/sum(last_event) where sum by all streams var tfPosition = @@ -171,21 +163,19 @@ private void EnqueueEvents(ClientMessage.ReadStreamEventsForwardCompleted messag } private Queue GetStreamQueue(EventRecord positionEvent) { - Queue queue; - if (!_buffers.TryGetValue(positionEvent.EventStreamId, out queue)) { - queue = new Queue(); + if (!_buffers.TryGetValue(positionEvent.EventStreamId, out var queue)) { + queue = new(); _buffers.Add(positionEvent.EventStreamId, queue); } return queue; } - private bool BeforeTheLastKnownIndexCheckpoint(TFPos tfPosition) { - return _lastKnownIndexCheckpointPosition != null && tfPosition <= _lastKnownIndexCheckpointPosition; - } + private bool BeforeTheLastKnownIndexCheckpoint(TFPos tfPosition) => tfPosition <= _lastKnownIndexCheckpointPosition; private void ReadIndexCheckpointStreamCompleted( - ReadStreamResult result, IReadOnlyList events) { + ReadStreamResult result, + IReadOnlyList events) { if (_disposed) return; @@ -198,13 +188,11 @@ private void ReadIndexCheckpointStreamCompleted( case ReadStreamResult.NoStream: _indexStreamEof = true; _lastKnownIndexCheckpointPosition = default(TFPos); - ProcessBuffersAndContinue(null); break; case ReadStreamResult.Success: if (events is []) { _indexStreamEof = true; - if (_lastKnownIndexCheckpointPosition == null) - _lastKnownIndexCheckpointPosition = default(TFPos); + _lastKnownIndexCheckpointPosition ??= default; } else { _indexStreamEof = false; //NOTE: only one event if backward order was requested @@ -215,8 +203,6 @@ private void ReadIndexCheckpointStreamCompleted( // reset eofs before this point - probably some where updated so we cannot go // forward with this position without making sure nothing appeared // NOTE: performance is not very good, but we should switch to TF mode shortly - - foreach (var corrId in _validRequests) { _publisher.Publish(new AwakeServiceMessage.UnsubscribeAwake(corrId)); } @@ -232,12 +218,12 @@ private void ReadIndexCheckpointStreamCompleted( } } - ProcessBuffersAndContinue(null); break; default: - throw new NotSupportedException( - String.Format("ReadEvents result code was not recognized. Code: {0}", result)); + throw new NotSupportedException($"ReadEvents result code was not recognized. Code: {result}"); } + + ProcessBuffersAndContinue(null); } private void ProcessBuffers() { @@ -245,12 +231,11 @@ private void ProcessBuffers() { return; while (true) { var minStreamId = ""; - var minPosition = new TFPos(Int64.MaxValue, Int64.MaxValue); + var minPosition = new TFPos(long.MaxValue, long.MaxValue); var any = false; var anyEof = false; foreach (var streamId in _streamToEventType.Keys) { - Queue buffer; - _buffers.TryGetValue(streamId, out buffer); + _buffers.TryGetValue(streamId, out var buffer); if ((buffer == null || buffer.Count == 0)) if (_eofs[streamId]) { @@ -260,7 +245,7 @@ private void ProcessBuffers() { return; // still reading var head = buffer.Peek(); - var targetEventPosition = GetTargetEventPosition(head); + var targetEventPosition = head.TfPosition; if (targetEventPosition < minPosition) { minPosition = targetEventPosition; @@ -326,8 +311,7 @@ private void RequestEvents(string stream, bool delay) { if (_eventsRequested.Contains(stream)) return; - Queue queue; - if (_buffers.TryGetValue(stream, out queue) && queue.Count > 0) + if (_buffers.TryGetValue(stream, out var queue) && queue.Count > 0) return; _eventsRequested.Add(stream); @@ -360,9 +344,7 @@ private void DeliverEventRetrievedByIndex(KurrentDB.Core.Data.ResolvedEvent pair DeliverEvent(progress, resolvedEvent, position, pair); } - public override bool AreEventsRequested() { - return _eventsRequested.Count != 0 || _indexCheckpointStreamRequested; - } + public override bool AreEventsRequested() => _eventsRequested.Count != 0 || _indexCheckpointStreamRequested; public override void Dispose() { _disposed = true; @@ -379,16 +361,11 @@ private bool ShouldSwitch() { return false; if (_reader.Paused || _reader.PauseRequested) return false; - Queue q; - var shouldSwitch = _lastKnownIndexCheckpointPosition != null - && _streamToEventType.Keys.All( - v => - _eofs[v] - || _buffers.TryGetValue(v, out q) && q.Count > 0 - && - !BeforeTheLastKnownIndexCheckpoint( - q.Peek().TfPosition)); - return shouldSwitch; + return + _lastKnownIndexCheckpointPosition != null + && _streamToEventType.Keys.All(v + => _eofs[v] || _buffers.TryGetValue(v, out var q) && q.Count > 0 && + !BeforeTheLastKnownIndexCheckpoint(q.Peek().TfPosition)); } } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.PendingEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.PendingEvent.cs index 905599e91e7..2b8a78287a3 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.PendingEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.PendingEvent.cs @@ -6,15 +6,9 @@ namespace KurrentDB.Projections.Core.Services.Processing.EventByType; public partial class EventByTypeIndexEventReader { - private class PendingEvent { - public readonly KurrentDB.Core.Data.ResolvedEvent ResolvedEvent; - public readonly float Progress; - public readonly TFPos TfPosition; - - public PendingEvent(KurrentDB.Core.Data.ResolvedEvent resolvedEvent, TFPos tfPosition, float progress) { - ResolvedEvent = resolvedEvent; - Progress = progress; - TfPosition = tfPosition; - } + private class PendingEvent(KurrentDB.Core.Data.ResolvedEvent resolvedEvent, TFPos tfPosition, float progress) { + public readonly KurrentDB.Core.Data.ResolvedEvent ResolvedEvent = resolvedEvent; + public readonly float Progress = progress; + public readonly TFPos TfPosition = tfPosition; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.State.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.State.cs index e39bd997f80..b59a0b3cdf6 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.State.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.State.cs @@ -10,18 +10,13 @@ namespace KurrentDB.Projections.Core.Services.Processing.EventByType; public partial class EventByTypeIndexEventReader { - private abstract class State : IDisposable { + private abstract class State(EventByTypeIndexEventReader reader, ClaimsPrincipal readAs) : IDisposable { public abstract void RequestEvents(); public abstract bool AreEventsRequested(); public abstract void Dispose(); - protected readonly EventByTypeIndexEventReader _reader; - protected readonly ClaimsPrincipal _readAs; - - protected State(EventByTypeIndexEventReader reader, ClaimsPrincipal readAs) { - _reader = reader; - _readAs = readAs; - } + protected readonly EventByTypeIndexEventReader _reader = reader; + protected readonly ClaimsPrincipal _readAs = readAs; protected void DeliverEvent(float progress, ResolvedEvent resolvedEvent, TFPos position, KurrentDB.Core.Data.ResolvedEvent pair) { @@ -29,23 +24,17 @@ protected void DeliverEvent(float progress, ResolvedEvent resolvedEvent, TFPos p return; _reader._lastEventPosition = resolvedEvent.EventOrLinkTargetPosition; //TODO: this is incomplete. where reading from TF we need to handle actual deletes - - string deletedPartitionStreamId; - - if (resolvedEvent.IsLinkToDeletedStream && !resolvedEvent.IsLinkToDeletedStreamTombstone) return; bool isDeletedStreamEvent = StreamDeletedHelper.IsStreamDeletedEventOrLinkToStreamDeletedEvent( - resolvedEvent, pair.ResolveResult, out deletedPartitionStreamId); + resolvedEvent, pair.ResolveResult, out var deletedPartitionStreamId); if (isDeletedStreamEvent) { - var deletedPartition = deletedPartitionStreamId; - if (_reader._includeDeletedStreamNotification) _reader._publisher.Publish( //TODO: publish both link and event data new ReaderSubscriptionMessage.EventReaderPartitionDeleted( - _reader.EventReaderCorrelationId, deletedPartition, source: this.GetType(), deleteEventOrLinkTargetPosition: position, + _reader.EventReaderCorrelationId, deletedPartitionStreamId, source: GetType(), deleteEventOrLinkTargetPosition: position, deleteLinkOrEventPosition: resolvedEvent.EventOrLinkTargetPosition, positionStreamId: resolvedEvent.PositionStreamId, positionEventNumber: resolvedEvent.PositionSequenceNumber)); @@ -54,8 +43,8 @@ protected void DeliverEvent(float progress, ResolvedEvent resolvedEvent, TFPos p //TODO: publish both link and event data new ReaderSubscriptionMessage.CommittedEventDistributed( _reader.EventReaderCorrelationId, resolvedEvent, - _reader._stopOnEof ? (long?)null : position.PreparePosition, progress, - source: this.GetType())); + _reader._stopOnEof ? null : position.PreparePosition, progress, + source: GetType())); } protected void SendNotAuthorized() { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.TfBased.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.TfBased.cs index a3709633621..34d251c257a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.TfBased.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.TfBased.cs @@ -16,10 +16,10 @@ namespace KurrentDB.Projections.Core.Services.Processing.EventByType; public partial class EventByTypeIndexEventReader { - private class TfBased : State, - IHandle, - IHandle { - private readonly HashSet _eventTypes; + private class TfBased + : State, + IHandle, + IHandle { private readonly ITimeProvider _timeProvider; private bool _tfEventsRequested; private bool _disposed; @@ -30,12 +30,15 @@ private class TfBased : State, private Guid _pendingRequestCorrelationId; public TfBased( - ITimeProvider timeProvider, EventByTypeIndexEventReader reader, TFPos fromTfPosition, - IPublisher publisher, ClaimsPrincipal readAs) + ITimeProvider timeProvider, + EventByTypeIndexEventReader reader, + TFPos fromTfPosition, + IPublisher publisher, + ClaimsPrincipal readAs) : base(reader, readAs) { _timeProvider = timeProvider; - _eventTypes = reader._eventTypes; - _streamToEventType = _eventTypes.ToDictionary(v => "$et-" + v, v => v); + var eventTypes = reader._eventTypes; + _streamToEventType = eventTypes.ToDictionary(v => $"$et-{v}", v => v); _publisher = publisher; _fromTfPosition = fromTfPosition; } @@ -89,8 +92,7 @@ public void Handle(ClientMessage.ReadAllEventsForwardCompleted message) { _reader.UpdateNextStreamPosition(link.EventStreamId, link.EventNumber + 1); // recover unresolved link event var unresolvedLinkEvent = - KurrentDB.Core.Data.ResolvedEvent.ForUnresolvedEvent(link, - originalTfPosition.CommitPosition); + KurrentDB.Core.Data.ResolvedEvent.ForUnresolvedEvent(link, originalTfPosition.CommitPosition); DeliverEventRetrievedFromTf( unresolvedLinkEvent, 100.0f * link.LogPosition / message.TfLastCommitPosition, originalTfPosition); @@ -102,13 +104,9 @@ public void Handle(ClientMessage.ReadAllEventsForwardCompleted message) { } } - if (_disposed) - return; - break; default: - throw new NotSupportedException( - String.Format("ReadEvents result code was not recognized. Code: {0}", message.Result)); + throw new NotSupportedException($"ReadEvents result code was not recognized. Code: {message.Result}"); } } @@ -160,7 +158,8 @@ private void DeliverLastCommitPosition(TFPos lastPosition) { //TODO: check was is passed here } - private void DeliverEventRetrievedFromTf(KurrentDB.Core.Data.ResolvedEvent pair, float progress, + private void DeliverEventRetrievedFromTf(KurrentDB.Core.Data.ResolvedEvent pair, + float progress, TFPos position) { var resolvedEvent = new ResolvedEvent(pair, null); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.cs index fa5b75a31fc..a0e218441d7 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexEventReader.cs @@ -14,7 +14,7 @@ namespace KurrentDB.Projections.Core.Services.Processing.EventByType; public partial class EventByTypeIndexEventReader : EventReader { - public const int MaxReadCount = 50; + private const int MaxReadCount = 50; private readonly HashSet _eventTypes; private readonly bool _resolveLinkTos; private readonly bool _includeDeletedStreamNotification; @@ -38,19 +38,17 @@ public EventByTypeIndexEventReader( ITimeProvider timeProvider, bool stopOnEof = false) : base(publisher, eventReaderCorrelationId, readAs, stopOnEof) { - if (eventTypes == null) - throw new ArgumentNullException("eventTypes"); - if (timeProvider == null) - throw new ArgumentNullException("timeProvider"); + ArgumentNullException.ThrowIfNull(eventTypes); + ArgumentNullException.ThrowIfNull(timeProvider); if (eventTypes.Length == 0) - throw new ArgumentException("empty", "eventTypes"); + throw new ArgumentException("empty", nameof(eventTypes)); _includeDeletedStreamNotification = includeDeletedStreamNotification; _timeProvider = timeProvider; - _eventTypes = new HashSet(eventTypes); + _eventTypes = new(eventTypes); if (includeDeletedStreamNotification) _eventTypes.Add("$deleted"); - _streamToEventType = eventTypes.ToDictionary(v => "$et-" + v, v => v); + _streamToEventType = eventTypes.ToDictionary(v => $"$et-{v}", v => v); _lastEventPosition = fromTfPosition; _resolveLinkTos = resolveLinkTos; @@ -62,15 +60,13 @@ public EventByTypeIndexEventReader( private void ValidateTag(Dictionary fromPositions) { if (_eventTypes.Count != fromPositions.Count) - throw new ArgumentException("Number of streams does not match", "fromPositions"); + throw new ArgumentException("Number of streams does not match", nameof(fromPositions)); foreach (var stream in _streamToEventType.Keys.Where(stream => !fromPositions.ContainsKey(stream))) { - throw new ArgumentException( - String.Format("The '{0}' stream position has not been set", stream), "fromPositions"); + throw new ArgumentException($"The '{stream}' stream position has not been set", nameof(fromPositions)); } } - public override void Dispose() { _state.Dispose(); base.Dispose(); @@ -82,12 +78,9 @@ protected override void RequestEvents() { _state.RequestEvents(); } - protected override bool AreEventsRequested() { - return _state.AreEventsRequested(); - } + protected override bool AreEventsRequested() => _state.AreEventsRequested(); - private void PublishIORequest(bool delay, Message readEventsForward, Message timeoutMessage, - Guid correlationId) { + private void PublishIORequest(bool delay, Message readEventsForward, Message timeoutMessage, Guid correlationId) { if (delay) { _publisher.Publish( new AwakeServiceMessage.SubscribeAwake( @@ -100,8 +93,7 @@ private void PublishIORequest(bool delay, Message readEventsForward, Message tim } private void UpdateNextStreamPosition(string eventStreamId, long nextPosition) { - long streamPosition; - if (!_fromPositions.TryGetValue(eventStreamId, out streamPosition)) + if (!_fromPositions.TryGetValue(eventStreamId, out var streamPosition)) streamPosition = -1; if (nextPosition > streamPosition) _fromPositions[eventStreamId] = nextPosition; @@ -116,7 +108,7 @@ private void DoSwitch(TFPos lastKnownIndexCheckpointPosition) { if (lastKnownIndexCheckpointPosition > _lastEventPosition) _lastEventPosition = lastKnownIndexCheckpointPosition; - _state = new TfBased(_timeProvider, this, _lastEventPosition, this._publisher, ReadAs); + _state = new TfBased(_timeProvider, this, _lastEventPosition, _publisher, ReadAs); _state.RequestEvents(); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexPositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexPositionTagger.cs index 7df78790826..eb02ad83cac 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexPositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventByType/EventByTypeIndexPositionTagger.cs @@ -18,16 +18,14 @@ public class EventByTypeIndexPositionTagger : PositionTagger { public EventByTypeIndexPositionTagger( int phase, string[] eventTypes, bool includeStreamDeletedNotification = false) : base(phase) { - if (eventTypes == null) - throw new ArgumentNullException("eventTypes"); + ArgumentNullException.ThrowIfNull(eventTypes); if (eventTypes.Length == 0) throw new ArgumentException("eventTypes"); - _eventTypes = new HashSet(eventTypes); + _eventTypes = new(eventTypes); if (includeStreamDeletedNotification) _eventTypes.Add("$deleted"); - _streams = new HashSet(from eventType in eventTypes - select "$et-" + eventType); - _streamToEventType = eventTypes.ToDictionary(v => "$et-" + v, v => v); + _streams = new(eventTypes.Select(eventType => $"$et-{eventType}")); + _streamToEventType = eventTypes.ToDictionary(v => $"$et-{v}", v => v); } public override bool IsMessageAfterCheckpointTag( @@ -35,9 +33,9 @@ public override bool IsMessageAfterCheckpointTag( if (previous.Phase < Phase) return true; if (previous.Mode_ != CheckpointTag.Mode.EventTypeIndex) - throw new ArgumentException("Mode.EventTypeIndex expected", "previous"); + throw new ArgumentException("Mode.EventTypeIndex expected", nameof(previous)); if (committedEvent.Data.EventOrLinkTargetPosition.CommitPosition <= 0) - throw new ArgumentException("complete TF position required", "committedEvent"); + throw new ArgumentException("complete TF position required", nameof(committedEvent)); return committedEvent.Data.EventOrLinkTargetPosition > previous.Position; } @@ -45,14 +43,11 @@ public override bool IsMessageAfterCheckpointTag( public override CheckpointTag MakeCheckpointTag( CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); + throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}"); if (committedEvent.Data.EventOrLinkTargetPosition < previous.Position) throw new InvalidOperationException( - string.Format( - "Cannot make a checkpoint tag at earlier position. '{0}' < '{1}'", - committedEvent.Data.EventOrLinkTargetPosition, previous.Position)); + $"Cannot make a checkpoint tag at earlier position. '{committedEvent.Data.EventOrLinkTargetPosition}' < '{previous.Position}'"); var byIndex = _streams.Contains(committedEvent.Data.PositionStreamId); return byIndex ? previous.UpdateEventTypeIndexPosition( @@ -65,14 +60,11 @@ public override CheckpointTag MakeCheckpointTag( public override CheckpointTag MakeCheckpointTag( CheckpointTag previous, ReaderSubscriptionMessage.EventReaderPartitionDeleted partitionDeleted) { if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); + throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}"); if (partitionDeleted.DeleteEventOrLinkTargetPosition < previous.Position) throw new InvalidOperationException( - string.Format( - "Cannot make a checkpoint tag at earlier position. '{0}' < '{1}'", - partitionDeleted.DeleteEventOrLinkTargetPosition, previous.Position)); + $"Cannot make a checkpoint tag at earlier position. '{partitionDeleted.DeleteEventOrLinkTargetPosition}' < '{previous.Position}'"); var byIndex = _streams.Contains(partitionDeleted.PositionStreamId); //TODO: handle invalid partition deleted messages without required values return byIndex @@ -85,7 +77,7 @@ public override CheckpointTag MakeCheckpointTag( public override CheckpointTag MakeZeroCheckpointTag() { return CheckpointTag.FromEventTypeIndexPositions( - Phase, new TFPos(0, -1), _eventTypes.ToDictionary(v => v, v => ExpectedVersion.NoStream)); + Phase, new TFPos(0, -1), _eventTypes.ToDictionary(v => v, _ => ExpectedVersion.NoStream)); } public override bool IsCompatible(CheckpointTag checkpointTag) { @@ -99,33 +91,25 @@ public override CheckpointTag AdjustTag(CheckpointTag tag) { return tag; if (tag.Phase > Phase) throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected less or equal to: {0} Was: {1}", Phase, - tag.Phase), "tag"); + $"Invalid checkpoint tag phase. Expected less or equal to: {Phase} Was: {tag.Phase}", nameof(tag)); if (tag.Mode_ == CheckpointTag.Mode.EventTypeIndex) { - long p; return CheckpointTag.FromEventTypeIndexPositions( tag.Phase, tag.Position, - _eventTypes.ToDictionary(v => v, v => tag.Streams.TryGetValue(v, out p) ? p : -1)); + _eventTypes.ToDictionary(v => v, v => tag.Streams.TryGetValue(v, out var p) ? p : -1)); } - switch (tag.Mode_) { - case CheckpointTag.Mode.MultiStream: - throw new NotSupportedException( - "Conversion from MultiStream to EventTypeIndex position tag is not supported"); - case CheckpointTag.Mode.Stream: - throw new NotSupportedException( - "Conversion from Stream to EventTypeIndex position tag is not supported"); - case CheckpointTag.Mode.PreparePosition: - throw new NotSupportedException( - "Conversion from PreparePosition to EventTypeIndex position tag is not supported"); - case CheckpointTag.Mode.Position: - return CheckpointTag.FromEventTypeIndexPositions( - tag.Phase, tag.Position, _eventTypes.ToDictionary(v => v, v => (long)-1)); - default: - throw new NotSupportedException(string.Format( - "The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {0}", - tag.ToString())); - } + return tag.Mode_ switch { + CheckpointTag.Mode.MultiStream => throw new NotSupportedException( + "Conversion from MultiStream to EventTypeIndex position tag is not supported"), + CheckpointTag.Mode.Stream => throw new NotSupportedException( + "Conversion from Stream to EventTypeIndex position tag is not supported"), + CheckpointTag.Mode.PreparePosition => throw new NotSupportedException( + "Conversion from PreparePosition to EventTypeIndex position tag is not supported"), + CheckpointTag.Mode.Position => CheckpointTag.FromEventTypeIndexPositions(tag.Phase, tag.Position, + _eventTypes.ToDictionary(v => v, _ => (long)-1)), + _ => throw new NotSupportedException( + $"The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {tag}") + }; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventFilter.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventFilter.cs index 7eef938e38a..42a3eb91bbe 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventFilter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventFilter.cs @@ -28,12 +28,12 @@ protected EventFilter(bool allEvents, bool includeDeletedStreamEvents, HashSet data) { + public static bool PassesValidation(bool isJson, ReadOnlyMemory data) { return !isJson || data.IsValidUtf8Json(); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventProcessedResult.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventProcessedResult.cs index 6c8e5f5ce18..fbb843d929a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventProcessedResult.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventProcessedResult.cs @@ -2,81 +2,44 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; +using KurrentDB.Common.Utils; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; using KurrentDB.Projections.Core.Services.Processing.Partitioning; namespace KurrentDB.Projections.Core.Services.Processing; -public class EventProcessedResult { - private readonly EmittedEventEnvelope[] _emittedEvents; - private readonly PartitionState _oldState; - private readonly PartitionState _newState; - private readonly PartitionState _oldSharedState; - private readonly PartitionState _newSharedState; - private readonly string _partition; - private readonly CheckpointTag _checkpointTag; - private readonly Guid _causedBy; - private readonly string _correlationId; +public class EventProcessedResult( + string partition, + CheckpointTag checkpointTag, + PartitionState oldState, + PartitionState newState, + PartitionState oldSharedState, + PartitionState newSharedState, + EmittedEventEnvelope[] emittedEvents, + Guid causedBy, + string correlationId) { + public EmittedEventEnvelope[] EmittedEvents { get; } = emittedEvents; - public EventProcessedResult( - string partition, CheckpointTag checkpointTag, PartitionState oldState, PartitionState newState, - PartitionState oldSharedState, PartitionState newSharedState, EmittedEventEnvelope[] emittedEvents, - Guid causedBy, string correlationId) { - if (partition == null) - throw new ArgumentNullException("partition"); - if (checkpointTag == null) - throw new ArgumentNullException("checkpointTag"); - _emittedEvents = emittedEvents; - _causedBy = causedBy; - _correlationId = correlationId; - _oldState = oldState; - _newState = newState; - _oldSharedState = oldSharedState; - _newSharedState = newSharedState; - _partition = partition; - _checkpointTag = checkpointTag; - } - - public EmittedEventEnvelope[] EmittedEvents { - get { return _emittedEvents; } - } - - public PartitionState OldState { - get { return _oldState; } - } + public PartitionState OldState { get; } = oldState; /// /// null - means no state change /// - public PartitionState NewState { - get { return _newState; } - } + public PartitionState NewState { get; } = newState; - public PartitionState OldSharedState { - get { return _oldSharedState; } - } + public PartitionState OldSharedState { get; } = oldSharedState; /// /// null - means no state change /// - public PartitionState NewSharedState { - get { return _newSharedState; } - } + public PartitionState NewSharedState { get; } = newSharedState; - public string Partition { - get { return _partition; } - } + public string Partition { get; } = Ensure.NotNull(partition); - public CheckpointTag CheckpointTag { - get { return _checkpointTag; } - } + public CheckpointTag CheckpointTag { get; } = Ensure.NotNull(checkpointTag); - public Guid CausedBy { - get { return _causedBy; } - } + public Guid CausedBy { get; } = causedBy; - public string CorrelationId { - get { return _correlationId; } - } + public string CorrelationId { get; } = correlationId; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventReader.cs index 536e7c165ea..65f0a4d98ca 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventReader.cs @@ -7,57 +7,44 @@ using KurrentDB.Core.Data; using KurrentDB.Core.Messages; using KurrentDB.Projections.Core.Messages; -using KurrentDB.Projections.Core.Services.Processing.Checkpointing; namespace KurrentDB.Projections.Core.Services.Processing; public abstract class EventReader : IEventReader { protected readonly Guid EventReaderCorrelationId; - private readonly ClaimsPrincipal _readAs; protected readonly IPublisher _publisher; - protected readonly bool _stopOnEof; - private bool _paused = true; - private bool _pauseRequested = true; protected bool _disposed; private bool _startingSent; protected EventReader(IPublisher publisher, Guid eventReaderCorrelationId, ClaimsPrincipal readAs, bool stopOnEof) { - if (publisher == null) - throw new ArgumentNullException("publisher"); + ArgumentNullException.ThrowIfNull(publisher); if (eventReaderCorrelationId == Guid.Empty) - throw new ArgumentException("eventReaderCorrelationId"); + throw new ArgumentException(null, nameof(eventReaderCorrelationId)); _publisher = publisher; EventReaderCorrelationId = eventReaderCorrelationId; - _readAs = readAs; + ReadAs = readAs; _stopOnEof = stopOnEof; } - protected bool PauseRequested { - get { return _pauseRequested; } - } + protected bool PauseRequested { get; private set; } = true; - protected bool Paused { - get { return _paused; } - } + protected bool Paused { get; private set; } = true; - protected ClaimsPrincipal ReadAs { - get { return _readAs; } - } + protected ClaimsPrincipal ReadAs { get; } public void Resume() { if (_disposed) throw new InvalidOperationException("Disposed"); - if (!_pauseRequested) + if (!PauseRequested) throw new InvalidOperationException("Is not paused"); - if (!_paused) { - _pauseRequested = false; + if (!Paused) { + PauseRequested = false; return; } - _paused = false; - _pauseRequested = false; - // _logger.Trace("Resuming event distribution {eventReaderCorrelationId} at '{at}'", EventReaderCorrelationId, FromAsText()); + Paused = false; + PauseRequested = false; RequestEvents(); } @@ -65,12 +52,11 @@ public void Pause() { if (_disposed) return; // due to possible self disposed - if (_pauseRequested) + if (PauseRequested) throw new InvalidOperationException("Pause has been already requested"); - _pauseRequested = true; + PauseRequested = true; if (!AreEventsRequested()) - _paused = true; - // _logger.Trace("Pausing event distribution {eventReaderCorrelationId} at '{at}'", EventReaderCorrelationId, FromAsText()); + Paused = true; } public virtual void Dispose() { @@ -88,7 +74,7 @@ protected void SendEof() { } protected void SendPartitionDeleted_WhenReadingDataStream( - string partition, long? lastEventNumber, TFPos? deletedLinkOrEventPosition, TFPos? deletedEventPosition, + string partition, TFPos? deletedLinkOrEventPosition, TFPos? deletedEventPosition, string positionStreamId, int? positionEventNumber) { if (_disposed) return; @@ -109,23 +95,21 @@ public void SendNotAuthorized() { return (msg.IsEndOfStream || msg.Result == ReadStreamResult.NoStream || msg.Result == ReadStreamResult.StreamDeleted) - ? (msg.TfLastCommitPosition == -1 ? (long?)null : msg.TfLastCommitPosition) - : (long?)null; + ? msg.TfLastCommitPosition == -1 ? null : msg.TfLastCommitPosition + : null; } protected void PauseOrContinueProcessing() { if (_disposed) return; - if (_pauseRequested) - _paused = !AreEventsRequested(); + if (PauseRequested) + Paused = !AreEventsRequested(); else RequestEvents(); } private void SendStarting(long startingLastCommitPosition) { - _publisher.Publish( - new ReaderSubscriptionMessage.EventReaderStarting(EventReaderCorrelationId, - startingLastCommitPosition)); + _publisher.Publish(new ReaderSubscriptionMessage.EventReaderStarting(EventReaderCorrelationId, startingLastCommitPosition)); } protected void NotifyIfStarting(long startingLastCommitPosition) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/EventReaderCoreService.cs b/src/KurrentDB.Projections.Core/Services/Processing/EventReaderCoreService.cs index 02dd1c1b551..e5114d6ac14 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/EventReaderCoreService.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/EventReaderCoreService.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using KurrentDB.Core.Bus; using KurrentDB.Core.Data; -using KurrentDB.Core.Helpers; using KurrentDB.Core.Messaging; using KurrentDB.Core.Services.TimerService; using KurrentDB.Core.Services.UserManagement; @@ -37,18 +36,14 @@ public class EventReaderCoreService : public const string SubComponentName = "EventReaderCoreService"; private readonly IPublisher _publisher; - private readonly IODispatcher _ioDispatcher; private readonly ILogger _logger = Log.ForContext(); private bool _stopped = true; - private readonly Dictionary _subscriptions = - new Dictionary(); - - private readonly Dictionary _eventReaders = new Dictionary(); - - private readonly Dictionary _subscriptionEventReaders = new Dictionary(); - private readonly Dictionary _eventReaderSubscriptions = new Dictionary(); - private readonly HashSet _pausedSubscriptions = new HashSet(); + private readonly Dictionary _subscriptions = new(); + private readonly Dictionary _eventReaders = new(); + private readonly Dictionary _subscriptionEventReaders = new(); + private readonly Dictionary _eventReaderSubscriptions = new(); + private readonly HashSet _pausedSubscriptions = []; private readonly HeadingEventReader _headingEventReader; private readonly ICheckpoint _writerCheckpoint; private readonly bool _runHeadingReader; @@ -58,12 +53,11 @@ public class EventReaderCoreService : private Guid _reportProgressId; public EventReaderCoreService( - IPublisher publisher, IODispatcher ioDispatcher, int eventCacheSize, + IPublisher publisher, int eventCacheSize, ICheckpoint writerCheckpoint, bool runHeadingReader, bool faultOutOfOrderProjections) { _publisher = publisher; - _ioDispatcher = ioDispatcher; if (runHeadingReader) - _headingEventReader = new HeadingEventReader(eventCacheSize, _publisher); + _headingEventReader = new(eventCacheSize, _publisher); _writerCheckpoint = writerCheckpoint; _runHeadingReader = runHeadingReader; _faultOutOfOrderProjections = faultOutOfOrderProjections; @@ -74,8 +68,7 @@ public void Handle(ReaderSubscriptionManagement.Pause message) { if (!_pausedSubscriptions.Add(message.SubscriptionId)) throw new InvalidOperationException("Already paused projection"); - IReaderSubscription projectionSubscription; - if (!_subscriptions.TryGetValue(message.SubscriptionId, out projectionSubscription)) + if (!_subscriptions.TryGetValue(message.SubscriptionId, out var projectionSubscription)) return; // may be already unsubscribed when self-unsubscribing var eventReaderId = _subscriptionEventReaders[message.SubscriptionId]; @@ -84,8 +77,7 @@ public void Handle(ReaderSubscriptionManagement.Pause message) { _subscriptionEventReaders.Remove(message.SubscriptionId); _headingEventReader.Unsubscribe(message.SubscriptionId); var forkedEventReaderId = Guid.NewGuid(); - var forkedEventReader = projectionSubscription.CreatePausedEventReader( - _publisher, _ioDispatcher, forkedEventReaderId); + var forkedEventReader = projectionSubscription.CreatePausedEventReader(_publisher, forkedEventReaderId); _subscriptionEventReaders.Add(message.SubscriptionId, forkedEventReaderId); _eventReaderSubscriptions.Add(forkedEventReaderId, message.SubscriptionId); _eventReaders.Add(forkedEventReaderId, forkedEventReader); @@ -121,7 +113,7 @@ public void Handle(ReaderSubscriptionManagement.Subscribe message) { var distributionPointCorrelationId = Guid.NewGuid(); var eventReader = projectionSubscription.CreatePausedEventReader( - _publisher, _ioDispatcher, distributionPointCorrelationId); + _publisher, distributionPointCorrelationId); _eventReaders.Add(distributionPointCorrelationId, eventReader); _subscriptionEventReaders.Add(subscriptionId, distributionPointCorrelationId); _eventReaderSubscriptions.Add(distributionPointCorrelationId, subscriptionId); @@ -153,12 +145,11 @@ public void Handle(ReaderSubscriptionManagement.Unsubscribe message) { } public void Handle(ReaderSubscriptionMessage.CommittedEventDistributed message) { - Guid projectionId; if (_stopped) return; if (_runHeadingReader && _headingEventReader.Handle(message)) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed if (TrySubscribeHeadingEventReader(message, projectionId)) @@ -170,61 +161,51 @@ public void Handle(ReaderSubscriptionMessage.CommittedEventDistributed message) var subscription = _subscriptions[projectionId]; Handle(new ReaderSubscriptionManagement.Unsubscribe(subscription.SubscriptionId)); _publisher.Publish(new EventReaderSubscriptionMessage.Failed(subscription.SubscriptionId, - string.Format("The subscription failed to handle an event {0}:{1}@{2} because {3}", - message.Data.EventStreamId, message.Data.EventType, message.Data.EventSequenceNumber, - ex.Message))); + $"The subscription failed to handle an event {message.Data.EventStreamId}:{message.Data.EventType}@{message.Data.EventSequenceNumber} because {ex.Message}")); } } } public void Handle(ReaderSubscriptionMessage.EventReaderIdle message) { - Guid projectionId; if (_stopped) return; if (_runHeadingReader && _headingEventReader.Handle(message)) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed _subscriptions[projectionId].Handle(message); } public void Handle(ReaderSubscriptionMessage.EventReaderStarting message) { - Guid projectionId; if (_stopped) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed _subscriptions[projectionId].Handle(message); } public void Handle(ReaderSubscriptionMessage.EventReaderEof message) { - Guid projectionId; if (_stopped) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed _subscriptions[projectionId].Handle(message); - - // _pausedSubscriptions.Add(projectionId); // it is actually disposed -- workaround - // Handle(new ReaderSubscriptionManagement.Unsubscribe(projectionId)); } public void Handle(ReaderSubscriptionMessage.EventReaderPartitionDeleted message) { - Guid projectionId; if (_stopped) return; if (_runHeadingReader && _headingEventReader.Handle(message)) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed _subscriptions[projectionId].Handle(message); } public void Handle(ReaderSubscriptionMessage.EventReaderNotAuthorized message) { - Guid projectionId; if (_stopped) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed _subscriptions[projectionId].Handle(message); @@ -233,10 +214,9 @@ public void Handle(ReaderSubscriptionMessage.EventReaderNotAuthorized message) { } public void Handle(ReaderSubscriptionMessage.Faulted message) { - Guid projectionId; if (_stopped) return; - if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out projectionId)) + if (!_eventReaderSubscriptions.TryGetValue(message.CorrelationId, out var projectionId)) return; // unsubscribed if (!_faultOutOfOrderProjections && message.Reason.Contains("was expected in the stream")) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/IEventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/IEventReader.cs index 5ed40e9b0c8..db16faf0f06 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/IEventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/IEventReader.cs @@ -8,5 +8,4 @@ namespace KurrentDB.Projections.Core.Services.Processing; public interface IEventReader : IDisposable { void Resume(); void Pause(); - void SendNotAuthorized(); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventFilter.cs b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventFilter.cs index 193d5434c5e..5f8b9adbe2e 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventFilter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventFilter.cs @@ -5,23 +5,12 @@ namespace KurrentDB.Projections.Core.Services.Processing.MultiStream; -public class MultiStreamEventFilter : EventFilter { - private readonly HashSet _streams; +public class MultiStreamEventFilter(HashSet streams, bool allEvents, HashSet events) + : EventFilter(allEvents, false, events) { + protected override bool DeletedNotificationPasses(string positionStreamId) => false; - public MultiStreamEventFilter(HashSet streams, bool allEvents, HashSet events) - : base(allEvents, false, events) { - _streams = streams; - } + public override bool PassesSource(bool resolvedFromLinkTo, string positionStreamId, string eventType) + => streams.Contains(positionStreamId); - protected override bool DeletedNotificationPasses(string positionStreamId) { - return false; - } - - public override bool PassesSource(bool resolvedFromLinkTo, string positionStreamId, string eventType) { - return _streams.Contains(positionStreamId); - } - - public override string GetCategory(string positionStreamId) { - return null; - } + public override string GetCategory(string positionStreamId) => null; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventReader.cs index 6c83aef4e11..ba4262e9503 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamEventReader.cs @@ -8,7 +8,6 @@ using System.Security.Claims; using KurrentDB.Core.Bus; using KurrentDB.Core.Data; -using KurrentDB.Core.Helpers; using KurrentDB.Core.Messages; using KurrentDB.Core.Messaging; using KurrentDB.Core.Services.TimerService; @@ -20,50 +19,49 @@ namespace KurrentDB.Projections.Core.Services.Processing.MultiStream; -public class MultiStreamEventReader : EventReader, - IHandle, - IHandle { +public class MultiStreamEventReader + : EventReader, + IHandle, + IHandle { private readonly HashSet _streams; private CheckpointTag _fromPositions; private readonly bool _resolveLinkTos; private readonly ITimeProvider _timeProvider; - - private readonly HashSet _eventsRequested = new HashSet(); - private readonly Dictionary _preparePositions = new Dictionary(); + private readonly HashSet _eventsRequested = []; + private readonly Dictionary _preparePositions = new(); // event, link, progress // null element in a queue means stream deleted - private readonly Dictionary>> _buffers = - new Dictionary>>(); + private readonly Dictionary> _buffers = new(); - private const int _maxReadCount = 111; + private const int MaxReadCount = 111; private long? _safePositionToJoin; private readonly ConcurrentDictionary _eofs; - private int _deliveredEvents; private long _lastPosition; - private readonly ConcurrentDictionary _pendingRequests; + private readonly ConcurrentDictionary _pendingRequests = new(); - public MultiStreamEventReader( - IODispatcher ioDispatcher, IPublisher publisher, Guid eventReaderCorrelationId, ClaimsPrincipal readAs, + public MultiStreamEventReader(IPublisher publisher, + Guid eventReaderCorrelationId, + ClaimsPrincipal readAs, int phase, - string[] streams, Dictionary fromPositions, bool resolveLinkTos, ITimeProvider timeProvider, - bool stopOnEof = false, int? stopAfterNEvents = null) + string[] streams, + Dictionary fromPositions, + bool resolveLinkTos, + ITimeProvider timeProvider, + bool stopOnEof = false) : base(publisher, eventReaderCorrelationId, readAs, stopOnEof) { - if (streams == null) - throw new ArgumentNullException("streams"); - if (timeProvider == null) - throw new ArgumentNullException("timeProvider"); + ArgumentNullException.ThrowIfNull(streams); + ArgumentNullException.ThrowIfNull(timeProvider); if (streams.Length == 0) throw new ArgumentException("streams"); - _streams = new HashSet(streams); - _eofs = new ConcurrentDictionary(_streams.ToDictionary(v => v, v => false)); + _streams = new(streams); + _eofs = new(_streams.ToDictionary(v => v, _ => false)); var positions = CheckpointTag.FromStreamPositions(phase, fromPositions); ValidateTag(positions); _fromPositions = positions; _resolveLinkTos = resolveLinkTos; _timeProvider = timeProvider; - _pendingRequests = new ConcurrentDictionary(); foreach (var stream in streams) { _pendingRequests[stream] = Guid.Empty; _preparePositions.Add(stream, null); @@ -72,12 +70,11 @@ public MultiStreamEventReader( private void ValidateTag(CheckpointTag fromPositions) { if (_streams.Count != fromPositions.Streams.Count) - throw new ArgumentException("Number of streams does not match", "fromPositions"); + throw new ArgumentException("Number of streams does not match", nameof(fromPositions)); foreach (var stream in _streams) { if (!fromPositions.Streams.ContainsKey(stream)) - throw new ArgumentException( - string.Format("The '{0}' stream position has not been set", stream), "fromPositions"); + throw new ArgumentException($"The '{stream}' stream position has not been set", nameof(fromPositions)); } } @@ -106,12 +103,12 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { if (_disposed) return; if (!_streams.Contains(message.EventStreamId)) - throw new InvalidOperationException(string.Format("Invalid stream name: {0}", message.EventStreamId)); + throw new InvalidOperationException($"Invalid stream name: {message.EventStreamId}"); if (!_eventsRequested.Contains(message.EventStreamId)) throw new InvalidOperationException("Read events has not been requested"); if (Paused) throw new InvalidOperationException("Paused"); - if (!_pendingRequests.Values.Any(x => x == message.CorrelationId)) + if (_pendingRequests.Values.All(x => x != message.CorrelationId)) return; _lastPosition = message.TfLastCommitPosition; @@ -121,7 +118,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { _eofs[message.EventStreamId] = true; UpdateSafePositionToJoin(message.EventStreamId, MessageToLastCommitPosition(message)); if (message.Result == ReadStreamResult.StreamDeleted - || (message.Result == ReadStreamResult.NoStream && message.LastEventNumber >= 0)) + || (message.Result == ReadStreamResult.NoStream && message.LastEventNumber >= 0)) EnqueueItem(null, message.EventStreamId); ProcessBuffers(); _eventsRequested.Remove(message.EventStreamId); @@ -141,13 +138,14 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { if (message.Events is []) { _fromPositions.Streams[message.EventStreamId] = message.NextEventNumber; } + for (int index = 0; index < message.Events.Count; index++) { var @event = message.Events[index].Event; - var @link = message.Events[index].Link; - EventRecord positionEvent = (link ?? @event); + var link = message.Events[index].Link; + EventRecord positionEvent = link ?? @event; UpdateSafePositionToJoin( positionEvent.EventStreamId, EventPairToPosition(message.Events[index])); - Tuple itemToEnqueue = Tuple.Create( + var itemToEnqueue = ( message.Events[index], 100.0f * (link ?? @event).EventNumber / message.LastEventNumber); EnqueueItem(itemToEnqueue, positionEvent.EventStreamId); @@ -162,8 +160,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { SendNotAuthorized(); return; default: - throw new NotSupportedException( - string.Format("ReadEvents result code was not recognized. Code: {0}", message.Result)); + throw new NotSupportedException($"ReadEvents result code was not recognized. Code: {message.Result}"); } } @@ -172,17 +169,16 @@ public void Handle(ProjectionManagementMessage.Internal.ReadTimeout message) { return; if (Paused) return; - if (!_pendingRequests.Values.Any(x => x == message.CorrelationId)) + if (_pendingRequests.Values.All(x => x != message.CorrelationId)) return; _eventsRequested.Remove(message.StreamId); PauseOrContinueProcessing(); } - private void EnqueueItem(Tuple itemToEnqueue, string streamId) { - Queue> queue; - if (!_buffers.TryGetValue(streamId, out queue)) { - queue = new Queue>(); + private void EnqueueItem((KurrentDB.Core.Data.ResolvedEvent, float)? itemToEnqueue, string streamId) { + if (!_buffers.TryGetValue(streamId, out var queue)) { + queue = new(); _buffers.Add(streamId, queue); } @@ -216,19 +212,17 @@ private void ProcessBuffers() { var anyDeletedStream = false; var deletedStreamId = ""; - foreach (var buffer in _buffers) { - if (buffer.Value.Count == 0) + foreach (var (currentStreamId, queue) in _buffers) { + if (queue.Count == 0) continue; anyNonEmpty = true; - var head = buffer.Value.Peek(); - - var currentStreamId = buffer.Key; + var head = queue.Peek(); if (head != null) { - var itemPosition = GetItemPosition(head); + var itemPosition = GetItemPosition(head.Value); if (_safePositionToJoin != null - && itemPosition.CompareTo(_safePositionToJoin.GetValueOrDefault()) <= 0 - && itemPosition.CompareTo(minPosition) < 0) { + && itemPosition.CompareTo(_safePositionToJoin.GetValueOrDefault()) <= 0 + && itemPosition.CompareTo(minPosition) < 0) { minPosition = itemPosition; minStreamId = currentStreamId; anyEvent = true; @@ -247,7 +241,9 @@ private void ProcessBuffers() { if (anyEvent) { var minHead = _buffers[minStreamId].Dequeue(); - DeliverEvent(minHead.Item1, minHead.Item2); + if (minHead != null) { + DeliverEvent(minHead.Value.Item1, minHead.Value.Item2); + } if (_buffers[minStreamId].Count == 0) PauseOrContinueProcessing(); @@ -255,7 +251,7 @@ private void ProcessBuffers() { if (anyDeletedStream) { _buffers[deletedStreamId].Dequeue(); - SendPartitionDeleted_WhenReadingDataStream(deletedStreamId, -1, null, null, null, null); + SendPartitionDeleted_WhenReadingDataStream(deletedStreamId, null, null, null, null); } } } @@ -268,8 +264,7 @@ private void RequestEvents(string stream, bool delay) { if (_eventsRequested.Contains(stream)) return; - Queue> queue; - if (_buffers.TryGetValue(stream, out queue) && queue.Count > 0) + if (_buffers.TryGetValue(stream, out var queue) && queue.Count > 0) return; _eventsRequested.Add(stream); @@ -279,7 +274,7 @@ private void RequestEvents(string stream, bool delay) { var readEventsForward = new ClientMessage.ReadStreamEventsForward( Guid.NewGuid(), pendingRequestCorrelationId, new SendToThisEnvelope(this), stream, _fromPositions.Streams[stream], - _maxReadCount, _resolveLinkTos, false, null, ReadAs, replyOnExpired: false); + MaxReadCount, _resolveLinkTos, false, null, ReadAs, replyOnExpired: false); if (delay) { _publisher.Publish( new AwakeServiceMessage.SubscribeAwake( @@ -314,7 +309,7 @@ private void DeliverSafePositionToJoin() { _publisher.Publish( new ReaderSubscriptionMessage.CommittedEventDistributed( EventReaderCorrelationId, null, PositionToSafeJoinPosition(_safePositionToJoin), 100.0f, - source: this.GetType())); + source: GetType())); } private void UpdateSafePositionToJoin(string streamId, long? preparePosition) { @@ -324,7 +319,6 @@ private void UpdateSafePositionToJoin(string streamId, long? preparePosition) { } private void DeliverEvent(KurrentDB.Core.Data.ResolvedEvent pair, float progress) { - _deliveredEvents++; var positionEvent = pair.OriginalEvent; string streamId = positionEvent.EventStreamId; long fromPosition = _fromPositions.Streams[streamId]; @@ -336,9 +330,8 @@ private void DeliverEvent(KurrentDB.Core.Data.ResolvedEvent pair, float progress if (positionEvent.EventNumber != fromPosition) { // This can happen when the original stream has $maxAge/$maxCount set - _publisher.Publish(new ReaderSubscriptionMessage.Faulted(EventReaderCorrelationId, string.Format( - "Event number {0} was expected in the stream {1}, but event number {2} was received. This may happen if events have been deleted from the beginning of your stream, please reset your projection.", - fromPosition, streamId, positionEvent.EventNumber), this.GetType())); + _publisher.Publish(new ReaderSubscriptionMessage.Faulted(EventReaderCorrelationId, + $"Event number {fromPosition} was expected in the stream {streamId}, but event number {positionEvent.EventNumber} was received. This may happen if events have been deleted from the beginning of your stream, please reset your projection.", GetType())); return; } @@ -347,26 +340,18 @@ private void DeliverEvent(KurrentDB.Core.Data.ResolvedEvent pair, float progress //TODO: publish both link and event data new ReaderSubscriptionMessage.CommittedEventDistributed( EventReaderCorrelationId, new ResolvedEvent(pair, null), - _stopOnEof ? (long?)null : positionEvent.LogPosition, progress, source: this.GetType())); + _stopOnEof ? null : positionEvent.LogPosition, progress, source: GetType())); } - private long? EventPairToPosition(KurrentDB.Core.Data.ResolvedEvent resolvedEvent) { - return resolvedEvent.OriginalEvent.LogPosition; - } + private static long? EventPairToPosition(KurrentDB.Core.Data.ResolvedEvent resolvedEvent) => resolvedEvent.OriginalEvent.LogPosition; - private long? MessageToLastCommitPosition(ClientMessage.ReadStreamEventsForwardCompleted message) { - return GetLastCommitPositionFrom(message); - } + private static long? MessageToLastCommitPosition(ClientMessage.ReadStreamEventsForwardCompleted message) + => GetLastCommitPositionFrom(message); - private long GetItemPosition(Tuple head) { - return head.Item1.OriginalEvent.LogPosition; - } + private static long GetItemPosition((KurrentDB.Core.Data.ResolvedEvent, float) head) + => head.Item1.OriginalEvent.LogPosition; - private long GetMaxPosition() { - return long.MaxValue; - } + private static long GetMaxPosition() => long.MaxValue; - private long? PositionToSafeJoinPosition(long? safePositionToJoin) { - return safePositionToJoin; - } + private static long? PositionToSafeJoinPosition(long? safePositionToJoin) => safePositionToJoin; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.Item.cs b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.Item.cs index db75855945a..40aafa50115 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.Item.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.Item.cs @@ -6,17 +6,10 @@ namespace KurrentDB.Projections.Core.Services.Processing.MultiStream; public partial class MultiStreamMultiOutputCheckpointManager { - private class Item { + private class Item(CheckpointTag tag) { internal KurrentDB.Core.Data.ResolvedEvent? _result; - private readonly CheckpointTag _tag; - public Item(CheckpointTag tag) { - _tag = tag; - } - - public CheckpointTag Tag { - get { return _tag; } - } + public CheckpointTag Tag { get; } = tag; public void SetLoadedEvent(KurrentDB.Core.Data.ResolvedEvent eventLinkPair) { _result = eventLinkPair; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.cs b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.cs index d0395109ba1..c3bce3b2cd4 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamMultiOutputCheckpointManager.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Security.Claims; -using KurrentDB.Common.Utils; using KurrentDB.Core.Bus; using KurrentDB.Core.Data; using KurrentDB.Core.Helpers; @@ -15,6 +14,7 @@ using KurrentDB.Projections.Core.Services.Processing.Emitting; using KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; using KurrentDB.Projections.Core.Services.Processing.Partitioning; +using KurrentDB.Projections.Core.Utils; namespace KurrentDB.Projections.Core.Services.Processing.MultiStream; @@ -24,19 +24,18 @@ public partial class MultiStreamMultiOutputCheckpointManager : DefaultCheckpoint private EmittedStream _orderStream; private bool _orderStreamReadingCompleted; private int _loadingItemsCount; - private readonly Stack _loadQueue = new Stack(); + private readonly Stack _loadQueue = new(); private CheckpointTag _loadingPrerecordedEventsFrom; - private static readonly char[] _linkToSeparator = new[] { '@' }; + private static readonly char[] LinkToSeparator = ['@']; public MultiStreamMultiOutputCheckpointManager( IPublisher publisher, Guid projectionCorrelationId, ProjectionVersion projectionVersion, ClaimsPrincipal runAs, - IODispatcher ioDispatcher, ProjectionConfig projectionConfig, string name, PositionTagger positionTagger, - ProjectionNamesBuilder namingBuilder, bool usePersistentCheckpoints, bool producesRunningResults, - bool definesFold, + IODispatcher ioDispatcher, ProjectionConfig projectionConfig, PositionTagger positionTagger, + ProjectionNamesBuilder namingBuilder, bool usePersistentCheckpoints, CoreProjectionCheckpointWriter coreProjectionCheckpointWriter, int maxProjectionStateSize) : base( - publisher, projectionCorrelationId, projectionVersion, runAs, ioDispatcher, projectionConfig, name, - positionTagger, namingBuilder, usePersistentCheckpoints, producesRunningResults, definesFold, + publisher, projectionCorrelationId, projectionVersion, runAs, ioDispatcher, projectionConfig, + positionTagger, namingBuilder, usePersistentCheckpoints, coreProjectionCheckpointWriter, maxProjectionStateSize) { _positionTagger = positionTagger; } @@ -44,8 +43,7 @@ public MultiStreamMultiOutputCheckpointManager( public override void Initialize() { base.Initialize(); _lastOrderCheckpointTag = null; - if (_orderStream != null) - _orderStream.Dispose(); + _orderStream?.Dispose(); _orderStream = null; } @@ -55,8 +53,7 @@ public override void Start(CheckpointTag checkpointTag, PartitionState rootParti _orderStream.Start(); } - public override void RecordEventOrder( - ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, Action committed) { + public override void RecordEventOrder(ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, Action committed) { EnsureStarted(); if (_stopping) throw new InvalidOperationException("Stopping"); @@ -64,12 +61,12 @@ public override void RecordEventOrder( //TODO: -order stream requires correctly configured event expiration. // the best is to truncate using $startFrom, but $maxAge should be also acceptable _orderStream.EmitEvents( - new[] { - new EmittedDataEvent( + [ + new EmittedDataEvent( orderStreamName, Guid.NewGuid(), "$>", - false, resolvedEvent.PositionSequenceNumber + "@" + resolvedEvent.PositionStreamId, null, - orderCheckpointTag, _lastOrderCheckpointTag, v => committed()) - }); + false, $"{resolvedEvent.PositionSequenceNumber}@{resolvedEvent.PositionStreamId}", null, + orderCheckpointTag, _lastOrderCheckpointTag, _ => committed()) + ]); _lastOrderCheckpointTag = orderCheckpointTag; } @@ -78,10 +75,8 @@ private EmittedStream CreateOrderStream(CheckpointTag from) { return new EmittedStream( /* MUST NEVER SEND READY MESSAGE */ _namingBuilder.GetOrderStreamName(), - new EmittedStream.WriterConfiguration( - new EmittedStreamsWriter(_ioDispatcher), new EmittedStream.WriterConfiguration.StreamMetadata(), - SystemAccounts.System, 100, _logger), - _projectionVersion, _positionTagger, @from, _publisher, _ioDispatcher, this, noCheckpoints: true); + new(new EmittedStreamsWriter(_ioDispatcher), new(), SystemAccounts.System, 100, _logger), + _projectionVersion, _positionTagger, from, _ioDispatcher, this, noCheckpoints: true); } public override void GetStatistics(ProjectionStatistics info) { @@ -93,7 +88,6 @@ public override void GetStatistics(ProjectionStatistics info) { } } - public override void BeginLoadPrerecordedEvents(CheckpointTag checkpointTag) { BeginLoadPrerecordedEventsChunk(checkpointTag, -1); } @@ -150,12 +144,10 @@ private void BeginLoadPrerecordedEventsChunk(CheckpointTag checkpointTag, long f } private void EnqueuePrerecordedEvent(EventRecord @event, CheckpointTag tag) { - if (@event == null) - throw new ArgumentNullException("event"); - if (tag == null) - throw new ArgumentNullException("tag"); + ArgumentNullException.ThrowIfNull(@event); + ArgumentNullException.ThrowIfNull(tag); if (@event.EventType != "$>") - throw new ArgumentException("linkto ($>) event expected", "event"); + throw new ArgumentException("linkto ($>) event expected", nameof(@event)); _loadingItemsCount++; @@ -164,8 +156,8 @@ private void EnqueuePrerecordedEvent(EventRecord @event, CheckpointTag tag) { //NOTE: we do manual link-to resolution as we write links to the position events // which may in turn be a link. This is necessary to provide a correct // ResolvedEvent when replaying from the -order stream - var linkTo = Helper.UTF8NoBom.GetString(@event.Data.Span); - string[] parts = linkTo.Split(_linkToSeparator, 2); + var linkTo = @event.Data.FromUtf8(); + string[] parts = linkTo.Split(LinkToSeparator, 2); long eventNumber = long.Parse(parts[0]); string streamId = parts[1]; @@ -180,7 +172,7 @@ private void EnqueuePrerecordedEvent(EventRecord @event, CheckpointTag tag) { CheckAllEventsLoaded(); break; default: - throw new Exception(string.Format("Cannot read {0}. Error: {1}", linkTo, completed.Error)); + throw new Exception($"Cannot read {linkTo}. Error: {completed.Error}"); } }); } @@ -225,7 +217,5 @@ public void Handle(CoreProjectionProcessingMessage.EmittedStreamAwaiting message } public void Handle(CoreProjectionProcessingMessage.EmittedStreamWriteCompleted message) { - if (_stopped) - return; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamPositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamPositionTagger.cs index 62ee72cb787..b07be942de9 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamPositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/MultiStream/MultiStreamPositionTagger.cs @@ -14,11 +14,10 @@ public class MultiStreamPositionTagger : PositionTagger { private readonly HashSet _streams; public MultiStreamPositionTagger(int phase, string[] streams) : base(phase) { - if (streams == null) - throw new ArgumentNullException("streams"); + ArgumentNullException.ThrowIfNull(streams); if (streams.Length == 0) - throw new ArgumentException("streams"); - _streams = new HashSet(streams); + throw new ArgumentException(null, nameof(streams)); + _streams = new(streams); } public override bool IsMessageAfterCheckpointTag( @@ -26,7 +25,7 @@ public override bool IsMessageAfterCheckpointTag( if (previous.Phase < Phase) return true; if (previous.Mode_ != CheckpointTag.Mode.MultiStream) - throw new ArgumentException("Mode.MultiStream expected", "previous"); + throw new ArgumentException("Mode.MultiStream expected", nameof(previous)); return _streams.Contains(committedEvent.Data.PositionStreamId) && committedEvent.Data.PositionSequenceNumber > previous.Streams[committedEvent.Data.PositionStreamId]; @@ -35,14 +34,11 @@ public override bool IsMessageAfterCheckpointTag( public override CheckpointTag MakeCheckpointTag( CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); + throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}"); if (!_streams.Contains(committedEvent.Data.PositionStreamId)) - throw new InvalidOperationException( - string.Format("Invalid stream '{0}'", committedEvent.Data.EventStreamId)); - return previous.UpdateStreamPosition( - committedEvent.Data.PositionStreamId, committedEvent.Data.PositionSequenceNumber); + throw new InvalidOperationException($"Invalid stream '{committedEvent.Data.EventStreamId}'"); + return previous.UpdateStreamPosition(committedEvent.Data.PositionStreamId, committedEvent.Data.PositionSequenceNumber); } public override CheckpointTag MakeCheckpointTag(CheckpointTag previous, @@ -52,7 +48,7 @@ public override CheckpointTag MakeCheckpointTag(CheckpointTag previous, public override CheckpointTag MakeZeroCheckpointTag() { return CheckpointTag.FromStreamPositions(Phase, - _streams.ToDictionary(v => v, v => (long)ExpectedVersion.NoStream)); + _streams.ToDictionary(v => v, _ => ExpectedVersion.NoStream)); } public override bool IsCompatible(CheckpointTag checkpointTag) { @@ -66,33 +62,24 @@ public override CheckpointTag AdjustTag(CheckpointTag tag) { return tag; if (tag.Phase > Phase) throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected less or equal to: {0} Was: {1}", Phase, - tag.Phase), "tag"); + $"Invalid checkpoint tag phase. Expected less or equal to: {Phase} Was: {tag.Phase}", nameof(tag)); if (tag.Mode_ == CheckpointTag.Mode.MultiStream) { - long p; return CheckpointTag.FromStreamPositions( - tag.Phase, _streams.ToDictionary(v => v, v => tag.Streams.TryGetValue(v, out p) ? p : -1)); + tag.Phase, _streams.ToDictionary(v => v, v => tag.Streams.TryGetValue(v, out var p) ? p : -1)); } - switch (tag.Mode_) { - case CheckpointTag.Mode.EventTypeIndex: - throw new NotSupportedException( - "Conversion from EventTypeIndex to MultiStream position tag is not supported"); - case CheckpointTag.Mode.Stream: - long p; - return CheckpointTag.FromStreamPositions( - tag.Phase, _streams.ToDictionary(v => v, v => tag.Streams.TryGetValue(v, out p) ? p : -1)); - case CheckpointTag.Mode.PreparePosition: - throw new NotSupportedException( - "Conversion from PreparePosition to MultiStream position tag is not supported"); - case CheckpointTag.Mode.Position: - throw new NotSupportedException( - "Conversion from Position to MultiStream position tag is not supported"); - default: - throw new NotSupportedException(string.Format( - "The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {0}", - tag.ToString())); - } + return tag.Mode_ switch { + CheckpointTag.Mode.EventTypeIndex => throw new NotSupportedException( + "Conversion from EventTypeIndex to MultiStream position tag is not supported"), + CheckpointTag.Mode.Stream => CheckpointTag.FromStreamPositions(tag.Phase, + _streams.ToDictionary(v => v, v => tag.Streams.TryGetValue(v, out var p) ? p : -1)), + CheckpointTag.Mode.PreparePosition => throw new NotSupportedException( + "Conversion from PreparePosition to MultiStream position tag is not supported"), + CheckpointTag.Mode.Position => throw new NotSupportedException( + "Conversion from Position to MultiStream position tag is not supported"), + _ => throw new NotSupportedException( + $"The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {tag}") + }; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByHandleStatePartitionSelector.cs b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByHandleStatePartitionSelector.cs index 890d04187dd..bc33cbb5099 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByHandleStatePartitionSelector.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByHandleStatePartitionSelector.cs @@ -5,15 +5,9 @@ namespace KurrentDB.Projections.Core.Services.Processing.Partitioning; -public class ByHandleStatePartitionSelector : StatePartitionSelector { - private readonly IProjectionStateHandler _handler; - - public ByHandleStatePartitionSelector(IProjectionStateHandler handler) { - _handler = handler; - } - +public class ByHandleStatePartitionSelector(IProjectionStateHandler handler) : StatePartitionSelector { public override string GetStatePartition(EventReaderSubscriptionMessage.CommittedEventReceived @event) { - return _handler.GetStatePartition(@event.CheckpointTag, @event.EventCategory, @event.Data); + return handler.GetStatePartition(@event.CheckpointTag, @event.EventCategory, @event.Data); } public override bool EventReaderBasePartitionDeletedIsSupported() { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByStreamStatePartitionSelector.cs b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByStreamStatePartitionSelector.cs index 4c4a0370075..ec1bdf56b89 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByStreamStatePartitionSelector.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/ByStreamStatePartitionSelector.cs @@ -12,8 +12,7 @@ public class ByStreamStatePartitionSelector : StatePartitionSelector { public override string GetStatePartition(EventReaderSubscriptionMessage.CommittedEventReceived @event) { if (@event.Data.ResolvedLinkTo && @event.Data.PositionMetadata != null) { var extra = @event.Data.PositionMetadata.ParseCheckpointExtraJson(); - JToken v; - if (extra != null && extra.TryGetValue("$o", out v)) { + if (extra != null && extra.TryGetValue("$o", out var v)) { //TODO: handle exceptions properly var originalStream = (string)((JValue)v).Value; return originalStream; @@ -22,7 +21,7 @@ public override string GetStatePartition(EventReaderSubscriptionMessage.Committe var eventStreamId = @event.Data.EventStreamId; return SystemStreams.IsMetastream(eventStreamId) - ? eventStreamId.Substring("$$".Length) + ? eventStreamId["$$".Length..] : eventStreamId; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/NoopStatePartitionSelector.cs b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/NoopStatePartitionSelector.cs index 46d02c38375..fd7f315534a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/NoopStatePartitionSelector.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/NoopStatePartitionSelector.cs @@ -6,11 +6,7 @@ namespace KurrentDB.Projections.Core.Services.Processing.Partitioning; public class NoopStatePartitionSelector : StatePartitionSelector { - public override string GetStatePartition(EventReaderSubscriptionMessage.CommittedEventReceived @event) { - return ""; - } + public override string GetStatePartition(EventReaderSubscriptionMessage.CommittedEventReceived @event) => ""; - public override bool EventReaderBasePartitionDeletedIsSupported() { - return false; - } + public override bool EventReaderBasePartitionDeletedIsSupported() => false; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionState.cs b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionState.cs index 391281a5658..4987813a61e 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionState.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionState.cs @@ -10,28 +10,23 @@ namespace KurrentDB.Projections.Core.Services.Processing.Partitioning; public class PartitionState { - private static readonly JsonSerializerSettings JsonSettings = new JsonSerializerSettings { - DateParseHandling = DateParseHandling.None, - }; + private static readonly JsonSerializerSettings JsonSettings = new() { DateParseHandling = DateParseHandling.None }; - public bool IsChanged(PartitionState newState) { - return State != newState.State || Result != newState.Result; - } + public bool IsChanged(PartitionState newState) => State != newState.State || Result != newState.Result; public static PartitionState Deserialize(string serializedState, CheckpointTag causedBy) { if (serializedState == null) - return new PartitionState("", null, causedBy); + return new("", null, causedBy); JToken state = null; JToken result = null; if (!string.IsNullOrEmpty(serializedState)) { var deserialized = JsonConvert.DeserializeObject(serializedState, JsonSettings); - var array = deserialized as JArray; - if (array != null && array.Count > 0) { - state = array[0] as JToken; + if (deserialized is JArray { Count: > 0 } array) { + state = array[0]; if (array.Count == 2) { - result = array[1] as JToken; + result = array[1]; } } else { state = deserialized as JObject; @@ -39,50 +34,29 @@ public static PartitionState Deserialize(string serializedState, CheckpointTag c } var stateJson = state != null ? state.ToCanonicalJson() : ""; - var resultJson = result != null ? result.ToCanonicalJson() : null; + var resultJson = result?.ToCanonicalJson(); - return new PartitionState(stateJson, resultJson, causedBy); + return new(stateJson, resultJson, causedBy); } - private readonly string _state; - private readonly string _result; - private readonly CheckpointTag _causedBy; - private readonly int _size; - public PartitionState(string state, string result, CheckpointTag causedBy) { - if (state == null) - throw new ArgumentNullException("state"); - if (causedBy == null) - throw new ArgumentNullException("causedBy"); + ArgumentNullException.ThrowIfNull(state); + ArgumentNullException.ThrowIfNull(causedBy); - _state = state; - _result = result; - _causedBy = causedBy; - _size = _state.Length + (_result?.Length ?? 0); + State = state; + Result = result; + CausedBy = causedBy; + Size = State.Length + (Result?.Length ?? 0); } - public string State { - get { return _state; } - } - - public CheckpointTag CausedBy { - get { return _causedBy; } - } - - public string Result { - get { return _result; } - } - - public int Size { - get { return _size; } - } + public string State { get; } + public CheckpointTag CausedBy { get; } + public string Result { get; } + public int Size { get; } public string Serialize() { - var state = _state; - if (state == "" && Result != null) + if (State == "" && Result != null) throw new Exception("state == \"\" && Result != null"); - return Result != null - ? "[" + state + "," + _result + "]" - : "[" + state + "]"; + return Result != null ? $"[{State},{Result}]" : $"[{State}]"; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateCache.cs b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateCache.cs index 88624a3c045..c7fbc88e50e 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateCache.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateCache.cs @@ -8,110 +8,78 @@ namespace KurrentDB.Projections.Core.Services.Processing.Partitioning; -public class PartitionStateCache { - private readonly int _maxCachedPartitions; +public class PartitionStateCache(int maxCachedPartitions = 4000) { + private readonly LinkedList<(CheckpointTag Tag, string Partition)> _cacheOrder = []; + private readonly Dictionary _partitionStates = new(); + private CheckpointTag _unlockedBefore = CheckpointTag.Empty; + private readonly CheckpointTag _zeroPosition = CheckpointTag.Empty; - private readonly LinkedList> _cacheOrder = - new LinkedList>(); - - private readonly Dictionary> _partitionStates = - new Dictionary>(); - - private int _cachedItemCount; - - private CheckpointTag _unlockedBefore; - private readonly CheckpointTag _zeroPosition; - - public PartitionStateCache(int maxCachedPartitions = 4000) { - _zeroPosition = CheckpointTag.Empty; - _unlockedBefore = CheckpointTag.Empty; - _maxCachedPartitions = maxCachedPartitions; - } - - public int CachedItemCount { - get { return _cachedItemCount; } - } + public int CachedItemCount { get; private set; } public void Initialize() { _partitionStates.Clear(); - _cachedItemCount = 0; - + CachedItemCount = 0; _cacheOrder.Clear(); _unlockedBefore = _zeroPosition; } public void CacheAndLockPartitionState(string partition, PartitionState data, CheckpointTag at) { - if (partition == null) - throw new ArgumentNullException("partition"); - if (data == null) - throw new ArgumentNullException("data"); + ArgumentNullException.ThrowIfNull(partition); + ArgumentNullException.ThrowIfNull(data); EnsureCanLockPartitionAt(partition, at); - _partitionStates[partition] = Tuple.Create(data, at); - _cachedItemCount = _partitionStates.Count; + _partitionStates[partition] = (data, at); + CachedItemCount = _partitionStates.Count; if (!string.IsNullOrEmpty(partition)) // cached forever - for root state - _cacheOrder.AddLast(Tuple.Create(at, partition)); + _cacheOrder.AddLast((at, partition)); CleanUp(); } public void CachePartitionState(string partition, PartitionState data) { - if (partition == null) - throw new ArgumentNullException("partition"); - if (data == null) - throw new ArgumentNullException("data"); + ArgumentNullException.ThrowIfNull(partition); + ArgumentNullException.ThrowIfNull(data); - _partitionStates[partition] = Tuple.Create(data, _zeroPosition); - _cachedItemCount = _partitionStates.Count; + _partitionStates[partition] = (data, _zeroPosition); + CachedItemCount = _partitionStates.Count; - _cacheOrder.AddFirst(Tuple.Create(_zeroPosition, partition)); + _cacheOrder.AddFirst((_zeroPosition, partition)); CleanUp(); } public PartitionState TryGetAndLockPartitionState(string partition, CheckpointTag lockAt) { - if (partition == null) - throw new ArgumentNullException("partition"); - Tuple stateData; - if (!_partitionStates.TryGetValue(partition, out stateData)) + ArgumentNullException.ThrowIfNull(partition); + if (!_partitionStates.TryGetValue(partition, out var stateData)) return null; EnsureCanLockPartitionAt(partition, lockAt); - if (lockAt != null && lockAt <= stateData.Item2) + if (lockAt != null && lockAt <= stateData.Tag) throw new InvalidOperationException( - string.Format( - "Attempt to relock the '{0}' partition state locked at the '{1}' position at the earlier position '{2}'", - partition, stateData.Item2, lockAt)); + $"Attempt to relock the '{partition}' partition state locked at the '{stateData.Tag}' position at the earlier position '{lockAt}'"); - _partitionStates[partition] = Tuple.Create(stateData.Item1, lockAt); - _cachedItemCount = _partitionStates.Count; + _partitionStates[partition] = (stateData.State, lockAt); + CachedItemCount = _partitionStates.Count; if (!string.IsNullOrEmpty(partition)) // cached forever - for root state - _cacheOrder.AddLast(Tuple.Create(lockAt, partition)); + _cacheOrder.AddLast((lockAt, partition)); CleanUp(); - return stateData.Item1; + return stateData.State; } public PartitionState TryGetPartitionState(string partition) { - if (partition == null) - throw new ArgumentNullException("partition"); - Tuple stateData; - if (!_partitionStates.TryGetValue(partition, out stateData)) - return null; - return stateData.Item1; + ArgumentNullException.ThrowIfNull(partition); + return !_partitionStates.TryGetValue(partition, out var stateData) ? null : stateData.Item1; } public PartitionState GetLockedPartitionState(string partition) { - Tuple stateData; - if (!_partitionStates.TryGetValue(partition, out stateData)) { + if (!_partitionStates.TryGetValue(partition, out var stateData)) { throw new InvalidOperationException( - string.Format( - "Partition '{0}' state was requested as locked but it is missing in the cache.", partition)); + $"Partition '{partition}' state was requested as locked but it is missing in the cache."); } - if (stateData.Item2 != null && stateData.Item2 <= _unlockedBefore) + if (stateData.Tag != null && stateData.Tag <= _unlockedBefore) throw new InvalidOperationException( - string.Format( - "Partition '{0}' state was requested as locked but it is cached as unlocked", partition)); - return stateData.Item1; + $"Partition '{partition}' state was requested as locked but it is cached as unlocked"); + return stateData.State; } public void Unlock(CheckpointTag beforeCheckpoint, bool forgetUnlocked = false) { @@ -120,37 +88,33 @@ public void Unlock(CheckpointTag beforeCheckpoint, bool forgetUnlocked = false) } private void CleanUp(bool removeAllUnlocked = false) { - while (removeAllUnlocked || _cacheOrder.Count > _maxCachedPartitions * 5 - || CachedItemCount > _maxCachedPartitions) { + while (removeAllUnlocked || _cacheOrder.Count > maxCachedPartitions * 5 + || CachedItemCount > maxCachedPartitions) { if (_cacheOrder.Count == 0) break; - Tuple top = _cacheOrder.FirstOrDefault(); - if (top.Item1 >= _unlockedBefore) + var top = _cacheOrder.FirstOrDefault(); + if (top.Tag >= _unlockedBefore) break; // other entries were locked after the checkpoint (or almost .. order is not very strong) _cacheOrder.RemoveFirst(); - Tuple entry; - if (!_partitionStates.TryGetValue(top.Item2, out entry)) + if (!_partitionStates.TryGetValue(top.Partition, out var entry)) continue; // already removed - if (entry.Item2 >= _unlockedBefore) + if (entry.Tag >= _unlockedBefore) continue; // was relocked - _partitionStates.Remove(top.Item2); - _cachedItemCount = _partitionStates.Count; + _partitionStates.Remove(top.Partition); + CachedItemCount = _partitionStates.Count; } } private void EnsureCanLockPartitionAt(string partition, CheckpointTag at) { - if (partition == null) - throw new ArgumentNullException("partition"); + ArgumentNullException.ThrowIfNull(partition); if (at == null && partition != "") throw new InvalidOperationException("Only the root partition can be locked forever"); if (partition == "" && at != null) throw new InvalidOperationException("Root partition must be locked forever"); if (at != null && at <= _unlockedBefore) throw new InvalidOperationException( - string.Format( - "Attempt to lock the '{0}' partition state at the position '{1}' before the unlocked position '{2}'", - partition, at, _unlockedBefore)); + $"Attempt to lock the '{partition}' partition state at the position '{at}' before the unlocked position '{_unlockedBefore}'"); } public IEnumerable> Enumerate() { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateUpdateManager.cs b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateUpdateManager.cs index 8ccf2e68b43..4e247583cdc 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateUpdateManager.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Partitioning/PartitionStateUpdateManager.cs @@ -16,21 +16,17 @@ private class State { public CheckpointTag ExpectedTag; } - private readonly Dictionary _states = new Dictionary(); + private readonly Dictionary _states = new(); private readonly ProjectionNamesBuilder _namingBuilder; - - private readonly EmittedStream.WriterConfiguration.StreamMetadata _partitionCheckpointStreamMetadata = - new EmittedStream.WriterConfiguration.StreamMetadata(maxCount: 2); + private readonly EmittedStream.WriterConfiguration.StreamMetadata _partitionCheckpointStreamMetadata = new(maxCount: 2); public PartitionStateUpdateManager(ProjectionNamesBuilder namingBuilder) { - if (namingBuilder == null) - throw new ArgumentNullException("namingBuilder"); + ArgumentNullException.ThrowIfNull(namingBuilder); _namingBuilder = namingBuilder; } public void StateUpdated(string partition, PartitionState state, CheckpointTag basedOn) { - State stateEntry; - if (_states.TryGetValue(partition, out stateEntry)) { + if (_states.TryGetValue(partition, out var stateEntry)) { stateEntry.PartitionState = state; } else { _states.Add(partition, new State { PartitionState = state, ExpectedTag = basedOn }); @@ -38,29 +34,22 @@ public void StateUpdated(string partition, PartitionState state, CheckpointTag b } public void EmitEvents(IEventWriter eventWriter) { - if (_states.Count > 0) { - var list = new List(); - foreach (var entry in _states) { - var partition = entry.Key; - var streamId = _namingBuilder.MakePartitionCheckpointStreamName(partition); - var data = entry.Value.PartitionState.Serialize(); - var causedBy = entry.Value.PartitionState.CausedBy; - var expectedTag = entry.Value.ExpectedTag; - list.Add( - new EmittedEventEnvelope( - new EmittedDataEvent( - streamId, Guid.NewGuid(), ProjectionEventTypes.PartitionCheckpoint, true, - data, null, causedBy, expectedTag), _partitionCheckpointStreamMetadata)); - } - - //NOTE: order yb is required to satisfy internal emit events validation - // which ensures that events are ordered by causedBy tag. - // it is too strong check, but ... - eventWriter.ValidateOrderAndEmitEvents(list.OrderBy(v => v.Event.CausedByTag).ToArray()); + if (_states.Count <= 0) return; + + var list = new List(); + foreach (var (partition, state) in _states) { + var streamId = _namingBuilder.MakePartitionCheckpointStreamName(partition); + var data = state.PartitionState.Serialize(); + var causedBy = state.PartitionState.CausedBy; + var expectedTag = state.ExpectedTag; + list.Add( + new(new EmittedDataEvent(streamId, Guid.NewGuid(), ProjectionEventTypes.PartitionCheckpoint, true, + data, null, causedBy, expectedTag), _partitionCheckpointStreamMetadata)); } - } - public void PartitionCompleted(string partition) { - _states.Remove(partition); + //NOTE: order yb is required to satisfy internal emit events validation + // which ensures that events are ordered by causedBy tag. + // it is too strong check, but ... + eventWriter.ValidateOrderAndEmitEvents(list.OrderBy(v => v.Event.CausedByTag).ToArray()); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventProcessingProjectionProcessingPhase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventProcessingProjectionProcessingPhase.cs index 2d49fc6986a..190553d0a4b 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventProcessingProjectionProcessingPhase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventProcessingProjectionProcessingPhase.cs @@ -16,21 +16,18 @@ namespace KurrentDB.Projections.Core.Services.Processing.Phases; -public class EventProcessingProjectionProcessingPhase : EventSubscriptionBasedProjectionProcessingPhase, - IHandle, - IHandle, - IEventProcessingProjectionPhase { +public class EventProcessingProjectionProcessingPhase + : EventSubscriptionBasedProjectionProcessingPhase, + IHandle, + IHandle, + IEventProcessingProjectionPhase { private readonly IProjectionStateHandler _projectionStateHandler; private readonly bool _definesStateTransform; private readonly StatePartitionSelector _statePartitionSelector; private readonly bool _isBiState; - + private readonly Stopwatch _stopwatch = new(); private string _handlerPartition; - //private bool _sharedStateSet; - private readonly Stopwatch _stopwatch; - - public EventProcessingProjectionProcessingPhase( CoreProjection coreProjection, Guid projectionCorrelationId, @@ -80,8 +77,6 @@ public EventProcessingProjectionProcessingPhase( _definesStateTransform = definesStateTransform; _statePartitionSelector = statePartitionSelector; _isBiState = isBiState; - - _stopwatch = new Stopwatch(); } public void Handle(EventReaderSubscriptionMessage.CommittedEventReceived message) { @@ -118,7 +113,8 @@ public void Handle(EventReaderSubscriptionMessage.PartitionDeleted message) { } public EventProcessedResult ProcessCommittedEvent( - EventReaderSubscriptionMessage.CommittedEventReceived message, string partition) { + EventReaderSubscriptionMessage.CommittedEventReceived message, + string partition) { switch (_state) { case PhaseState.Running: var result = InternalProcessCommittedEvent(partition, message); @@ -144,15 +140,10 @@ public EventProcessedResult ProcessPartitionDeleted(string partition, Checkpoint } } - private EventProcessedResult InternalProcessCommittedEvent( - string partition, EventReaderSubscriptionMessage.CommittedEventReceived message) { - string newState; - string projectionResult; - EmittedEventEnvelope[] emittedEvents; - //TODO: support shared state - string newSharedState; + private EventProcessedResult InternalProcessCommittedEvent(string partition, + EventReaderSubscriptionMessage.CommittedEventReceived message) { var hasBeenProcessed = SafeProcessEventByHandler( - partition, message, out newState, out newSharedState, out projectionResult, out emittedEvents); + partition, message, out var newState, out var newSharedState, out var projectionResult, out var emittedEvents); if (hasBeenProcessed) { var newPartitionState = new PartitionState(newState, projectionResult, message.CheckpointTag); var newSharedPartitionState = newSharedState != null @@ -166,12 +157,9 @@ private EventProcessedResult InternalProcessCommittedEvent( return null; } - private EventProcessedResult InternalProcessPartitionDeleted( - string partition, CheckpointTag deletedPosition) { - string newState; - string projectionResult; + private EventProcessedResult InternalProcessPartitionDeleted(string partition, CheckpointTag deletedPosition) { var hasBeenProcessed = SafeProcessPartitionDeletedByHandler( - partition, deletedPosition, out newState, out projectionResult); + partition, deletedPosition, out var newState, out var projectionResult); if (hasBeenProcessed) { var newPartitionState = new PartitionState(newState, projectionResult, deletedPosition); @@ -182,8 +170,12 @@ private EventProcessedResult InternalProcessPartitionDeleted( } private bool SafeProcessEventByHandler( - string partition, EventReaderSubscriptionMessage.CommittedEventReceived message, out string newState, - out string newSharedState, out string projectionResult, out EmittedEventEnvelope[] emittedEvents) { + string partition, + EventReaderSubscriptionMessage.CommittedEventReceived message, + out string newState, + out string newSharedState, + out string projectionResult, + out EmittedEventEnvelope[] emittedEvents) { projectionResult = null; //TODO: not emitting (optimized) projection handlers can skip serializing state on each processed event bool hasBeenProcessed; @@ -192,55 +184,58 @@ private bool SafeProcessEventByHandler( partition, message, out newState, out newSharedState, out projectionResult, out emittedEvents); } catch (Exception ex) { // update progress to reflect exact fault position - _checkpointManager.Progress(message.Progress); + CheckpointManager.Progress(message.Progress); SetFaulting( - String.Format( - "The {0} projection failed to process an event.\r\nHandler: {1}\r\nEvent Position: {2}\r\n\r\nMessage:\r\n\r\n{3}", - _projectionName, GetHandlerTypeName(), message.CheckpointTag, ex.Message), ex); + $"The {_projectionName} projection failed to process an event.\r\nHandler: {GetHandlerTypeName()}\r\nEvent Position: {message.CheckpointTag}\r\n\r\nMessage:\r\n\r\n{ex.Message}", + ex); newState = null; newSharedState = null; emittedEvents = null; hasBeenProcessed = false; } - newState = newState ?? ""; + newState ??= ""; return hasBeenProcessed; } private bool SafeProcessPartitionDeletedByHandler( - string partition, CheckpointTag deletedPosition, out string newState, + string partition, + CheckpointTag deletedPosition, + out string newState, out string projectionResult) { projectionResult = null; //TODO: not emitting (optimized) projection handlers can skip serializing state on each processed event bool hasBeenProcessed; try { - hasBeenProcessed = ProcessPartitionDeletedByHandler( - partition, deletedPosition, out newState, out projectionResult); + hasBeenProcessed = ProcessPartitionDeletedByHandler(partition, deletedPosition, out newState, out projectionResult); } catch (Exception ex) { SetFaulting( - String.Format( - "The {0} projection failed to process a delete partition notification.\r\nHandler: {1}\r\nEvent Position: {2}\r\n\r\nMessage:\r\n\r\n{3}", - _projectionName, GetHandlerTypeName(), deletedPosition, ex.Message), ex); + $"The {_projectionName} projection failed to process a delete partition notification.\r\nHandler: {GetHandlerTypeName()}\r\nEvent Position: {deletedPosition}\r\n\r\nMessage:\r\n\r\n{ex.Message}", + ex); newState = null; hasBeenProcessed = false; } - newState = newState ?? ""; + newState ??= ""; return hasBeenProcessed; } private string GetHandlerTypeName() { - return _projectionStateHandler.GetType().Namespace + "." + _projectionStateHandler.GetType().Name; + return $"{_projectionStateHandler.GetType().Namespace}.{_projectionStateHandler.GetType().Name}"; } private bool ProcessEventByHandler( - string partition, EventReaderSubscriptionMessage.CommittedEventReceived message, out string newState, - out string newSharedState, out string projectionResult, out EmittedEventEnvelope[] emittedEvents) { + string partition, + EventReaderSubscriptionMessage.CommittedEventReceived message, + out string newState, + out string newSharedState, + out string projectionResult, + out EmittedEventEnvelope[] emittedEvents) { projectionResult = null; - var newPatitionInitialized = InitOrLoadHandlerState(partition); + var newPartitionInitialized = InitOrLoadHandlerState(partition); _stopwatch.Start(); EmittedEventEnvelope[] eventsEmittedOnInitialization = null; - if (newPatitionInitialized) { + if (newPartitionInitialized) { _projectionStateHandler.ProcessPartitionCreated( partition, message.CheckpointTag, message.Data, out eventsEmittedOnInitialization); } @@ -251,48 +246,36 @@ private bool ProcessEventByHandler( if (result) { var oldState = _partitionStateCache.GetLockedPartitionState(partition); //TODO: depending on query processing final state to result transformation should happen either here (if EOF) on while writing results - if ( /*_producesRunningResults && */oldState.State != newState) { - if (_definesStateTransform) { - projectionResult = _projectionStateHandler.TransformStateToResult(); - } else { - projectionResult = newState; - } - } else { - projectionResult = oldState.Result; - } + projectionResult = oldState.State != newState + ? _definesStateTransform ? _projectionStateHandler.TransformStateToResult() : newState + : oldState.Result; } _stopwatch.Stop(); if (eventsEmittedOnInitialization != null) { - if (emittedEvents == null || emittedEvents.Length == 0) - emittedEvents = eventsEmittedOnInitialization; - else - emittedEvents = eventsEmittedOnInitialization.Concat(emittedEvents).ToArray(); + emittedEvents = emittedEvents == null || emittedEvents.Length == 0 + ? eventsEmittedOnInitialization + : eventsEmittedOnInitialization.Concat(emittedEvents).ToArray(); } return result; } private bool ProcessPartitionDeletedByHandler( - string partition, CheckpointTag deletePosition, out string newState, + string partition, + CheckpointTag deletePosition, + out string newState, out string projectionResult) { projectionResult = null; InitOrLoadHandlerState(partition); _stopwatch.Start(); - var result = _projectionStateHandler.ProcessPartitionDeleted( - partition, deletePosition, out newState); + var result = _projectionStateHandler.ProcessPartitionDeleted(partition, deletePosition, out newState); if (result) { var oldState = _partitionStateCache.GetLockedPartitionState(partition); //TODO: depending on query processing final state to result transformation should happen either here (if EOF) on while writing results - if ( /*_producesRunningResults && */oldState.State != newState) { - if (_definesStateTransform) { - projectionResult = _projectionStateHandler.TransformStateToResult(); - } else { - projectionResult = newState; - } - } else { - projectionResult = oldState.Result; - } + projectionResult = oldState.State != newState + ? _definesStateTransform ? _projectionStateHandler.TransformStateToResult() : newState + : oldState.Result; } _stopwatch.Stop(); @@ -311,17 +294,16 @@ private bool InitOrLoadHandlerState(string partition) { var newState = _partitionStateCache.GetLockedPartitionState(partition); _handlerPartition = partition; var initialized = false; - if (newState != null && !String.IsNullOrEmpty(newState.State)) + if (newState != null && !string.IsNullOrEmpty(newState.State)) _projectionStateHandler.Load(newState.State); else { initialized = true; _projectionStateHandler.Initialize(); } - //if (!_sharedStateSet && _isBiState) if (_isBiState) { var newSharedState = _partitionStateCache.GetLockedPartitionState(""); - if (newSharedState != null && !String.IsNullOrEmpty(newSharedState.State)) + if (newSharedState != null && !string.IsNullOrEmpty(newSharedState.State)) _projectionStateHandler.LoadShared(newSharedState.State); else _projectionStateHandler.InitializeShared(); @@ -331,33 +313,29 @@ private bool InitOrLoadHandlerState(string partition) { } public override void NewCheckpointStarted(CheckpointTag at) { - if (!(_state == PhaseState.Running || _state == PhaseState.Starting)) { + if (_state is not (PhaseState.Running or PhaseState.Starting)) { _logger.Debug("Starting a checkpoint in non-runnable state"); return; } - var checkpointHandler = _projectionStateHandler as IProjectionCheckpointHandler; - if (checkpointHandler != null) { - EmittedEventEnvelope[] emittedEvents; - try { - checkpointHandler.ProcessNewCheckpoint(at, out emittedEvents); - } catch (Exception ex) { - var faultedReason = - String.Format( - "The {0} projection failed to process a checkpoint start.\r\nHandler: {1}\r\nEvent Position: {2}\r\n\r\nMessage:\r\n\r\n{3}", - _projectionName, GetHandlerTypeName(), at, ex.Message); - SetFaulting(faultedReason, ex); - emittedEvents = null; - } + if (_projectionStateHandler is not IProjectionCheckpointHandler checkpointHandler) return; + + EmittedEventEnvelope[] emittedEvents; + try { + checkpointHandler.ProcessNewCheckpoint(at, out emittedEvents); + } catch (Exception ex) { + var faultedReason = + $"The {_projectionName} projection failed to process a checkpoint start.\r\nHandler: {GetHandlerTypeName()}\r\nEvent Position: {at}\r\n\r\nMessage:\r\n\r\n{ex.Message}"; + SetFaulting(faultedReason, ex); + emittedEvents = null; + } - if (emittedEvents != null && emittedEvents.Length > 0) { - if (!ValidateEmittedEvents(emittedEvents)) - return; + if (emittedEvents is { Length: > 0 }) { + if (!ValidateEmittedEvents(emittedEvents)) + return; - if (_state == PhaseState.Running || _state == PhaseState.Starting) - _resultWriter.EventsEmitted( - emittedEvents, Guid.Empty, correlationId: null); - } + if (_state is PhaseState.Running or PhaseState.Starting) + _resultWriter.EventsEmitted(emittedEvents, Guid.Empty, correlationId: null); } } @@ -367,7 +345,6 @@ public override void GetStatistics(ProjectionStatistics info) { } public override void Dispose() { - if (_projectionStateHandler != null) - _projectionStateHandler.Dispose(); + _projectionStateHandler?.Dispose(); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.ProgressResultWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.ProgressResultWriter.cs deleted file mode 100644 index c4a1cbfb6a0..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.ProgressResultWriter.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -using KurrentDB.Projections.Core.Services.Processing.Strategies; - -namespace KurrentDB.Projections.Core.Services.Processing.Phases; - -public abstract partial class EventSubscriptionBasedProjectionProcessingPhase { - internal class ProgressResultWriter : IProgressResultWriter { - private readonly EventSubscriptionBasedProjectionProcessingPhase _phase; - private readonly IResultWriter _resultWriter; - - public ProgressResultWriter(EventSubscriptionBasedProjectionProcessingPhase phase, - IResultWriter resultWriter) { - _phase = phase; - _resultWriter = resultWriter; - } - - public void WriteProgress(float progress) { - _resultWriter.WriteProgress(_phase._currentSubscriptionId, progress); - } - } -} diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.cs index c6b7c8c1946..a35c2c7dc3f 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/EventSubscriptionBasedProjectionProcessingPhase.cs @@ -20,42 +20,39 @@ namespace KurrentDB.Projections.Core.Services.Processing.Phases; -public abstract partial class EventSubscriptionBasedProjectionProcessingPhase : IProjectionPhaseCompleter, - IProjectionPhaseCheckpointManager, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IProjectionProcessingPhase, - IProjectionPhaseStateManager { - protected readonly IPublisher _publisher; - private readonly IPublisher _inputQueue; +public abstract class EventSubscriptionBasedProjectionProcessingPhase + : IProjectionPhaseCompleter, + IProjectionPhaseCheckpointManager, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IProjectionProcessingPhase, + IProjectionPhaseStateManager { + private readonly IPublisher _publisher; protected readonly ICoreProjectionForProcessingPhase _coreProjection; - protected readonly Guid _projectionCorrelationId; - protected readonly ICoreProjectionCheckpointManager _checkpointManager; - protected readonly IProgressResultWriter _progressResultWriter; - protected readonly ProjectionConfig _projectionConfig; + private readonly Guid _projectionCorrelationId; + private readonly ProjectionConfig _projectionConfig; protected readonly string _projectionName; protected readonly ILogger _logger; - protected readonly CheckpointTag _zeroCheckpointTag; + private readonly CheckpointTag _zeroCheckpointTag; protected readonly CoreProjectionQueue _processingQueue; protected readonly PartitionStateCache _partitionStateCache; - protected readonly ReaderSubscriptionDispatcher _subscriptionDispatcher; - protected readonly IReaderStrategy _readerStrategy; + private readonly ReaderSubscriptionDispatcher _subscriptionDispatcher; + private readonly IReaderStrategy _readerStrategy; protected readonly IResultWriter _resultWriter; - protected readonly bool _useCheckpoints; - protected long _expectedSubscriptionMessageSequenceNumber = -1; - protected Guid _currentSubscriptionId; - protected PhaseSubscriptionState _subscriptionState; + private readonly bool _useCheckpoints; + private long _expectedSubscriptionMessageSequenceNumber = -1; + private Guid _currentSubscriptionId; + private PhaseSubscriptionState _subscriptionState; protected PhaseState _state; - protected readonly bool _stopOnEof; + private readonly bool _stopOnEof; private readonly bool _isBiState; - protected readonly IEmittedStreamsTracker _emittedStreamsTracker; - protected readonly bool _enableContentTypeValidation; + private readonly bool _enableContentTypeValidation; private readonly Action _updateStatistics; @@ -81,10 +78,9 @@ protected EventSubscriptionBasedProjectionProcessingPhase( IEmittedStreamsTracker emittedStreamsTracker, bool enableContentTypeValidation) { _publisher = publisher; - _inputQueue = inputQueue; _coreProjection = coreProjection; _projectionCorrelationId = projectionCorrelationId; - _checkpointManager = checkpointManager; + CheckpointManager = checkpointManager; _projectionConfig = projectionConfig; _projectionName = projectionName; _logger = logger; @@ -92,43 +88,30 @@ protected EventSubscriptionBasedProjectionProcessingPhase( _partitionStateCache = partitionStateCache; _resultWriter = resultWriter; _updateStatistics = updateStatistics; - _processingQueue = new CoreProjectionQueue(publisher, - projectionConfig.PendingEventsThreshold, - orderedPartitionProcessing); + _processingQueue = new(publisher, projectionConfig.PendingEventsThreshold, orderedPartitionProcessing); _processingQueue.EnsureTickPending += EnsureTickPending; _subscriptionDispatcher = subscriptionDispatcher; _readerStrategy = readerStrategy; _useCheckpoints = useCheckpoints; _stopOnEof = stopOnEof; _isBiState = isBiState; - _progressResultWriter = new ProgressResultWriter(this, _resultWriter); - _inutQueueEnvelope = _inputQueue; - _emittedStreamsTracker = emittedStreamsTracker; + _inputQueueEnvelope = inputQueue; + EmittedStreamsTracker = emittedStreamsTracker; _enableContentTypeValidation = enableContentTypeValidation; } - public void UnlockAndForgetBefore(CheckpointTag checkpointTag) { - _partitionStateCache.Unlock(checkpointTag, forgetUnlocked: true); - } + public CheckpointTag LastProcessedEventPosition => _coreProjection.LastProcessedEventPosition; - public CheckpointTag LastProcessedEventPosition { - get { return _coreProjection.LastProcessedEventPosition; } - } + public ICoreProjectionCheckpointManager CheckpointManager { get; } - public ICoreProjectionCheckpointManager CheckpointManager { - get { return _checkpointManager; } - } - - public IEmittedStreamsTracker EmittedStreamsTracker { - get { return _emittedStreamsTracker; } - } + public IEmittedStreamsTracker EmittedStreamsTracker { get; } protected bool IsOutOfOrderSubscriptionMessage(EventReaderSubscriptionMessageBase message) { if (_currentSubscriptionId != message.SubscriptionId) return true; - if (_expectedSubscriptionMessageSequenceNumber != message.SubscriptionMessageSequenceNumber) - throw new InvalidOperationException("Out of order message detected"); - return false; + return _expectedSubscriptionMessageSequenceNumber != message.SubscriptionMessageSequenceNumber + ? throw new InvalidOperationException("Out of order message detected") + : false; } protected void RegisterSubscriptionMessage(EventReaderSubscriptionMessageBase message) { @@ -141,17 +124,17 @@ protected void EnsureTickPending() { public void ProcessEvent() { _processingQueue.ProcessEvent(); - EnsureUpdateStatisticksTickPending(); + EnsureUpdateStatisticsTickPending(); } - private void EnsureUpdateStatisticksTickPending() { + private void EnsureUpdateStatisticsTickPending() { if (_updateStatisticsTicketPending) return; _updateStatisticsTicketPending = true; _publisher.Publish( TimerMessage.Schedule.Create( _updateInterval, - _inutQueueEnvelope, + _inputQueueEnvelope, new UnwrapEnvelopeMessage(MarkTicketReceivedAndUpdateStatistics, nameof(MarkTicketReceivedAndUpdateStatistics)))); } @@ -161,8 +144,7 @@ private void MarkTicketReceivedAndUpdateStatistics() { } private void UpdateStatistics() { - if (_updateStatistics != null) - _updateStatistics(); + _updateStatistics?.Invoke(); } public void Handle(EventReaderSubscriptionMessage.ProgressChanged message) { @@ -170,8 +152,7 @@ public void Handle(EventReaderSubscriptionMessage.ProgressChanged message) { return; RegisterSubscriptionMessage(message); try { - var progressWorkItem = - new ProgressWorkItem(_checkpointManager, _progressResultWriter, message.Progress); + var progressWorkItem = new ProgressWorkItem(CheckpointManager, message.Progress); _processingQueue.EnqueueTask(progressWorkItem, message.CheckpointTag, allowCurrentPosition: true); ProcessEvent(); } catch (Exception ex) { @@ -184,7 +165,6 @@ public void Handle(EventReaderSubscriptionMessage.SubscriptionStarted message) { return; RegisterSubscriptionMessage(message); try { - _subscriptionStartedAtLastCommitPosition = message.StartingLastCommitPosition; } catch (Exception ex) { _coreProjection.SetFaulted(ex); } @@ -203,7 +183,7 @@ public void Handle(EventReaderSubscriptionMessage.NotAuthorized message) { } } - public void Unsubscribed() { + private void Unsubscribed() { _subscriptionDispatcher.Cancel(_projectionCorrelationId); _subscriptionState = PhaseSubscriptionState.Unsubscribed; _processingQueue.Unsubscribed(); @@ -230,10 +210,8 @@ public void Handle(EventReaderSubscriptionMessage.CheckpointSuggested message) { try { if (_useCheckpoints) { CheckpointTag checkpointTag = message.CheckpointTag; - var checkpointSuggestedWorkItem = - new CheckpointSuggestedWorkItem(this, message, _checkpointManager); - _processingQueue.EnqueueTask(checkpointSuggestedWorkItem, checkpointTag, - allowCurrentPosition: true); + var checkpointSuggestedWorkItem = new CheckpointSuggestedWorkItem(this, message, CheckpointManager); + _processingQueue.EnqueueTask(checkpointSuggestedWorkItem, checkpointTag, allowCurrentPosition: true); } ProcessEvent(); @@ -244,8 +222,7 @@ public void Handle(EventReaderSubscriptionMessage.CheckpointSuggested message) { public void Handle(CoreProjectionManagementMessage.GetState message) { try { - var getStateWorkItem = new GetStateWorkItem( - _publisher, message.CorrelationId, message.ProjectionId, this, message.Partition); + var getStateWorkItem = new GetStateWorkItem(_publisher, message.CorrelationId, message.ProjectionId, this, message.Partition); _processingQueue.EnqueueOutOfOrderTask(getStateWorkItem); ProcessEvent(); } catch (Exception ex) { @@ -273,8 +250,7 @@ public void Handle(CoreProjectionManagementMessage.GetResult message) { } public void Handle(EventReaderSubscriptionMessage.SubscribeTimeout message) { - if (_subscriptionState is not PhaseSubscriptionState.Subscribing - || message.SubscriptionId != _currentSubscriptionId) + if (_subscriptionState is not PhaseSubscriptionState.Subscribing || message.SubscriptionId != _currentSubscriptionId) return; SubscriptionFailed("Reader subscription timed out"); } @@ -288,25 +264,24 @@ private void SubscriptionFailed(string reason) { _coreProjection.SetFaulted(reason); } - protected void UnsubscribeFromPreRecordedOrderEvents() { + private void UnsubscribeFromPreRecordedOrderEvents() { // projectionCorrelationId is used as a subscription identifier for delivery // of pre-recorded order events recovered by checkpoint manager _subscriptionDispatcher.Cancel(_projectionCorrelationId); _subscriptionState = PhaseSubscriptionState.Unsubscribed; } - public void Subscribed(Guid subscriptionId) { + private void Subscribed(Guid subscriptionId) { _processingQueue.Subscribed(subscriptionId); } - public ReaderSubscriptionOptions GetSubscriptionOptions() { - return new ReaderSubscriptionOptions( + private ReaderSubscriptionOptions GetSubscriptionOptions() { + return new( _projectionConfig.CheckpointUnhandledBytesThreshold, _projectionConfig.CheckpointHandledThreshold, - _projectionConfig.CheckpointAfterMs, - _stopOnEof, stopAfterNEvents: null, _enableContentTypeValidation); + _projectionConfig.CheckpointAfterMs, _stopOnEof, stopAfterNEvents: null, _enableContentTypeValidation); } - protected void SubscribeReaders(CheckpointTag checkpointTag) { + private void SubscribeReaders(CheckpointTag checkpointTag) { _expectedSubscriptionMessageSequenceNumber = 0; _currentSubscriptionId = Guid.NewGuid(); Subscribed(_currentSubscriptionId); @@ -314,7 +289,7 @@ protected void SubscribeReaders(CheckpointTag checkpointTag) { if (readerStrategy != null) { _subscriptionState = PhaseSubscriptionState.Subscribing; _subscriptionDispatcher.PublishSubscribe( - new ReaderSubscriptionManagement.Subscribe( + new( _currentSubscriptionId, checkpointTag, readerStrategy, GetSubscriptionOptions()), this, scheduleTimeout: true); } else { @@ -322,7 +297,7 @@ protected void SubscribeReaders(CheckpointTag checkpointTag) { } } - public void SubscribeToPreRecordedOrderEvents() { + private void SubscribeToPreRecordedOrderEvents() { var coreProjection = (CoreProjection)_coreProjection; // projectionCorrelationId is used as a subscription identifier for delivery // of pre-recorded order events recovered by checkpoint manager @@ -334,12 +309,12 @@ public void SubscribeToPreRecordedOrderEvents() { } public virtual void Subscribe(CheckpointTag from, bool fromCheckpoint) { - Contract.Assert(_checkpointManager.LastProcessedEventPosition == @from); + Contract.Assert(CheckpointManager.LastProcessedEventPosition == from); if (fromCheckpoint) { SubscribeToPreRecordedOrderEvents(); - _checkpointManager.BeginLoadPrerecordedEvents(@from); + CheckpointManager.BeginLoadPrerecordedEvents(from); } else - SubscribeReaders(@from); + SubscribeReaders(from); } public void Handle(CoreProjectionProcessingMessage.PrerecordedEventsLoaded message) { @@ -364,7 +339,7 @@ protected void SetFaulting(string faultedReason, Exception ex = null) { protected bool ValidateEmittedEvents(EmittedEventEnvelope[] emittedEvents) { if (!_projectionConfig.EmitEventEnabled) { - if (emittedEvents != null && emittedEvents.Length > 0) { + if (emittedEvents is { Length: > 0 }) { SetFaulting("'emit' is not allowed by the projection/configuration/mode"); return false; } @@ -382,17 +357,15 @@ public void InitializeFromCheckpoint(CheckpointTag checkpointTag) { _processingQueue.InitializeQueue(adjustedCheckpointTag); } - public int GetBufferedEventCount() { - return _processingQueue.GetBufferedEventCount(); - } + private int GetBufferedEventCount() => _processingQueue.GetBufferedEventCount(); - public string GetStatus() { - return _processingQueue.GetStatus(); - } + private string GetStatus() => _processingQueue.GetStatus(); protected EventProcessedResult InternalCommittedEventProcessed( - string partition, EventReaderSubscriptionMessage.CommittedEventReceived message, - EmittedEventEnvelope[] emittedEvents, PartitionState newPartitionState, + string partition, + EventReaderSubscriptionMessage.CommittedEventReceived message, + EmittedEventEnvelope[] emittedEvents, + PartitionState newPartitionState, PartitionState newSharedPartitionState) { if (_subscriptionState != PhaseSubscriptionState.Subscribed) _logger?.Verbose("Got CommittedEventReceived in {state} SubscriptionState, but expected to be in {expectedState}", @@ -405,7 +378,7 @@ protected EventProcessedResult InternalCommittedEventProcessed( var oldState = _partitionStateCache.GetLockedPartitionState(partition); var oldSharedState = _isBiState ? _partitionStateCache.GetLockedPartitionState("") : null; bool changed = oldState.IsChanged(newPartitionState) - || (_isBiState && oldSharedState.IsChanged(newSharedPartitionState)); + || (_isBiState && oldSharedState?.IsChanged(newSharedPartitionState) == true); PartitionState partitionState = null; // NOTE: projectionResult cannot change independently unless projection definition has changed @@ -419,24 +392,24 @@ protected EventProcessedResult InternalCommittedEventProcessed( } if (changed || eventsWereEmitted) { - var correlationId = - message.Data.IsJson ? message.Data.Metadata.ParseCheckpointTagCorrelationId() : null; - return new EventProcessedResult( + var correlationId = message.Data.IsJson ? message.Data.Metadata.ParseCheckpointTagCorrelationId() : null; + return new( partition, message.CheckpointTag, oldState, partitionState, oldSharedState, newSharedPartitionState, emittedEvents, message.Data.EventId, correlationId); - } else - return null; + } + + return null; } protected EventProcessedResult InternalPartitionDeletedProcessed( - string partition, CheckpointTag deletePosition, + string partition, + CheckpointTag deletePosition, PartitionState newPartitionState ) { var oldState = _partitionStateCache.GetLockedPartitionState(partition); var oldSharedState = _isBiState ? _partitionStateCache.GetLockedPartitionState("") : null; bool changed = oldState.IsChanged(newPartitionState); - PartitionState partitionState = null; // NOTE: projectionResult cannot change independently unless projection definition has changed if (changed) { @@ -457,75 +430,67 @@ PartitionState newPartitionState correlationId: null); } - public void BeginGetPartitionStateAt( - string statePartition, CheckpointTag at, Action loadCompleted, bool lockLoaded) { + public void BeginGetPartitionStateAt(string statePartition, CheckpointTag at, Action loadCompleted, bool lockLoaded) { if (statePartition == "") // root is always cached { // root partition is always locked var state = _partitionStateCache.TryGetAndLockPartitionState(statePartition, null); loadCompleted(state); - } else { - var s = lockLoaded - ? _partitionStateCache.TryGetAndLockPartitionState(statePartition, at) - : _partitionStateCache.TryGetPartitionState(statePartition); - if (s != null) - loadCompleted(s); - else { - Action completed = state => { - if (lockLoaded) - _partitionStateCache.CacheAndLockPartitionState(statePartition, state, at); - else - _partitionStateCache.CachePartitionState(statePartition, state); - loadCompleted(state); - }; - if (_projectionConfig.CheckpointsEnabled) { - _checkpointManager.BeginLoadPartitionStateAt(statePartition, at, completed); - } else { - var state = new PartitionState("", null, _zeroCheckpointTag); - completed(state); - } + return; + } + + var s = lockLoaded + ? _partitionStateCache.TryGetAndLockPartitionState(statePartition, at) + : _partitionStateCache.TryGetPartitionState(statePartition); + if (s != null) + loadCompleted(s); + else { + void Completed(PartitionState state) { + if (lockLoaded) + _partitionStateCache.CacheAndLockPartitionState(statePartition, state, at); + else + _partitionStateCache.CachePartitionState(statePartition, state); + loadCompleted(state); + } + + if (_projectionConfig.CheckpointsEnabled) { + CheckpointManager.BeginLoadPartitionStateAt(statePartition, at, Completed); + } else { + var state = new PartitionState("", null, _zeroCheckpointTag); + Completed(state); } } } - public void FinalizeEventProcessing( - EventProcessedResult result, CheckpointTag eventCheckpointTag, float progress) { - if (_state == PhaseState.Running) { - //TODO: move to separate projection method and cache result in work item - if (result != null) { - _resultWriter.AccountPartition(result); - if (_projectionConfig.EmitEventEnabled && result.EmittedEvents != null) { - _resultWriter.EventsEmitted( - result.EmittedEvents, result.CausedBy, result.CorrelationId); - _emittedStreamsTracker.TrackEmittedStream(result.EmittedEvents.Select(x => x.Event).ToArray()); - } - - if (result.NewState != null) { - _resultWriter.WriteRunningResult(result); - _checkpointManager.StateUpdated(result.Partition, result.OldState, result.NewState); - } - - if (result.NewSharedState != null) { - _checkpointManager.StateUpdated("", result.OldSharedState, result.NewSharedState); - } + public void FinalizeEventProcessing(EventProcessedResult result, CheckpointTag eventCheckpointTag, float progress) { + if (_state != PhaseState.Running) return; + + //TODO: move to separate projection method and cache result in work item + if (result != null) { + _resultWriter.AccountPartition(result); + if (_projectionConfig.EmitEventEnabled && result.EmittedEvents != null) { + _resultWriter.EventsEmitted( + result.EmittedEvents, result.CausedBy, result.CorrelationId); + EmittedStreamsTracker.TrackEmittedStream(result.EmittedEvents.Select(x => x.Event).ToArray()); + } + + if (result.NewState != null) { + _resultWriter.WriteRunningResult(result); + CheckpointManager.StateUpdated(result.Partition, result.OldState, result.NewState); } - _checkpointManager.EventProcessed(eventCheckpointTag, progress); - _progressResultWriter.WriteProgress(progress); + if (result.NewSharedState != null) { + CheckpointManager.StateUpdated("", result.OldSharedState, result.NewSharedState); + } } - } - public void EmitEofResult( - string partition, string resultBody, CheckpointTag causedBy, Guid causedByGuid, string correlationId) { - _resultWriter.WriteEofResult( - _currentSubscriptionId, partition, resultBody, causedBy, causedByGuid, correlationId); + CheckpointManager.EventProcessed(eventCheckpointTag, progress); } public void RecordEventOrder(ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, Action completed) { switch (_state) { case PhaseState.Running: - _checkpointManager.RecordEventOrder( - resolvedEvent, orderCheckpointTag, completed); + CheckpointManager.RecordEventOrder(resolvedEvent, orderCheckpointTag, completed); break; case PhaseState.Stopped: _logger.Error("Should not receive events in stopped state anymore"); @@ -545,26 +510,22 @@ public void SetCurrentCheckpointSuggestedWorkItem(CheckpointSuggestedWorkItem ch } public virtual void GetStatistics(ProjectionStatistics info) { - info.Status = info.Status + GetStatus(); + info.Status += GetStatus(); info.BufferedEvents += GetBufferedEventCount(); } - public CheckpointTag MakeZeroCheckpointTag() { - return _zeroCheckpointTag; - } + public CheckpointTag MakeZeroCheckpointTag() => _zeroCheckpointTag; public void EnsureUnsubscribed() { if (_subscriptionState is PhaseSubscriptionState.Subscribed) { Unsubscribed(); // this way we distinguish pre-recorded events subscription if (_currentSubscriptionId != _projectionCorrelationId) - _publisher.Publish( - new ReaderSubscriptionManagement.Unsubscribe(_currentSubscriptionId)); + _publisher.Publish(new ReaderSubscriptionManagement.Unsubscribe(_currentSubscriptionId)); } } - protected long _subscriptionStartedAtLastCommitPosition; - private readonly IEnvelope _inutQueueEnvelope; + private readonly IEnvelope _inputQueueEnvelope; private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250); private bool _updateStatisticsTicketPending; @@ -572,7 +533,7 @@ public void Handle(EventReaderSubscriptionMessage.ReaderAssignedReader message) if (_state != PhaseState.Starting) return; if (_subscriptionState is not PhaseSubscriptionState.Subscribing - || message.SubscriptionId != _currentSubscriptionId) + || message.SubscriptionId != _currentSubscriptionId) return; _subscriptionState = PhaseSubscriptionState.Subscribed; _coreProjection.Subscribed(); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/IEventProcessingPhase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/IEventProcessingPhase.cs index 4ce756148ab..239983ebd5e 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/IEventProcessingPhase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/IEventProcessingPhase.cs @@ -19,26 +19,17 @@ public interface IProjectionPhaseCheckpointManager { } public interface IProjectionPhaseStateManager { - void BeginGetPartitionStateAt( - string statePartition, CheckpointTag at, Action loadCompleted, - bool lockLoaded); - - void UnlockAndForgetBefore(CheckpointTag checkpointTag); + void BeginGetPartitionStateAt(string statePartition, CheckpointTag at, Action loadCompleted, bool lockLoaded); CheckpointTag LastProcessedEventPosition { get; } } public interface IEventProcessingProjectionPhase : IProjectionPhaseStateManager { - EventProcessedResult ProcessCommittedEvent(EventReaderSubscriptionMessage.CommittedEventReceived message, - string partition); + EventProcessedResult ProcessCommittedEvent(EventReaderSubscriptionMessage.CommittedEventReceived message, string partition); - void FinalizeEventProcessing( - EventProcessedResult result, CheckpointTag eventCheckpointTag, float progress); + void FinalizeEventProcessing(EventProcessedResult result, CheckpointTag eventCheckpointTag, float progress); void RecordEventOrder(ResolvedEvent resolvedEvent, CheckpointTag orderCheckpointTag, Action completed); - void EmitEofResult( - string partition, string resultBody, CheckpointTag causedBy, Guid causedByGuid, string correlationId); - EventProcessedResult ProcessPartitionDeleted(string partition, CheckpointTag deletedPosition); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/IProgressResultWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/IProgressResultWriter.cs deleted file mode 100644 index aee5f85328a..00000000000 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/IProgressResultWriter.cs +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright (c) Kurrent, Inc and/or licensed to Kurrent, Inc under one or more agreements. -// Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). - -namespace KurrentDB.Projections.Core.Services.Processing.Phases; - -public interface IProgressResultWriter { - void WriteProgress(float progress); -} diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/PhasePositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/PhasePositionTagger.cs index 32418199189..caf14e4b909 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/PhasePositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/PhasePositionTagger.cs @@ -7,10 +7,7 @@ namespace KurrentDB.Projections.Core.Services.Processing.Phases; -public class PhasePositionTagger : PositionTagger { - public PhasePositionTagger(int phase) : base(phase) { - } - +public class PhasePositionTagger(int phase) : PositionTagger(phase) { public override bool IsMessageAfterCheckpointTag( CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { throw new NotSupportedException(); @@ -39,13 +36,8 @@ public override CheckpointTag AdjustTag(CheckpointTag tag) { return tag; if (tag.Phase > Phase) throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected less or equal to: {0} Was: {1}", Phase, - tag.Phase), "tag"); - - if (tag.Mode_ == CheckpointTag.Mode.Phase) { - return tag; - } + $"Invalid checkpoint tag phase. Expected less or equal to: {Phase} Was: {tag.Phase}", nameof(tag)); - throw new NotSupportedException("Conversion to phase based checkpoint tag is not supported"); + return tag.Mode_ == CheckpointTag.Mode.Phase ? tag : throw new NotSupportedException("Conversion to phase based checkpoint tag is not supported"); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryEofProjectionProcessingPhase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryEofProjectionProcessingPhase.cs index 0b05d2f0d55..c135a4c261e 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryEofProjectionProcessingPhase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryEofProjectionProcessingPhase.cs @@ -10,20 +10,18 @@ namespace KurrentDB.Projections.Core.Services.Processing.Phases; -public sealed class WriteQueryEofProjectionProcessingPhase : WriteQueryResultProjectionProcessingPhaseBase { - public WriteQueryEofProjectionProcessingPhase( - IPublisher publisher, - int phase, - string resultStream, - ICoreProjectionForProcessingPhase coreProjection, - PartitionStateCache stateCache, - ICoreProjectionCheckpointManager checkpointManager, - IEmittedEventWriter emittedEventWriter, - IEmittedStreamsTracker emittedStreamsTracker) - : base(publisher, phase, resultStream, coreProjection, stateCache, checkpointManager, emittedEventWriter, - emittedStreamsTracker) { - } - +public sealed class WriteQueryEofProjectionProcessingPhase( + IPublisher publisher, + int phase, + string resultStream, + ICoreProjectionForProcessingPhase coreProjection, + PartitionStateCache stateCache, + ICoreProjectionCheckpointManager checkpointManager, + IEmittedEventWriter emittedEventWriter, + IEmittedStreamsTracker emittedStreamsTracker) + : WriteQueryResultProjectionProcessingPhaseBase(publisher, phase, resultStream, coreProjection, stateCache, checkpointManager, + emittedEventWriter, + emittedStreamsTracker) { protected override IEnumerable WriteResults(CheckpointTag phaseCheckpointTag) { // do nothing yield break; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhase.cs index 329ccc8e0a8..3eb2d9f58d6 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhase.cs @@ -12,23 +12,20 @@ namespace KurrentDB.Projections.Core.Services.Processing.Phases; -public sealed class WriteQueryResultProjectionProcessingPhase : WriteQueryResultProjectionProcessingPhaseBase { - public WriteQueryResultProjectionProcessingPhase( - IPublisher publisher, - int phase, - string resultStream, - ICoreProjectionForProcessingPhase coreProjection, - PartitionStateCache stateCache, - ICoreProjectionCheckpointManager checkpointManager, - IEmittedEventWriter emittedEventWriter, - IEmittedStreamsTracker emittedStreamsTracker) - : base(publisher, phase, resultStream, coreProjection, stateCache, checkpointManager, emittedEventWriter, - emittedStreamsTracker) { - } - +public sealed class WriteQueryResultProjectionProcessingPhase( + IPublisher publisher, + int phase, + string resultStream, + ICoreProjectionForProcessingPhase coreProjection, + PartitionStateCache stateCache, + ICoreProjectionCheckpointManager checkpointManager, + IEmittedEventWriter emittedEventWriter, + IEmittedStreamsTracker emittedStreamsTracker) + : WriteQueryResultProjectionProcessingPhaseBase(publisher, phase, resultStream, coreProjection, stateCache, checkpointManager, + emittedEventWriter, + emittedStreamsTracker) { protected override IEnumerable WriteResults(CheckpointTag phaseCheckpointTag) { var items = _stateCache.Enumerate(); - EmittedStream.WriterConfiguration.StreamMetadata streamMetadata = null; return from item in items let partitionState = item.Item2 select @@ -41,7 +38,6 @@ protected override IEnumerable WriteResults(CheckpointTag partitionState.Result, null, phaseCheckpointTag, - null), - streamMetadata); + null)); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhaseBase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhaseBase.cs index 63aab522348..82b115e9644 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhaseBase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Phases/WriteQueryResultProjectionProcessingPhaseBase.cs @@ -19,13 +19,11 @@ public abstract class WriteQueryResultProjectionProcessingPhaseBase : IProjectio protected readonly string _resultStream; private readonly ICoreProjectionForProcessingPhase _coreProjection; protected readonly PartitionStateCache _stateCache; - protected readonly ICoreProjectionCheckpointManager _checkpointManager; - protected readonly IEmittedEventWriter _emittedEventWriter; - protected readonly IEmittedStreamsTracker _emittedStreamsTracker; + private readonly IEmittedEventWriter _emittedEventWriter; private bool _subscribed; private PhaseState _projectionState; - public WriteQueryResultProjectionProcessingPhaseBase( + protected WriteQueryResultProjectionProcessingPhaseBase( IPublisher publisher, int phase, string resultStream, @@ -34,45 +32,33 @@ public WriteQueryResultProjectionProcessingPhaseBase( ICoreProjectionCheckpointManager checkpointManager, IEmittedEventWriter emittedEventWriter, IEmittedStreamsTracker emittedStreamsTracker) { - if (resultStream == null) - throw new ArgumentNullException("resultStream"); - if (coreProjection == null) - throw new ArgumentNullException("coreProjection"); - if (stateCache == null) - throw new ArgumentNullException("stateCache"); - if (checkpointManager == null) - throw new ArgumentNullException("checkpointManager"); - if (emittedEventWriter == null) - throw new ArgumentNullException("emittedEventWriter"); - if (emittedStreamsTracker == null) - throw new ArgumentNullException("emittedStreamsTracker"); - if (string.IsNullOrEmpty(resultStream)) - throw new ArgumentException("resultStream"); + ArgumentException.ThrowIfNullOrEmpty(resultStream); + ArgumentNullException.ThrowIfNull(coreProjection); + ArgumentNullException.ThrowIfNull(stateCache); + ArgumentNullException.ThrowIfNull(checkpointManager); + ArgumentNullException.ThrowIfNull(emittedEventWriter); + ArgumentNullException.ThrowIfNull(emittedStreamsTracker); _publisher = publisher; _phase = phase; _resultStream = resultStream; _coreProjection = coreProjection; _stateCache = stateCache; - _checkpointManager = checkpointManager; + CheckpointManager = checkpointManager; _emittedEventWriter = emittedEventWriter; - _emittedStreamsTracker = emittedStreamsTracker; + EmittedStreamsTracker = emittedStreamsTracker; } - public ICoreProjectionCheckpointManager CheckpointManager { - get { return _checkpointManager; } - } + public ICoreProjectionCheckpointManager CheckpointManager { get; } - public IEmittedStreamsTracker EmittedStreamsTracker { - get { return _emittedStreamsTracker; } - } + public IEmittedStreamsTracker EmittedStreamsTracker { get; } public void Dispose() { } public void Handle(CoreProjectionManagementMessage.GetState message) { var state = _stateCache.TryGetPartitionState(message.Partition); - var stateString = state != null ? state.State : null; + var stateString = state?.State; _publisher.Publish( new CoreProjectionStatusMessage.StateReport( message.CorrelationId, @@ -84,7 +70,7 @@ public void Handle(CoreProjectionManagementMessage.GetState message) { public void Handle(CoreProjectionManagementMessage.GetResult message) { var state = _stateCache.TryGetPartitionState(message.Partition); - var resultString = state != null ? state.Result : null; + var resultString = state?.Result; _publisher.Publish( new CoreProjectionStatusMessage.ResultReport( message.CorrelationId, @@ -119,12 +105,11 @@ public void ProcessEvent() { _emittedEventWriter.EventsEmitted(writeResults.Concat(writeEofResults).ToArray(), Guid.Empty, null); - _checkpointManager.EventProcessed(phaseCheckpointTag, 100.0f); + CheckpointManager.EventProcessed(phaseCheckpointTag, 100.0f); _coreProjection.CompletePhase(); } private IEnumerable WriteEofEvent(CheckpointTag phaseCheckpointTag) { - EmittedStream.WriterConfiguration.StreamMetadata streamMetadata = null; yield return new EmittedEventEnvelope( new EmittedDataEvent( @@ -135,8 +120,7 @@ private IEnumerable WriteEofEvent(CheckpointTag phaseCheck null, null, phaseCheckpointTag, - null), - streamMetadata); + null)); } protected abstract IEnumerable WriteResults(CheckpointTag phaseCheckpointTag); @@ -151,7 +135,7 @@ public void SetProjectionState(PhaseState state) { } public void GetStatistics(ProjectionStatistics info) { - info.Status = info.Status + "/Writing results"; + info.Status += "/Writing results"; } public CheckpointTag MakeZeroCheckpointTag() { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionCoreService.cs b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionCoreService.cs index b37bd54bad8..20e43b889a3 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionCoreService.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionCoreService.cs @@ -42,20 +42,15 @@ public class ProjectionCoreService private readonly IPublisher _publisher; private readonly IPublisher _inputQueue; private readonly ILogger _logger = Log.ForContext(); - - private readonly Dictionary _projections = new Dictionary(); - + private readonly Dictionary _projections = new(); private readonly IODispatcher _ioDispatcher; - - private readonly ReaderSubscriptionDispatcher _subscriptionDispatcher; - private readonly ITimeProvider _timeProvider; private readonly ProcessingStrategySelector _processingStrategySelector; private bool _stopping; - private readonly Dictionary _suspendingProjections = new Dictionary(); + private readonly Dictionary _suspendingProjections = new(); private Guid _stopQueueId = Guid.Empty; - private int _projectionStopTimeoutMs = 5000; + private const int ProjectionStopTimeoutMs = 5000; private readonly ProjectionStateHandlerFactory _factory; public ProjectionCoreService( @@ -70,10 +65,9 @@ public ProjectionCoreService( _inputQueue = inputQueue; _publisher = publisher; _ioDispatcher = ioDispatcher; - _subscriptionDispatcher = subscriptionDispatcher; _timeProvider = timeProvider; - _processingStrategySelector = new ProcessingStrategySelector(_subscriptionDispatcher, configuration.MaxProjectionStateSize); - _factory = new ProjectionStateHandlerFactory( + _processingStrategySelector = new(subscriptionDispatcher, configuration.MaxProjectionStateSize); + _factory = new( javascriptCompilationTimeout: TimeSpan.FromMilliseconds(configuration.ProjectionCompilationTimeout), javascriptExecutionTimeout: TimeSpan.FromMilliseconds(configuration.ProjectionExecutionTimeout), trackers: configuration.ProjectionTrackers); @@ -107,7 +101,7 @@ private void StopProjections() { FinishStopping(); } else { _publisher.Publish(TimerMessage.Schedule.Create( - TimeSpan.FromMilliseconds(_projectionStopTimeoutMs), + TimeSpan.FromMilliseconds(ProjectionStopTimeoutMs), _inputQueue, new ProjectionCoreServiceMessage.StopCoreTimeout(_stopQueueId))); } @@ -156,60 +150,42 @@ public void Handle(CoreProjectionManagementMessage.CreateAndPrepare message) { message.EnableContentTypeValidation, message.Config.ProjectionExecutionTimeout); - string name = message.Name; var sourceDefinition = ProjectionSourceDefinition.From(stateHandler.GetSourceDefinition()); var projectionVersion = message.Version; var projectionConfig = message.Config; - var namesBuilder = new ProjectionNamesBuilder(name, sourceDefinition); var projectionProcessingStrategy = _processingStrategySelector.CreateProjectionProcessingStrategy( - name, + message.Name, projectionVersion, - namesBuilder, sourceDefinition, projectionConfig, stateHandler, - message.HandlerType, - message.Query, message.EnableContentTypeValidation); CreateCoreProjection(message.ProjectionId, projectionConfig.RunAs, projectionProcessingStrategy); - _publisher.Publish( - new CoreProjectionStatusMessage.Prepared( - message.ProjectionId, sourceDefinition)); + _publisher.Publish(new CoreProjectionStatusMessage.Prepared(message.ProjectionId, sourceDefinition)); } catch (Exception ex) { - _publisher.Publish( - new CoreProjectionStatusMessage.Faulted(message.ProjectionId, ex.Message)); + _publisher.Publish(new CoreProjectionStatusMessage.Faulted(message.ProjectionId, ex.Message)); } } public void Handle(CoreProjectionManagementMessage.CreatePrepared message) { try { - var name = message.Name; var sourceDefinition = ProjectionSourceDefinition.From(message.SourceDefinition); - var projectionVersion = message.Version; - var projectionConfig = message.Config; - var namesBuilder = new ProjectionNamesBuilder(name, sourceDefinition); var projectionProcessingStrategy = _processingStrategySelector.CreateProjectionProcessingStrategy( - name, - projectionVersion, - namesBuilder, + message.Name, + message.Version, sourceDefinition, - projectionConfig, + message.Config, null, - message.HandlerType, - message.Query, message.EnableContentTypeValidation); - CreateCoreProjection(message.ProjectionId, projectionConfig.RunAs, projectionProcessingStrategy); - _publisher.Publish( - new CoreProjectionStatusMessage.Prepared( - message.ProjectionId, sourceDefinition)); + CreateCoreProjection(message.ProjectionId, message.Config.RunAs, projectionProcessingStrategy); + _publisher.Publish(new CoreProjectionStatusMessage.Prepared( message.ProjectionId, sourceDefinition)); } catch (Exception ex) { - _publisher.Publish( - new CoreProjectionStatusMessage.Faulted(message.ProjectionId, ex.Message)); + _publisher.Publish(new CoreProjectionStatusMessage.Faulted(message.ProjectionId, ex.Message)); } } @@ -222,15 +198,12 @@ private void CreateCoreProjection( runAs, _publisher, _ioDispatcher, - _subscriptionDispatcher, _timeProvider); _projections.Add(projectionCorrelationId, projection); } public void Handle(CoreProjectionManagementMessage.Dispose message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) { - _projections.Remove(message.ProjectionId); + if (_projections.Remove(message.ProjectionId, out var projection)) { projection.Dispose(); } } @@ -256,44 +229,37 @@ public void Handle(CoreProjectionManagementMessage.Kill message) { } public void Handle(CoreProjectionManagementMessage.GetState message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } public void Handle(CoreProjectionManagementMessage.GetResult message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } public void Handle(CoreProjectionProcessingMessage.CheckpointCompleted message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } public void Handle(CoreProjectionProcessingMessage.CheckpointLoaded message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } public void Handle(CoreProjectionProcessingMessage.PrerecordedEventsLoaded message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } public void Handle(CoreProjectionProcessingMessage.RestartRequested message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } public void Handle(CoreProjectionProcessingMessage.Failed message) { - CoreProjection projection; - if (_projections.TryGetValue(message.ProjectionId, out projection)) + if (_projections.TryGetValue(message.ProjectionId, out var projection)) projection.Handle(message); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionNamesBuilder.cs b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionNamesBuilder.cs index 8c177c75d9d..34cfd067056 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionNamesBuilder.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionNamesBuilder.cs @@ -15,57 +15,41 @@ public static class StandardProjections { public const string EventByCorrIdStandardProjection = "$by_correlation_id"; } - public static ProjectionNamesBuilder CreateForTest(string name) { - return new ProjectionNamesBuilder(name); - } + public static ProjectionNamesBuilder CreateForTest(string name) => new(name); - private readonly string _name; - private readonly IQuerySources _sources; private readonly string _resultStreamName; private readonly string _partitionCatalogStreamName; private readonly string _checkpointStreamName; private readonly string _orderStreamName; private readonly string _emittedStreamsName; private readonly string _emittedStreamsCheckpointName; + private readonly string _partitionResultStreamNamePattern; private ProjectionNamesBuilder(string name) : this(name, new QuerySourcesDefinition()) { } public ProjectionNamesBuilder(string name, IQuerySources sources) { - if (sources == null) - throw new ArgumentNullException("sources"); - _name = name; - _sources = sources; - _resultStreamName = _sources.ResultStreamNameOption - ?? ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionsStateStreamSuffix; - _partitionCatalogStreamName = ProjectionsStreamPrefix + EffectiveProjectionName - + ProjectionPartitionCatalogStreamSuffix; - _checkpointStreamName = - ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionCheckpointStreamSuffix; + ArgumentNullException.ThrowIfNull(sources); + EffectiveProjectionName = name; + _resultStreamName = sources.ResultStreamNameOption ?? + ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionsStateStreamSuffix; + _partitionCatalogStreamName = ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionPartitionCatalogStreamSuffix; + _checkpointStreamName = ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionCheckpointStreamSuffix; _orderStreamName = ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionOrderStreamSuffix; _emittedStreamsName = ProjectionsStreamPrefix + EffectiveProjectionName + ProjectionEmittedStreamSuffix; _emittedStreamsCheckpointName = ProjectionsStreamPrefix + EffectiveProjectionName + - ProjectionEmittedStreamSuffix + ProjectionCheckpointStreamSuffix; - } - - public string EffectiveProjectionName { - get { return _name; } + ProjectionEmittedStreamSuffix + ProjectionCheckpointStreamSuffix; + _partitionResultStreamNamePattern = sources.PartitionResultStreamNamePatternOption + ?? $"{ProjectionsStreamPrefix}{EffectiveProjectionName}-{{0}}{ProjectionsStateStreamSuffix}"; } + public string EffectiveProjectionName { get; } - private string GetPartitionResultStreamName(string partitionName) { - return String.Format(GetPartitionResultStreamNamePattern(), partitionName); - } - - public string GetResultStreamName() { - return _resultStreamName; - } + private string GetPartitionResultStreamName(string partitionName) + => string.Format(_partitionResultStreamNamePattern, partitionName); - public string GetPartitionResultStreamNamePattern() { - return _sources.PartitionResultStreamNamePatternOption - ?? ProjectionsStreamPrefix + EffectiveProjectionName + "-{0}" + ProjectionsStateStreamSuffix; - } + public string GetResultStreamName() => _resultStreamName; public const string ProjectionsStreamPrefix = "$projections-"; private const string ProjectionsStateStreamSuffix = "-result"; @@ -75,38 +59,24 @@ public string GetPartitionResultStreamNamePattern() { private const string ProjectionPartitionCatalogStreamSuffix = "-partitions"; public const string ProjectionsRegistrationStream = "$projections-$all"; - public string GetPartitionCatalogStreamName() { - return _partitionCatalogStreamName; - } + public string GetPartitionCatalogStreamName() => _partitionCatalogStreamName; - public string MakePartitionResultStreamName(string statePartition) { - return String.IsNullOrEmpty(statePartition) + public string MakePartitionResultStreamName(string statePartition) + => string.IsNullOrEmpty(statePartition) ? GetResultStreamName() : GetPartitionResultStreamName(statePartition); - } - public string MakePartitionCheckpointStreamName(string statePartition) { - if (String.IsNullOrEmpty(statePartition)) - throw new InvalidOperationException("Root partition cannot have a partition checkpoint stream"); + public string MakePartitionCheckpointStreamName(string statePartition) + => string.IsNullOrEmpty(statePartition) + ? throw new InvalidOperationException("Root partition cannot have a partition checkpoint stream") + : $"{ProjectionsStreamPrefix}{EffectiveProjectionName}-{statePartition}{ProjectionCheckpointStreamSuffix}"; - return ProjectionsStreamPrefix + EffectiveProjectionName + "-" + statePartition - + ProjectionCheckpointStreamSuffix; - } + public string MakeCheckpointStreamName() => _checkpointStreamName; - public string MakeCheckpointStreamName() { - return _checkpointStreamName; - } + public string GetEmittedStreamsName() => _emittedStreamsName; - public string GetEmittedStreamsName() { - return _emittedStreamsName; - } + public string GetEmittedStreamsCheckpointName() => _emittedStreamsCheckpointName; - public string GetEmittedStreamsCheckpointName() { - return _emittedStreamsCheckpointName; - } - - public string GetOrderStreamName() { - return _orderStreamName; - } + public string GetOrderStreamName() => _orderStreamName; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionSourceDefinition.cs b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionSourceDefinition.cs index 0fbe9267674..c0912efe875 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionSourceDefinition.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionSourceDefinition.cs @@ -25,49 +25,27 @@ public class ProjectionSourceDefinition : IQuerySources { [DataMember] public QuerySourceOptions Options { get; set; } - bool IQuerySources.DefinesStateTransform { - get { return Options != null && Options.DefinesStateTransform; } - } + bool IQuerySources.DefinesStateTransform => Options is { DefinesStateTransform: true }; - bool IQuerySources.ProducesResults { - get { return Options != null && Options.ProducesResults; } - } + bool IQuerySources.ProducesResults => Options is { ProducesResults: true }; - bool IQuerySources.DefinesFold { - get { return Options != null && Options.DefinesFold; } - } + bool IQuerySources.DefinesFold => Options is { DefinesFold: true }; - bool IQuerySources.HandlesDeletedNotifications { - get { return Options != null && Options.HandlesDeletedNotifications; } - } + bool IQuerySources.HandlesDeletedNotifications => Options is { HandlesDeletedNotifications: true }; - bool IQuerySources.IncludeLinksOption { - get { return Options != null && Options.IncludeLinks; } - } + bool IQuerySources.IncludeLinksOption => Options is { IncludeLinks: true }; - string IQuerySources.ResultStreamNameOption { - get { return Options != null ? Options.ResultStreamName : null; } - } + string IQuerySources.ResultStreamNameOption => Options?.ResultStreamName; - string IQuerySources.PartitionResultStreamNamePatternOption { - get { return Options != null ? Options.PartitionResultStreamNamePattern : null; } - } + string IQuerySources.PartitionResultStreamNamePatternOption => Options?.PartitionResultStreamNamePattern; - bool IQuerySources.ReorderEventsOption { - get { return Options != null && Options.ReorderEvents; } - } + bool IQuerySources.ReorderEventsOption => Options is { ReorderEvents: true }; - int? IQuerySources.ProcessingLagOption { - get { return Options != null ? Options.ProcessingLag : (int?)null; } - } + int? IQuerySources.ProcessingLagOption => Options?.ProcessingLag; - bool IQuerySources.IsBiState { - get { return Options != null ? Options.IsBiState : false; } - } + bool IQuerySources.IsBiState => Options is { IsBiState : true }; - bool IQuerySources.ByStreams { - get { return ByStream; } - } + bool IQuerySources.ByStreams => ByStream; public static ProjectionSourceDefinition From(IQuerySources sources) { return new ProjectionSourceDefinition { @@ -75,9 +53,9 @@ public static ProjectionSourceDefinition From(IQuerySources sources) { AllStreams = sources.AllStreams, ByStream = sources.ByStreams, ByCustomPartitions = sources.ByCustomPartitions, - Categories = (sources.Categories ?? new string[0]).ToArray(), - Events = (sources.Events ?? new string[0]).ToArray(), - Streams = (sources.Streams ?? new string[0]).ToArray(), + Categories = (sources.Categories ?? []).ToArray(), + Events = (sources.Events ?? []).ToArray(), + Streams = (sources.Streams ?? []).ToArray(), Options = new QuerySourceOptions { DefinesStateTransform = sources.DefinesStateTransform, @@ -94,7 +72,7 @@ public static ProjectionSourceDefinition From(IQuerySources sources) { }; } - private bool Equals(string[] a, string[] b) { + private static bool Equals(string[] a, string[] b) { bool aEmpty = (a == null || a.Length == 0); bool bEmpty = (b == null || b.Length == 0); if (aEmpty && bEmpty) @@ -106,12 +84,12 @@ private bool Equals(string[] a, string[] b) { protected bool Equals(ProjectionSourceDefinition other) { return AllEvents.Equals(other.AllEvents) && AllStreams.Equals(other.AllStreams) - && ByStream.Equals(other.ByStream) && - ByCustomPartitions.Equals(other.ByCustomPartitions) - && Equals(Categories, other.Categories) && - Equals(Events, other.Events) - && Equals(Streams, other.Streams) - && Equals(Options, other.Options); + && ByStream.Equals(other.ByStream) && + ByCustomPartitions.Equals(other.ByCustomPartitions) + && Equals(Categories, other.Categories) && + Equals(Events, other.Events) + && Equals(Streams, other.Streams) + && Equals(Options, other.Options); } public override bool Equals(object obj) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionVersion.cs b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionVersion.cs index 7f9afc4571f..2f2e6fa734a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/ProjectionVersion.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/ProjectionVersion.cs @@ -3,33 +3,4 @@ namespace KurrentDB.Projections.Core.Services.Processing; -public struct ProjectionVersion { - public readonly long ProjectionId; - public readonly long Epoch; - public readonly long Version; - - public ProjectionVersion(long projectionId, long epoch, long version) { - ProjectionId = projectionId; - Epoch = epoch; - Version = version; - } - - public bool Equals(ProjectionVersion other) { - return ProjectionId == other.ProjectionId && Epoch == other.Epoch && Version == other.Version; - } - - public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) - return false; - return obj is ProjectionVersion && Equals((ProjectionVersion)obj); - } - - public override int GetHashCode() { - unchecked { - var hashCode = ProjectionId.GetHashCode(); - hashCode = (hashCode * 397) ^ Epoch.GetHashCode(); - hashCode = (hashCode * 397) ^ Version.GetHashCode(); - return hashCode; - } - } -} +public readonly record struct ProjectionVersion(long ProjectionId, long Epoch, long Version); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/QuerySourceOptions.cs b/src/KurrentDB.Projections.Core/Services/Processing/QuerySourceOptions.cs index 3dd33b42fb7..1cd5185aa99 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/QuerySourceOptions.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/QuerySourceOptions.cs @@ -8,23 +8,14 @@ namespace KurrentDB.Projections.Core.Services.Processing; [DataContract] public class QuerySourceOptions { [DataMember] public string ResultStreamName { get; set; } - [DataMember] public string PartitionResultStreamNamePattern { get; set; } - [DataMember] public bool ReorderEvents { get; set; } - [DataMember] public int ProcessingLag { get; set; } - [DataMember] public bool IsBiState { get; set; } - [DataMember] public bool DefinesStateTransform { get; set; } - [DataMember] public bool ProducesResults { get; set; } - [DataMember] public bool DefinesFold { get; set; } - [DataMember] public bool HandlesDeletedNotifications { get; set; } - [DataMember] public bool IncludeLinks { get; set; } protected bool Equals(QuerySourceOptions other) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/RequestResponseQueueForwarder.cs b/src/KurrentDB.Projections.Core/Services/Processing/RequestResponseQueueForwarder.cs index 9e174dadfe9..0662627c51a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/RequestResponseQueueForwarder.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/RequestResponseQueueForwarder.cs @@ -9,41 +9,36 @@ namespace KurrentDB.Projections.Core.Services.Processing; -public class RequestResponseQueueForwarder : IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle, - IHandle { - private readonly IPublisher _externalRequestQueue; - private readonly IPublisher _inputQueue; - - public RequestResponseQueueForwarder(IPublisher inputQueue, IPublisher externalRequestQueue) { - _inputQueue = inputQueue; - _externalRequestQueue = externalRequestQueue; - } - +public class RequestResponseQueueForwarder(IPublisher inputQueue, IPublisher externalRequestQueue) + : IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle, + IHandle { public void Handle(ClientMessage.ReadEvent msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ClientMessage.ReadEvent( - msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(_inputQueue, msg.Envelope, nameof(ClientMessage.ReadEvent)), + msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(inputQueue, msg.Envelope, nameof(ClientMessage.ReadEvent)), msg.EventStreamId, msg.EventNumber, msg.ResolveLinkTos, msg.RequireLeader, msg.User)); } public void Handle(ClientMessage.WriteEvents msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ClientMessage.WriteEvents( - msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(_inputQueue, msg.Envelope, nameof(ClientMessage.WriteEvents)), true, + msg.InternalCorrId, msg.CorrelationId, + new PublishToWrapEnvelop(inputQueue, msg.Envelope, nameof(ClientMessage.WriteEvents)), true, msg.EventStreamIds, msg.ExpectedVersions, msg.Events, msg.EventStreamIndexes, msg.User)); } public void Handle(ClientMessage.DeleteStream msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ClientMessage.DeleteStream( - msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(_inputQueue, msg.Envelope, nameof(ClientMessage.DeleteStream)), true, + msg.InternalCorrId, msg.CorrelationId, + new PublishToWrapEnvelop(inputQueue, msg.Envelope, nameof(ClientMessage.DeleteStream)), true, msg.EventStreamId, msg.ExpectedVersion, msg.HardDelete, msg.User)); } @@ -52,9 +47,10 @@ public void Handle(ClientMessage.DeleteStream msg) { // However, in this fix we make the minimum impact change necessary which is to only pass the expiration that we // need (DateTime.MaxValue) and only for the messages that we need. public void Handle(ClientMessage.ReadStreamEventsBackward msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ClientMessage.ReadStreamEventsBackward( - msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(_inputQueue, msg.Envelope, nameof(ClientMessage.ReadStreamEventsBackward)), + msg.InternalCorrId, msg.CorrelationId, + new PublishToWrapEnvelop(inputQueue, msg.Envelope, nameof(ClientMessage.ReadStreamEventsBackward)), msg.EventStreamId, msg.FromEventNumber, msg.MaxCount, msg.ResolveLinkTos, msg.RequireLeader, msg.ValidationStreamVersion, msg.User, replyOnExpired: false, @@ -62,37 +58,39 @@ public void Handle(ClientMessage.ReadStreamEventsBackward msg) { } public void Handle(ClientMessage.ReadStreamEventsForward msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ClientMessage.ReadStreamEventsForward( - msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(_inputQueue, msg.Envelope, nameof(ClientMessage.ReadStreamEventsForward)), + msg.InternalCorrId, msg.CorrelationId, + new PublishToWrapEnvelop(inputQueue, msg.Envelope, nameof(ClientMessage.ReadStreamEventsForward)), msg.EventStreamId, msg.FromEventNumber, msg.MaxCount, msg.ResolveLinkTos, msg.RequireLeader, msg.ValidationStreamVersion, msg.User, replyOnExpired: false, expires: msg.Expires == DateTime.MaxValue ? msg.Expires : null)); } public void Handle(ClientMessage.ReadAllEventsForward msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ClientMessage.ReadAllEventsForward( - msg.InternalCorrId, msg.CorrelationId, new PublishToWrapEnvelop(_inputQueue, msg.Envelope, nameof(ClientMessage.ReadAllEventsForward)), + msg.InternalCorrId, msg.CorrelationId, + new PublishToWrapEnvelop(inputQueue, msg.Envelope, nameof(ClientMessage.ReadAllEventsForward)), msg.CommitPosition, msg.PreparePosition, msg.MaxCount, msg.ResolveLinkTos, msg.RequireLeader, msg.ValidationTfLastCommitPosition, msg.User, replyOnExpired: false)); } public void Handle(SystemMessage.SubSystemInitialized msg) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new SystemMessage.SubSystemInitialized(msg.SubSystemName)); } void IHandle.Handle( ProjectionCoreServiceMessage.SubComponentStarted message) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ProjectionCoreServiceMessage.SubComponentStarted(message.SubComponent, message.InstanceCorrelationId) ); } void IHandle.Handle( ProjectionCoreServiceMessage.SubComponentStopped message) { - _externalRequestQueue.Publish( + externalRequestQueue.Publish( new ProjectionCoreServiceMessage.SubComponentStopped(message.SubComponent, message.QueueId) ); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/ResolvedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/ResolvedEvent.cs index 5498857ea88..28813ecd2c8 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/ResolvedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/ResolvedEvent.cs @@ -14,24 +14,11 @@ namespace KurrentDB.Projections.Core.Services.Processing; public class ResolvedEvent { - private readonly string _eventStreamId; - private readonly long _eventSequenceNumber; - private readonly bool _resolvedLinkTo; - - private readonly string _positionStreamId; - private readonly long _positionSequenceNumber; - private readonly TFPos _position; - private readonly TFPos _eventOrLinkTargetPosition; - private readonly TFPos _linkOrEventPosition; - - public readonly Guid EventId; public readonly string EventType; public readonly bool IsJson; public readonly DateTime Timestamp; - public readonly ReadOnlyMemory DataMemory; - public readonly string Data; public readonly string Metadata; public readonly string PositionMetadata; @@ -41,14 +28,14 @@ public class ResolvedEvent { public ResolvedEvent(KurrentDB.Core.Data.ResolvedEvent resolvedEvent, byte[] streamMetadata) { var positionEvent = resolvedEvent.Link ?? resolvedEvent.Event; - _linkOrEventPosition = resolvedEvent.OriginalPosition.GetValueOrDefault(); + LinkOrEventPosition = resolvedEvent.OriginalPosition.GetValueOrDefault(); var @event = resolvedEvent.Event; - _positionStreamId = positionEvent.EventStreamId; - _positionSequenceNumber = positionEvent.EventNumber; - _eventStreamId = @event != null ? @event.EventStreamId : null; - _eventSequenceNumber = @event != null ? @event.EventNumber : -1; - _resolvedLinkTo = positionEvent != @event; - _position = resolvedEvent.OriginalPosition ?? new TFPos(-1, positionEvent.LogPosition); + PositionStreamId = positionEvent.EventStreamId; + PositionSequenceNumber = positionEvent.EventNumber; + EventStreamId = @event != null ? @event.EventStreamId : null; + EventSequenceNumber = @event != null ? @event.EventNumber : -1; + ResolvedLinkTo = positionEvent != @event; + Position = resolvedEvent.OriginalPosition ?? new TFPos(-1, positionEvent.LogPosition); EventId = @event != null ? @event.EventId : Guid.Empty; EventType = @event != null ? @event.EventType : null; IsJson = @event != null && (@event.Flags & PrepareFlags.IsJson) != 0; @@ -59,15 +46,15 @@ public ResolvedEvent(KurrentDB.Core.Data.ResolvedEvent resolvedEvent, byte[] str //TODO: handle utf-8 conversion exception Data = @event != null && @event.Data.Length > 0 ? Helper.UTF8NoBom.GetString(@event.Data.Span) : null; Metadata = @event != null && @event.Metadata.Length > 0 ? Helper.UTF8NoBom.GetString(@event.Metadata.Span) : null; - PositionMetadata = _resolvedLinkTo + PositionMetadata = ResolvedLinkTo ? (positionEvent.Metadata.Length > 0 ? Helper.UTF8NoBom.GetString(positionEvent.Metadata.Span) : null) : null; StreamMetadata = streamMetadata != null ? Helper.UTF8NoBom.GetString(streamMetadata) : null; TFPos eventOrLinkTargetPosition; - if (_resolvedLinkTo) { + if (ResolvedLinkTo) { Dictionary extraMetadata = null; - if (positionEvent.Metadata.Length > 0 && positionEvent.Metadata.Length > 0) { + if (positionEvent.Metadata.Length > 0) { //TODO: parse JSON only when unresolved link and just tag otherwise CheckpointTag tag; if (resolvedEvent.Link != null && resolvedEvent.Event == null) { @@ -102,22 +89,18 @@ public ResolvedEvent(KurrentDB.Core.Data.ResolvedEvent resolvedEvent, byte[] str : new TFPos(-1, positionEvent.LogPosition); } - JToken deletedValue; - IsLinkToDeletedStreamTombstone = extraMetadata != null - && extraMetadata.TryGetValue("$deleted", out deletedValue); + IsLinkToDeletedStreamTombstone = extraMetadata != null && extraMetadata.TryGetValue("$deleted", out _); if (resolvedEvent.ResolveResult == ReadEventResult.NoStream || resolvedEvent.ResolveResult == ReadEventResult.StreamDeleted || IsLinkToDeletedStreamTombstone) { IsLinkToDeletedStream = true; - var streamId = SystemEventTypes.StreamReferenceEventToStreamId( - SystemEventTypes.LinkTo, resolvedEvent.Link.Data); - _eventStreamId = streamId; + EventStreamId = SystemEventTypes.StreamReferenceEventToStreamId(SystemEventTypes.LinkTo, resolvedEvent.Link.Data); } } else { // not a link eventOrLinkTargetPosition = resolvedEvent.OriginalPosition ?? new TFPos(-1, positionEvent.LogPosition); } - _eventOrLinkTargetPosition = eventOrLinkTargetPosition; + EventOrLinkTargetPosition = eventOrLinkTargetPosition; } // Called from tests only @@ -126,13 +109,13 @@ public ResolvedEvent( bool resolvedLinkTo, TFPos position, TFPos eventOrLinkTargetPosition, Guid eventId, string eventType, bool isJson, byte[] data, byte[] metadata, byte[] positionMetadata, byte[] streamMetadata, DateTime timestamp) { - _positionStreamId = positionStreamId; - _positionSequenceNumber = positionSequenceNumber; - _eventStreamId = eventStreamId; - _eventSequenceNumber = eventSequenceNumber; - _resolvedLinkTo = resolvedLinkTo; - _position = position; - _eventOrLinkTargetPosition = eventOrLinkTargetPosition; + PositionStreamId = positionStreamId; + PositionSequenceNumber = positionSequenceNumber; + EventStreamId = eventStreamId; + EventSequenceNumber = eventSequenceNumber; + ResolvedLinkTo = resolvedLinkTo; + Position = position; + EventOrLinkTargetPosition = eventOrLinkTargetPosition; EventId = eventId; EventType = eventType; IsJson = isJson; @@ -158,12 +141,12 @@ public ResolvedEvent( if (string.IsNullOrEmpty(eventType)) throw new ArgumentException("Empty eventType provided."); - _positionStreamId = positionStreamId; - _positionSequenceNumber = positionSequenceNumber; - _eventStreamId = eventStreamId; - _eventSequenceNumber = eventSequenceNumber; - _resolvedLinkTo = resolvedLinkTo; - _position = position; + PositionStreamId = positionStreamId; + PositionSequenceNumber = positionSequenceNumber; + EventStreamId = eventStreamId; + EventSequenceNumber = eventSequenceNumber; + ResolvedLinkTo = resolvedLinkTo; + Position = position; EventId = eventId; EventType = eventType; IsJson = isJson; @@ -176,42 +159,21 @@ public ResolvedEvent( StreamMetadata = streamMetadata; } - public string EventStreamId { - get { return _eventStreamId; } - } + public string EventStreamId { get; } - public long EventSequenceNumber { - get { return _eventSequenceNumber; } - } + public long EventSequenceNumber { get; } - public bool ResolvedLinkTo { - get { return _resolvedLinkTo; } - } + public bool ResolvedLinkTo { get; } - public string PositionStreamId { - get { return _positionStreamId; } - } + public string PositionStreamId { get; } - public long PositionSequenceNumber { - get { return _positionSequenceNumber; } - } + public long PositionSequenceNumber { get; } - public TFPos Position { - get { return _position; } - } + public TFPos Position { get; } - public TFPos EventOrLinkTargetPosition { - get { return _eventOrLinkTargetPosition; } - } + public TFPos EventOrLinkTargetPosition { get; } - public TFPos LinkOrEventPosition { - get { return _linkOrEventPosition; } - } + public TFPos LinkOrEventPosition { get; } - public bool IsStreamDeletedEvent { - get { - string temp; - return StreamDeletedHelper.IsStreamDeletedEvent(EventStreamId, EventType, Data, out temp); - } - } + public bool IsStreamDeletedEvent => StreamDeletedHelper.IsStreamDeletedEvent(EventStreamId, EventType, Data, out _); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/CategoryEventFilter.cs b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/CategoryEventFilter.cs index fb175d0fc4d..736a4039c64 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/CategoryEventFilter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/CategoryEventFilter.cs @@ -6,15 +6,8 @@ namespace KurrentDB.Projections.Core.Services.Processing.SingleStream; -public class CategoryEventFilter : EventFilter { - private readonly string _category; - private readonly string _categoryStream; - - public CategoryEventFilter(string category, bool allEvents, HashSet events) - : base(allEvents, false, events) { - _category = category; - _categoryStream = "$ce-" + category; - } +public class CategoryEventFilter(string category, bool allEvents, HashSet events) : EventFilter(allEvents, false, events) { + private readonly string _categoryStream = $"$ce-{category}"; protected override bool DeletedNotificationPasses(string positionStreamId) { return _categoryStream == positionStreamId; @@ -24,14 +17,12 @@ public override bool PassesSource(bool resolvedFromLinkTo, string positionStream return resolvedFromLinkTo && _categoryStream == positionStreamId; } - public override string GetCategory(string positionStreamId) { - if (!positionStreamId.StartsWith("$ce-")) - throw new ArgumentException(string.Format("'{0}' is not a category stream", positionStreamId), - "positionStreamId"); - return positionStreamId.Substring("$ce-".Length); - } + public override string GetCategory(string positionStreamId) + => !positionStreamId.StartsWith("$ce-") + ? throw new ArgumentException($"'{positionStreamId}' is not a category stream", nameof(positionStreamId)) + : positionStreamId["$ce-".Length..]; public override string ToString() { - return string.Format("Category: {0}, CategoryStream: {1}", _category, _categoryStream); + return $"Category: {category}, CategoryStream: {_categoryStream}"; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventFilter.cs b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventFilter.cs index 867c9fdf0db..c3e8719e1b0 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventFilter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventFilter.cs @@ -5,20 +5,13 @@ namespace KurrentDB.Projections.Core.Services.Processing.SingleStream; -public class StreamEventFilter : EventFilter { - private readonly string _streamId; - - public StreamEventFilter(string streamId, bool allEvents, HashSet events) - : base(allEvents, false, events) { - _streamId = streamId; - } - +public class StreamEventFilter(string streamId, bool allEvents, HashSet events) : EventFilter(allEvents, false, events) { protected override bool DeletedNotificationPasses(string positionStreamId) { - return positionStreamId == _streamId; + return positionStreamId == streamId; } public override bool PassesSource(bool resolvedFromLinkTo, string positionStreamId, string eventType) { - return positionStreamId == _streamId; + return positionStreamId == streamId; } public override string GetCategory(string positionStreamId) { @@ -26,6 +19,6 @@ public override string GetCategory(string positionStreamId) { } public override string ToString() { - return string.Format("StreamId: {0}", _streamId); + return $"StreamId: {streamId}"; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventReader.cs index 87450f3e55b..51917bf6ead 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamEventReader.cs @@ -25,7 +25,7 @@ public class StreamEventReader : EventReader, private readonly bool _produceStreamDeletes; private bool _eventsRequested; - private int _maxReadCount = 111; + private const int MaxReadCount = 111; private long _lastPosition; private bool _eof; private Guid _pendingRequestCorrelationId; @@ -41,12 +41,8 @@ public StreamEventReader( bool produceStreamDeletes, bool stopOnEof = false) : base(publisher, eventReaderCorrelationId, readAs, stopOnEof) { - if (fromSequenceNumber < 0) - throw new ArgumentException("fromSequenceNumber"); - if (streamName == null) - throw new ArgumentNullException("streamName"); - if (string.IsNullOrEmpty(streamName)) - throw new ArgumentException("streamName"); + ArgumentOutOfRangeException.ThrowIfNegative(fromSequenceNumber); + ArgumentException.ThrowIfNullOrEmpty(streamName); _streamName = streamName; _fromSequenceNumber = fromSequenceNumber; _timeProvider = timeProvider; @@ -64,8 +60,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { if (!_eventsRequested) throw new InvalidOperationException("Read events has not been requested"); if (message.EventStreamId != _streamName) - throw new InvalidOperationException( - string.Format("Invalid stream name: {0}. Expected: {1}", message.EventStreamId, _streamName)); + throw new InvalidOperationException($"Invalid stream name: {message.EventStreamId}. Expected: {_streamName}"); if (Paused) throw new InvalidOperationException("Paused"); if (message.CorrelationId != _pendingRequestCorrelationId) { @@ -81,7 +76,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { DeliverSafeJoinPosition(GetLastCommitPositionFrom(message)); // allow joining heading distribution PauseOrContinueProcessing(); SendIdle(); - SendPartitionDeleted_WhenReadingDataStream(_streamName, -1, null, null, null, null); + SendPartitionDeleted_WhenReadingDataStream(_streamName, null, null, null, null); SendEof(); break; case ReadStreamResult.NoStream: @@ -90,7 +85,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { PauseOrContinueProcessing(); SendIdle(); if (message.LastEventNumber >= 0) - SendPartitionDeleted_WhenReadingDataStream(_streamName, message.LastEventNumber, null, null, + SendPartitionDeleted_WhenReadingDataStream(_streamName, null, null, null, null); SendEof(); break; @@ -113,7 +108,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { } else { for (int index = 0; index < message.Events.Count; index++) { var @event = message.Events[index].Event; - var @link = message.Events[index].Link; + var link = message.Events[index].Link; DeliverEvent(message.Events[index], 100.0f * (link ?? @event).EventNumber / message.LastEventNumber, ref oldFromSequenceNumber); @@ -125,8 +120,7 @@ public void Handle(ClientMessage.ReadStreamEventsForwardCompleted message) { SendNotAuthorized(); return; default: - throw new NotSupportedException( - string.Format("ReadEvents result code was not recognized. Code: {0}", message.Result)); + throw new NotSupportedException($"ReadEvents result code was not recognized. Code: {message.Result}"); } } @@ -142,19 +136,14 @@ public void Handle(ProjectionManagementMessage.Internal.ReadTimeout message) { PauseOrContinueProcessing(); } - private long StartFrom(ClientMessage.ReadStreamEventsForwardCompleted message, long fromSequenceNumber) { + private static long StartFrom(ClientMessage.ReadStreamEventsForwardCompleted message, long fromSequenceNumber) { if (fromSequenceNumber != 0) return fromSequenceNumber; - if (message.Events is not []) { - return message.Events[0].OriginalEventNumber; - } - - return fromSequenceNumber; + return message.Events is not [] ? message.Events[0].OriginalEventNumber : fromSequenceNumber; } private void SendIdle() { - _publisher.Publish( - new ReaderSubscriptionMessage.EventReaderIdle(EventReaderCorrelationId, _timeProvider.UtcNow)); + _publisher.Publish(new ReaderSubscriptionMessage.EventReaderIdle(EventReaderCorrelationId, _timeProvider.UtcNow)); } protected override void RequestEvents() { @@ -198,7 +187,7 @@ private Message CreateReadTimeoutMessage(Guid correlationId, string streamId) { private Message CreateReadEventsMessage(Guid readCorrelationId) { return new ClientMessage.ReadStreamEventsForward( readCorrelationId, readCorrelationId, new SendToThisEnvelope(this), _streamName, _fromSequenceNumber, - _maxReadCount, _resolveLinkTos, false, null, ReadAs, replyOnExpired: false); + MaxReadCount, _resolveLinkTos, false, null, ReadAs, replyOnExpired: false); } private void DeliverSafeJoinPosition(long? safeJoinPosition) { @@ -206,39 +195,34 @@ private void DeliverSafeJoinPosition(long? safeJoinPosition) { return; //TODO: this should not happen, but StorageReader does not return it now _publisher.Publish( new ReaderSubscriptionMessage.CommittedEventDistributed( - EventReaderCorrelationId, null, safeJoinPosition, 100.0f, source: this.GetType())); + EventReaderCorrelationId, null, safeJoinPosition, 100.0f, source: GetType())); } private void DeliverEvent(KurrentDB.Core.Data.ResolvedEvent pair, float progress, ref long sequenceNumber) { EventRecord positionEvent = pair.OriginalEvent; if (positionEvent.EventNumber != sequenceNumber) { // This can happen when the original stream has $maxAge/$maxCount set - _publisher.Publish(new ReaderSubscriptionMessage.Faulted(EventReaderCorrelationId, string.Format( - "Event number {0} was expected in the stream {1}, but event number {2} was received. This may happen if events have been deleted from the beginning of your stream, please reset your projection.", - sequenceNumber, _streamName, positionEvent.EventNumber), this.GetType())); + _publisher.Publish(new ReaderSubscriptionMessage.Faulted(EventReaderCorrelationId, + $"Event number {sequenceNumber} was expected in the stream {_streamName}, but event number {positionEvent.EventNumber} was received. This may happen if events have been deleted from the beginning of your stream, please reset your projection.", GetType())); return; } sequenceNumber = positionEvent.EventNumber + 1; var resolvedEvent = new ResolvedEvent(pair, null); - string deletedPartitionStreamId; - if (resolvedEvent.IsLinkToDeletedStream && !resolvedEvent.IsLinkToDeletedStreamTombstone) return; bool isDeletedStreamEvent = StreamDeletedHelper.IsStreamDeletedEventOrLinkToStreamDeletedEvent(resolvedEvent, pair.ResolveResult, - out deletedPartitionStreamId); + out var deletedPartitionStreamId); if (isDeletedStreamEvent) { - var deletedPartition = deletedPartitionStreamId; - if (_produceStreamDeletes) _publisher.Publish( //TODO: publish both link and event data new ReaderSubscriptionMessage.EventReaderPartitionDeleted( - EventReaderCorrelationId, deletedPartition, source: this.GetType(), + EventReaderCorrelationId, deletedPartitionStreamId, source: GetType(), deleteEventOrLinkTargetPosition: null, deleteLinkOrEventPosition: resolvedEvent.EventOrLinkTargetPosition, positionStreamId: resolvedEvent.PositionStreamId, @@ -247,7 +231,7 @@ private void DeliverEvent(KurrentDB.Core.Data.ResolvedEvent pair, float progress _publisher.Publish( //TODO: publish both link and event data new ReaderSubscriptionMessage.CommittedEventDistributed( - EventReaderCorrelationId, resolvedEvent, _stopOnEof ? (long?)null : positionEvent.LogPosition, - progress, source: this.GetType())); + EventReaderCorrelationId, resolvedEvent, _stopOnEof ? null : positionEvent.LogPosition, + progress, source: GetType())); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamPositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamPositionTagger.cs index 4b9ba8e9bf4..e6ee71da232 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamPositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/SingleStream/StreamPositionTagger.cs @@ -12,10 +12,7 @@ public class StreamPositionTagger : PositionTagger { private readonly string _stream; public StreamPositionTagger(int phase, string stream) : base(phase) { - if (stream == null) - throw new ArgumentNullException("stream"); - if (string.IsNullOrEmpty(stream)) - throw new ArgumentException("stream"); + ArgumentException.ThrowIfNullOrEmpty(stream); _stream = stream; } @@ -24,7 +21,7 @@ public override bool IsMessageAfterCheckpointTag( if (previous.Phase < Phase) return true; if (previous.Mode_ != CheckpointTag.Mode.Stream) - throw new ArgumentException("Mode.Stream expected", "previous"); + throw new ArgumentException("Mode.Stream expected", nameof(previous)); return committedEvent.Data.PositionStreamId == _stream && committedEvent.Data.PositionSequenceNumber > previous.Streams[_stream]; } @@ -32,13 +29,11 @@ public override bool IsMessageAfterCheckpointTag( public override CheckpointTag MakeCheckpointTag( CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); + throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}"); if (committedEvent.Data.PositionStreamId != _stream) throw new InvalidOperationException( - string.Format( - "Invalid stream '{0}'. Expected stream is '{1}'", committedEvent.Data.EventStreamId, _stream)); + $"Invalid stream '{committedEvent.Data.EventStreamId}'. Expected stream is '{_stream}'"); return CheckpointTag.FromStreamPosition(previous.Phase, committedEvent.Data.PositionStreamId, committedEvent.Data.PositionSequenceNumber); } @@ -46,17 +41,13 @@ public override CheckpointTag MakeCheckpointTag( public override CheckpointTag MakeCheckpointTag( CheckpointTag previous, ReaderSubscriptionMessage.EventReaderPartitionDeleted partitionDeleted) { if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); + throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}"); if (partitionDeleted.PositionStreamId != _stream) - throw new InvalidOperationException( - string.Format( - "Invalid stream '{0}'. Expected stream is '{1}'", partitionDeleted.Partition, _stream)); + throw new InvalidOperationException($"Invalid stream '{partitionDeleted.Partition}'. Expected stream is '{_stream}'"); // return ordinary checkpoint tag (suitable for fromCategory.foreachStream as well as for regular fromStream - return CheckpointTag.FromStreamPosition( - previous.Phase, partitionDeleted.PositionStreamId, partitionDeleted.PositionEventNumber.Value); + return CheckpointTag.FromStreamPosition(previous.Phase, partitionDeleted.PositionStreamId, partitionDeleted.PositionEventNumber.Value); } public override CheckpointTag MakeZeroCheckpointTag() { @@ -72,33 +63,24 @@ public override CheckpointTag AdjustTag(CheckpointTag tag) { return tag; if (tag.Phase > Phase) throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected less or equal to: {0} Was: {1}", Phase, - tag.Phase), "tag"); - + $"Invalid checkpoint tag phase. Expected less or equal to: {Phase} Was: {tag.Phase}", nameof(tag)); if (tag.Mode_ == CheckpointTag.Mode.Stream) { - long p; return CheckpointTag.FromStreamPosition( - tag.Phase, _stream, tag.Streams.TryGetValue(_stream, out p) ? p : -1); + tag.Phase, _stream, tag.Streams.TryGetValue(_stream, out var p) ? p : -1); } - switch (tag.Mode_) { - case CheckpointTag.Mode.EventTypeIndex: - throw new NotSupportedException( - "Conversion from EventTypeIndex to Stream position tag is not supported"); - case CheckpointTag.Mode.PreparePosition: - throw new NotSupportedException( - "Conversion from PreparePosition to Stream position tag is not supported"); - case CheckpointTag.Mode.MultiStream: - long p; - return CheckpointTag.FromStreamPosition( - tag.Phase, _stream, tag.Streams.TryGetValue(_stream, out p) ? p : -1); - case CheckpointTag.Mode.Position: - throw new NotSupportedException("Conversion from Position to Stream position tag is not supported"); - default: - throw new NotSupportedException(string.Format( - "The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {0}", - tag.ToString())); - } + return tag.Mode_ switch { + CheckpointTag.Mode.EventTypeIndex => throw new NotSupportedException( + "Conversion from EventTypeIndex to Stream position tag is not supported"), + CheckpointTag.Mode.PreparePosition => throw new NotSupportedException( + "Conversion from PreparePosition to Stream position tag is not supported"), + CheckpointTag.Mode.MultiStream => CheckpointTag.FromStreamPosition(tag.Phase, _stream, + tag.Streams.TryGetValue(_stream, out var p) ? p : -1), + CheckpointTag.Mode.Position => throw new NotSupportedException( + "Conversion from Position to Stream position tag is not supported"), + _ => throw new NotSupportedException( + $"The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {tag}") + }; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/SourceDefinitionBuilder.cs b/src/KurrentDB.Projections.Core/Services/Processing/SourceDefinitionBuilder.cs index c02fcaef24f..74247c75b6a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/SourceDefinitionBuilder.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/SourceDefinitionBuilder.cs @@ -8,32 +8,27 @@ namespace KurrentDB.Projections.Core.Services.Processing; public sealed class SourceDefinitionBuilder : IQuerySources { - private readonly QuerySourceOptions _options = new QuerySourceOptions(); - private bool _allStreams; + private readonly QuerySourceOptions _options = new(); private List _categories; private List _streams; private bool _allEvents; private List _events; - private bool _byStream; - private bool _byCustomPartitions; public SourceDefinitionBuilder() { _options.DefinesFold = true; } public void FromAll() { - _allStreams = true; + AllStreams = true; } public void FromCategory(string categoryName) { - if (_categories == null) - _categories = new List(); + _categories ??= []; _categories.Add(categoryName); } public void FromStream(string streamName) { - if (_streams == null) - _streams = new List(); + _streams ??= []; _streams.Add(streamName); } @@ -50,17 +45,16 @@ public void SetIncludeLinks(bool includeLinks = true) { } public void IncludeEvent(string eventName) { - if (_events == null) - _events = new List(); + _events ??= []; _events.Add(eventName); } public void SetByStream() { - _byStream = true; + ByStreams = true; } public void SetByCustomPartitions() { - _byCustomPartitions = true; + ByCustomPartitions = true; } public void SetDefinesStateTransform() { @@ -80,11 +74,11 @@ public void SetDefinesFold() { } public void SetResultStreamNameOption(string resultStreamName) { - _options.ResultStreamName = String.IsNullOrWhiteSpace(resultStreamName) ? null : resultStreamName; + _options.ResultStreamName = string.IsNullOrWhiteSpace(resultStreamName) ? null : resultStreamName; } public void SetPartitionResultStreamNamePatternOption(string partitionResultStreamNamePattern) { - _options.PartitionResultStreamNamePattern = String.IsNullOrWhiteSpace(partitionResultStreamNamePattern) + _options.PartitionResultStreamNamePattern = string.IsNullOrWhiteSpace(partitionResultStreamNamePattern) ? null : partitionResultStreamNamePattern; } @@ -105,73 +99,39 @@ public void SetHandlesStreamDeletedNotifications(bool value = true) { _options.HandlesDeletedNotifications = value; } - public bool AllStreams { - get { return _allStreams; } - } + public bool AllStreams { get; private set; } - public string[] Categories { - get { return _categories != null ? _categories.ToArray() : null; } - } + public string[] Categories => _categories?.ToArray(); - public string[] Streams { - get { return _streams != null ? _streams.ToArray() : null; } - } + public string[] Streams => _streams?.ToArray(); - bool IQuerySources.AllEvents { - get { return _allEvents; } - } + bool IQuerySources.AllEvents => _allEvents; - public string[] Events { - get { return _events != null ? _events.ToArray() : null; } - } + public string[] Events => _events?.ToArray(); - public bool ByStreams { - get { return _byStream; } - } + public bool ByStreams { get; private set; } - public bool ByCustomPartitions { - get { return _byCustomPartitions; } - } + public bool ByCustomPartitions { get; private set; } - public bool DefinesStateTransform { - get { return _options.DefinesStateTransform; } - } + public bool DefinesStateTransform => _options.DefinesStateTransform; - public bool ProducesResults { - get { return _options.ProducesResults; } - } + public bool ProducesResults => _options.ProducesResults; - public bool DefinesFold { - get { return _options.DefinesFold; } - } + public bool DefinesFold => _options.DefinesFold; - public bool HandlesDeletedNotifications { - get { return _options.HandlesDeletedNotifications; } - } + public bool HandlesDeletedNotifications => _options.HandlesDeletedNotifications; - public bool IncludeLinksOption { - get { return _options.IncludeLinks; } - } + public bool IncludeLinksOption => _options.IncludeLinks; - public string ResultStreamNameOption { - get { return _options.ResultStreamName; } - } + public string ResultStreamNameOption => _options.ResultStreamName; - public string PartitionResultStreamNamePatternOption { - get { return _options.PartitionResultStreamNamePattern; } - } + public string PartitionResultStreamNamePatternOption => _options.PartitionResultStreamNamePattern; - public bool ReorderEventsOption { - get { return _options.ReorderEvents; } - } + public bool ReorderEventsOption => _options.ReorderEvents; - public int? ProcessingLagOption { - get { return _options.ProcessingLag; } - } + public int? ProcessingLagOption => _options.ProcessingLag; - public bool IsBiState { - get { return _options.IsBiState; } - } + public bool IsBiState => _options.IsBiState; public static IQuerySources From(Action configure) { var b = new SourceDefinitionBuilder(); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/StagedProcessingQueue.cs b/src/KurrentDB.Projections.Core/Services/Processing/StagedProcessingQueue.cs index a5e238236c5..a0bd8cec5d8 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/StagedProcessingQueue.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/StagedProcessingQueue.cs @@ -17,27 +17,18 @@ namespace KurrentDB.Projections.Core.Services.Processing; /// it and store. But no subprojection can process events prior to preceding projections has completed processing. /// public class StagedProcessingQueue { - private class TaskEntry { - public readonly StagedTask Task; - public readonly long Sequence; + private class TaskEntry(StagedTask task, long sequence) { + public readonly StagedTask Task = task; + public readonly long Sequence = sequence; public bool Busy; public object BusyCorrelationId; public TaskEntry PreviousByCorrelation; public TaskEntry NextByCorrelation; public TaskEntry Next; public bool Completed; - public int ReadForStage; + public int ReadForStage = -1; - public TaskEntry(StagedTask task, long sequence) { - Task = task; - ReadForStage = -1; - Sequence = sequence; - } - - public override string ToString() { - return string.Format("ReadForStage: {3}, Busy: {1}, Completed: {2} => {0}", Task, Busy, Completed, - ReadForStage); - } + public override string ToString() => $"ReadForStage: {ReadForStage}, Busy: {Busy}, Completed: {Completed} => {Task}"; } private class StageEntry { @@ -46,14 +37,13 @@ private class StageEntry { } private readonly bool[] _orderedStage; - private readonly Dictionary _correlationLastEntries = new Dictionary(); + private readonly Dictionary _correlationLastEntries = new(); private StageEntry[] _byUnorderedStageFirst; // null means all processed? so append need to set it? private StageEntry[] _byUnorderedStageLast; // null means all processed? so append need to set it? private TaskEntry[] _byOrderedStageLast; // null means all processed? so append need to set it? - private long _sequence = 0; + private long _sequence; private TaskEntry _first; private TaskEntry _last; - private int _count; private readonly int _maxStage; public event Action EnsureTickPending; @@ -65,20 +55,18 @@ public StagedProcessingQueue(bool[] orderedStage) { _maxStage = _orderedStage.Length - 1; } - public int Count { - get { return _count; } - } + public int Count { get; private set; } public void Enqueue(StagedTask stagedTask) { var entry = new TaskEntry(stagedTask, ++_sequence); if (_first == null) { _first = entry; _last = entry; - _count = 1; + Count = 1; } else { _last.Next = entry; _last = entry; - _count++; + Count++; } // re-initialize already completed queues @@ -93,7 +81,7 @@ public void Enqueue(StagedTask stagedTask) { public bool Process(int max = 1) { int processed = 0; int fromStage = _maxStage; - while (_count > 0 && processed < max) { + while (Count > 0 && processed < max) { RemoveCompleted(); var entry = GetEntryToProcess(fromStage); if (entry == null) @@ -121,14 +109,14 @@ private TaskEntry GetEntryToProcess(int fromStage) { TaskEntry task = null; if (!_orderedStage[stageIndex]) { if (_byUnorderedStageFirst[stageIndex] != null - && _byUnorderedStageFirst[stageIndex].Entry.PreviousByCorrelation == null) { + && _byUnorderedStageFirst[stageIndex].Entry.PreviousByCorrelation == null) { var stageEntry = _byUnorderedStageFirst[stageIndex]; task = stageEntry.Entry; } } else { var taskEntry = _byOrderedStageLast[stageIndex]; if (taskEntry != null && taskEntry.ReadForStage == stageIndex && !taskEntry.Busy - && !taskEntry.Completed && taskEntry.PreviousByCorrelation == null) + && !taskEntry.Completed && taskEntry.PreviousByCorrelation == null) task = taskEntry; } @@ -137,21 +125,19 @@ private TaskEntry GetEntryToProcess(int fromStage) { continue; } - if (task.ReadForStage != stageIndex) - throw new Exception(); - return task; + return task.ReadForStage != stageIndex ? throw new Exception() : task; } return null; } private void RemoveCompleted() { - while (_first != null && _first.Completed) { + while (_first is { Completed: true }) { var task = _first; _first = task.Next; if (_first == null) _last = null; - _count--; + Count--; if (task.BusyCorrelationId != null) { var nextByCorrelation = task.NextByCorrelation; if (nextByCorrelation != null) { @@ -181,36 +167,34 @@ private void CompleteTaskProcessing(TaskEntry entry, int readyForStage, object n } else EnqueueForStage(entry, readyForStage); - if (EnsureTickPending != null) - EnsureTickPending(); + EnsureTickPending?.Invoke(); } private void EnqueueForStage(TaskEntry entry, int readyForStage) { entry.ReadForStage = readyForStage; - if (!_orderedStage[readyForStage] && (entry.PreviousByCorrelation == null)) { - var stageEntry = new StageEntry { Entry = entry, Next = null }; - if (_byUnorderedStageFirst[readyForStage] != null) { - _byUnorderedStageLast[readyForStage].Next = stageEntry; - _byUnorderedStageLast[readyForStage] = stageEntry; - } else { - _byUnorderedStageFirst[readyForStage] = stageEntry; - _byUnorderedStageLast[readyForStage] = stageEntry; - } + if (_orderedStage[readyForStage] || entry.PreviousByCorrelation != null) + return; + + var stageEntry = new StageEntry { Entry = entry, Next = null }; + if (_byUnorderedStageFirst[readyForStage] != null) { + _byUnorderedStageLast[readyForStage].Next = stageEntry; + } else { + _byUnorderedStageFirst[readyForStage] = stageEntry; } + + _byUnorderedStageLast[readyForStage] = stageEntry; } private void AdvanceStage(int stage, TaskEntry entry) { if (!_orderedStage[stage]) { if (_byUnorderedStageFirst[stage].Entry != entry) - throw new ArgumentException( - string.Format("entry is not a head of the queue at the stage {0}", stage), "entry"); + throw new ArgumentException($"entry is not a head of the queue at the stage {stage}", nameof(entry)); _byUnorderedStageFirst[stage] = _byUnorderedStageFirst[stage].Next; if (_byUnorderedStageFirst[stage] == null) _byUnorderedStageLast[stage] = null; } else { if (_byOrderedStageLast[stage] != entry) - throw new ArgumentException( - string.Format("entry is not a head of the queue at the stage {0}", stage), "entry"); + throw new ArgumentException($"entry is not a head of the queue at the stage {stage}", nameof(entry)); _byOrderedStageLast[stage] = entry.Next; } } @@ -224,8 +208,7 @@ private void SetEntryCorrelation(TaskEntry entry, object newCorrelationId) { entry.BusyCorrelationId = newCorrelationId; if (newCorrelationId != null) { - TaskEntry lastEntry; - if (_correlationLastEntries.TryGetValue(newCorrelationId, out lastEntry)) { + if (_correlationLastEntries.TryGetValue(newCorrelationId, out var lastEntry)) { if (entry.Sequence < lastEntry.Sequence) //NOTE: should never happen as we require ordered stage or initialization throw new InvalidOperationException( @@ -239,7 +222,7 @@ private void SetEntryCorrelation(TaskEntry entry, object newCorrelationId) { } } - private void MarkCompletedTask(TaskEntry entry) { + private static void MarkCompletedTask(TaskEntry entry) { entry.Completed = true; } @@ -248,18 +231,14 @@ public void Initialize() { _byUnorderedStageFirst = new StageEntry[_orderedStage.Length]; _byUnorderedStageLast = new StageEntry[_orderedStage.Length]; _byOrderedStageLast = new TaskEntry[_orderedStage.Length]; - _count = 0; + Count = 0; _first = null; _last = null; } } -public abstract class StagedTask { - public readonly object InitialCorrelationId; - - protected StagedTask(object initialCorrelationId) { - InitialCorrelationId = initialCorrelationId; - } +public abstract class StagedTask(object initialCorrelationId) { + public readonly object InitialCorrelationId = initialCorrelationId; public abstract void Process(int onStage, Action readyForStage); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ContinuousProjectionProcessingStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ContinuousProjectionProcessingStrategy.cs index 07796a25b99..39f4c305da2 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ContinuousProjectionProcessingStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ContinuousProjectionProcessingStrategy.cs @@ -12,16 +12,18 @@ namespace KurrentDB.Projections.Core.Services.Processing.Strategies; -public class ContinuousProjectionProcessingStrategy : DefaultProjectionProcessingStrategy { - public ContinuousProjectionProcessingStrategy( - string name, ProjectionVersion projectionVersion, IProjectionStateHandler stateHandler, - ProjectionConfig projectionConfig, IQuerySources sourceDefinition, ILogger logger, - ReaderSubscriptionDispatcher subscriptionDispatcher, bool enableContentTypeValidation, int maxProjectionStateSize) - : base( - name, projectionVersion, stateHandler, projectionConfig, sourceDefinition, logger, - subscriptionDispatcher, enableContentTypeValidation, maxProjectionStateSize) { - } - +public class ContinuousProjectionProcessingStrategy( + string name, + ProjectionVersion projectionVersion, + IProjectionStateHandler stateHandler, + ProjectionConfig projectionConfig, + IQuerySources sourceDefinition, + ILogger logger, + ReaderSubscriptionDispatcher subscriptionDispatcher, + bool enableContentTypeValidation, + int maxProjectionStateSize) + : DefaultProjectionProcessingStrategy(name, projectionVersion, stateHandler, projectionConfig, sourceDefinition, logger, + subscriptionDispatcher, enableContentTypeValidation, maxProjectionStateSize) { public override bool GetStopOnEof() { return false; } @@ -36,19 +38,18 @@ public override bool GetProducesRunningResults() { protected override IProjectionProcessingPhase[] CreateProjectionProcessingPhases( IPublisher publisher, - IPublisher inputQueue, Guid projectionCorrelationId, ProjectionNamesBuilder namingBuilder, PartitionStateCache partitionStateCache, CoreProjection coreProjection, IODispatcher ioDispatcher, IProjectionProcessingPhase firstPhase) { - return new IProjectionProcessingPhase[] { firstPhase }; + return [firstPhase]; } protected override IResultEventEmitter CreateFirstPhaseResultEmitter(ProjectionNamesBuilder namingBuilder) { return _sourceDefinition.ProducesResults ? new ResultEventEmitter(namingBuilder) - : (IResultEventEmitter)new NoopResultEventEmitter(); + : new NoopResultEventEmitter(); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/DefaultProjectionProcessingStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/DefaultProjectionProcessingStrategy.cs index 2cad0da2b75..e52afee0485 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/DefaultProjectionProcessingStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/DefaultProjectionProcessingStrategy.cs @@ -12,18 +12,19 @@ namespace KurrentDB.Projections.Core.Services.Processing.Strategies; -public abstract class DefaultProjectionProcessingStrategy : EventReaderBasedProjectionProcessingStrategy { - private readonly IProjectionStateHandler _stateHandler; - - protected DefaultProjectionProcessingStrategy( - string name, ProjectionVersion projectionVersion, IProjectionStateHandler stateHandler, - ProjectionConfig projectionConfig, IQuerySources sourceDefinition, ILogger logger, - ReaderSubscriptionDispatcher subscriptionDispatcher, bool enableContentTypeValidation, int maxProjectionStateSize) - : base(name, projectionVersion, projectionConfig, sourceDefinition, logger, subscriptionDispatcher, - enableContentTypeValidation, maxProjectionStateSize) { - _stateHandler = stateHandler; - } - +public abstract class DefaultProjectionProcessingStrategy( + string name, + ProjectionVersion projectionVersion, + IProjectionStateHandler stateHandler, + ProjectionConfig projectionConfig, + IQuerySources sourceDefinition, + ILogger logger, + ReaderSubscriptionDispatcher subscriptionDispatcher, + bool enableContentTypeValidation, + int maxProjectionStateSize) + : EventReaderBasedProjectionProcessingStrategy(name, projectionVersion, projectionConfig, sourceDefinition, logger, + subscriptionDispatcher, + enableContentTypeValidation, maxProjectionStateSize) { protected override IProjectionProcessingPhase CreateFirstProcessingPhase( IPublisher publisher, IPublisher inputQueue, @@ -47,7 +48,7 @@ protected override IProjectionProcessingPhase CreateFirstProcessingPhase( inputQueue, _projectionConfig, updateStatistics, - _stateHandler, + stateHandler, partitionStateCache, _sourceDefinition.DefinesStateTransform, _name, @@ -59,18 +60,18 @@ protected override IProjectionProcessingPhase CreateFirstProcessingPhase( readerStrategy, resultWriter, _projectionConfig.CheckpointsEnabled, - this.GetStopOnEof(), + GetStopOnEof(), _sourceDefinition.IsBiState, orderedPartitionProcessing: orderedPartitionProcessing, emittedStreamsTracker: emittedStreamsTracker, enableContentTypeValidation: _enableContentTypeValidation); } - protected virtual StatePartitionSelector CreateStatePartitionSelector() { + protected StatePartitionSelector CreateStatePartitionSelector() { return _sourceDefinition.ByCustomPartitions - ? new ByHandleStatePartitionSelector(_stateHandler) - : (_sourceDefinition.ByStreams - ? (StatePartitionSelector)new ByStreamStatePartitionSelector() - : new NoopStatePartitionSelector()); + ? new ByHandleStatePartitionSelector(stateHandler) + : _sourceDefinition.ByStreams + ? new ByStreamStatePartitionSelector() + : new NoopStatePartitionSelector(); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/EventReaderBasedProjectionProcessingStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/EventReaderBasedProjectionProcessingStrategy.cs index dea9d7ece85..b7e853b6e55 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/EventReaderBasedProjectionProcessingStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/EventReaderBasedProjectionProcessingStrategy.cs @@ -23,9 +23,14 @@ public abstract class EventReaderBasedProjectionProcessingStrategy : ProjectionP protected readonly bool _enableContentTypeValidation; protected EventReaderBasedProjectionProcessingStrategy( - string name, ProjectionVersion projectionVersion, ProjectionConfig projectionConfig, - IQuerySources sourceDefinition, ILogger logger, ReaderSubscriptionDispatcher subscriptionDispatcher, - bool enableContentTypeValidation, int maxProjectionStateSize) + string name, + ProjectionVersion projectionVersion, + ProjectionConfig projectionConfig, + IQuerySources sourceDefinition, + ILogger logger, + ReaderSubscriptionDispatcher subscriptionDispatcher, + bool enableContentTypeValidation, + int maxProjectionStateSize) : base(name, projectionVersion, logger, maxProjectionStateSize) { _projectionConfig = projectionConfig; _sourceDefinition = sourceDefinition; @@ -34,7 +39,7 @@ protected EventReaderBasedProjectionProcessingStrategy( _enableContentTypeValidation = enableContentTypeValidation; } - public override sealed IProjectionProcessingPhase[] CreateProcessingPhases( + public sealed override IProjectionProcessingPhase[] CreateProcessingPhases( IPublisher publisher, IPublisher inputQueue, Guid projectionCorrelationId, @@ -45,28 +50,20 @@ public override sealed IProjectionProcessingPhase[] CreateProcessingPhases( ITimeProvider timeProvider, IODispatcher ioDispatcher, CoreProjectionCheckpointWriter coreProjectionCheckpointWriter) { - var definesFold = _sourceDefinition.DefinesFold; - var readerStrategy = CreateReaderStrategy(timeProvider); - var zeroCheckpointTag = readerStrategy.PositionTagger.MakeZeroCheckpointTag(); - var checkpointManager = CreateCheckpointManager( projectionCorrelationId, publisher, ioDispatcher, namingBuilder, coreProjectionCheckpointWriter, - definesFold, readerStrategy); - var resultWriter = CreateFirstPhaseResultWriter( checkpointManager as IEmittedEventWriter, zeroCheckpointTag, namingBuilder); - var emittedStreamsTracker = new EmittedStreamsTracker(ioDispatcher, _projectionConfig, namingBuilder); - var firstPhase = CreateFirstProcessingPhase( publisher, inputQueue, @@ -80,10 +77,8 @@ public override sealed IProjectionProcessingPhase[] CreateProcessingPhases( readerStrategy, resultWriter, emittedStreamsTracker); - return CreateProjectionProcessingPhases( publisher, - inputQueue, projectionCorrelationId, namingBuilder, partitionStateCache, @@ -106,21 +101,13 @@ protected abstract IProjectionProcessingPhase CreateFirstProcessingPhase( IResultWriter resultWriter, IEmittedStreamsTracker emittedStreamsTracker); - protected virtual IReaderStrategy CreateReaderStrategy(ITimeProvider timeProvider) { - return ReaderStrategy.Create( - _name, - 0, - _sourceDefinition, - timeProvider, - _projectionConfig.StopOnEof, - _projectionConfig.RunAs); - } + private IReaderStrategy CreateReaderStrategy(ITimeProvider timeProvider) + => ReaderStrategy.Create(_name, 0, _sourceDefinition, timeProvider, _projectionConfig.RunAs); protected abstract IResultEventEmitter CreateFirstPhaseResultEmitter(ProjectionNamesBuilder namingBuilder); protected abstract IProjectionProcessingPhase[] CreateProjectionProcessingPhases( IPublisher publisher, - IPublisher inputQueue, Guid projectionCorrelationId, ProjectionNamesBuilder namingBuilder, PartitionStateCache partitionStateCache, @@ -128,44 +115,41 @@ protected abstract IProjectionProcessingPhase[] CreateProjectionProcessingPhases IODispatcher ioDispatcher, IProjectionProcessingPhase firstPhase); - protected override IQuerySources GetSourceDefinition() { - return _sourceDefinition; - } + protected override IQuerySources GetSourceDefinition() => _sourceDefinition; - public override bool GetRequiresRootPartition() { - return !(_sourceDefinition.ByStreams || _sourceDefinition.ByCustomPartitions) || _isBiState; - } + public override bool GetRequiresRootPartition() => !(_sourceDefinition.ByStreams || _sourceDefinition.ByCustomPartitions) || _isBiState; public override void EnrichStatistics(ProjectionStatistics info) { //TODO: get rid of this cast info.ResultStreamName = _sourceDefinition.ResultStreamNameOption; } - protected virtual ICoreProjectionCheckpointManager CreateCheckpointManager( - Guid projectionCorrelationId, IPublisher publisher, IODispatcher ioDispatcher, - ProjectionNamesBuilder namingBuilder, CoreProjectionCheckpointWriter coreProjectionCheckpointWriter, - bool definesFold, IReaderStrategy readerStrategy) { + private ICoreProjectionCheckpointManager CreateCheckpointManager( + Guid projectionCorrelationId, + IPublisher publisher, + IODispatcher ioDispatcher, + ProjectionNamesBuilder namingBuilder, + CoreProjectionCheckpointWriter coreProjectionCheckpointWriter, + IReaderStrategy readerStrategy) { var emitAny = _projectionConfig.EmitEventEnabled; //NOTE: not emitting one-time/transient projections are always handled by default checkpoint manager // as they don't depend on stable event order - if (emitAny && !readerStrategy.IsReadingOrderRepeatable) { - return new MultiStreamMultiOutputCheckpointManager( + return emitAny && !readerStrategy.IsReadingOrderRepeatable + ? new MultiStreamMultiOutputCheckpointManager( publisher, projectionCorrelationId, _projectionVersion, _projectionConfig.RunAs, ioDispatcher, - _projectionConfig, _name, readerStrategy.PositionTagger, namingBuilder, - _projectionConfig.CheckpointsEnabled, GetProducesRunningResults(), definesFold, - coreProjectionCheckpointWriter, _maxProjectionStateSize); - } else { - return new DefaultCheckpointManager( + _projectionConfig, readerStrategy.PositionTagger, namingBuilder, + _projectionConfig.CheckpointsEnabled, + coreProjectionCheckpointWriter, _maxProjectionStateSize) + : new DefaultCheckpointManager( publisher, projectionCorrelationId, _projectionVersion, _projectionConfig.RunAs, ioDispatcher, - _projectionConfig, _name, readerStrategy.PositionTagger, namingBuilder, - _projectionConfig.CheckpointsEnabled, GetProducesRunningResults(), definesFold, + _projectionConfig, readerStrategy.PositionTagger, namingBuilder, + _projectionConfig.CheckpointsEnabled, coreProjectionCheckpointWriter, _maxProjectionStateSize); - } } - protected virtual IResultWriter CreateFirstPhaseResultWriter( - IEmittedEventWriter emittedEventWriter, CheckpointTag zeroCheckpointTag, + protected IResultWriter CreateFirstPhaseResultWriter(IEmittedEventWriter emittedEventWriter, + CheckpointTag zeroCheckpointTag, ProjectionNamesBuilder namingBuilder) { return new ResultWriter( CreateFirstPhaseResultEmitter(namingBuilder), emittedEventWriter, GetProducesRunningResults(), diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IReaderStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IReaderStrategy.cs index c3a8afbf4cc..b1101715d0b 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IReaderStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IReaderStrategy.cs @@ -3,7 +3,6 @@ using System; using KurrentDB.Core.Bus; -using KurrentDB.Core.Helpers; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Projections.Core.Services.Processing.Subscriptions; @@ -18,7 +17,5 @@ IReaderSubscription CreateReaderSubscription( IPublisher publisher, CheckpointTag fromCheckpointTag, Guid subscriptionId, ReaderSubscriptionOptions readerSubscriptionOptions); - IEventReader CreatePausedEventReader( - Guid eventReaderId, IPublisher publisher, IODispatcher ioDispatcher, CheckpointTag checkpointTag, - bool stopOnEof, int? stopAfterNEvents); + IEventReader CreatePausedEventReader(Guid eventReaderId, IPublisher publisher, CheckpointTag checkpointTag, bool stopOnEof); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IResultWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IResultWriter.cs index 069bf1e31c6..a0b112c12d1 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IResultWriter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/IResultWriter.cs @@ -2,22 +2,14 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; -using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Projections.Core.Services.Processing.Emitting.EmittedEvents; namespace KurrentDB.Projections.Core.Services.Processing.Strategies; public interface IResultWriter { - //NOTE: subscriptionId should not be here. Reconsider how to pass it to follower projection result writer - void WriteEofResult( - Guid subscriptionId, string partition, string resultBody, CheckpointTag causedBy, Guid causedByGuid, - string correlationId); - void WriteRunningResult(EventProcessedResult result); void AccountPartition(EventProcessedResult result); void EventsEmitted(EmittedEventEnvelope[] scheduledWrites, Guid causedBy, string correlationId); - - void WriteProgress(Guid subscriptionId, float progress); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProcessingStrategySelector.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProcessingStrategySelector.cs index 0042432c6fd..4ef79f553d1 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProcessingStrategySelector.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProcessingStrategySelector.cs @@ -7,37 +7,27 @@ namespace KurrentDB.Projections.Core.Services.Processing.Strategies; -public class ProcessingStrategySelector { +public class ProcessingStrategySelector(ReaderSubscriptionDispatcher subscriptionDispatcher, int maxProjectionStateSize) { private readonly ILogger _logger = Log.ForContext(); - private readonly ReaderSubscriptionDispatcher _subscriptionDispatcher; - private readonly int _maxProjectionStateSize; - - public ProcessingStrategySelector( - ReaderSubscriptionDispatcher subscriptionDispatcher, int maxProjectionStateSize) { - _subscriptionDispatcher = subscriptionDispatcher; - _maxProjectionStateSize = maxProjectionStateSize; - } public ProjectionProcessingStrategy CreateProjectionProcessingStrategy( string name, ProjectionVersion projectionVersion, - ProjectionNamesBuilder namesBuilder, IQuerySources sourceDefinition, ProjectionConfig projectionConfig, - IProjectionStateHandler stateHandler, string handlerType, string query, bool enableContentTypeValidation) { - - return projectionConfig.StopOnEof - ? (ProjectionProcessingStrategy) - new QueryProcessingStrategy( + IProjectionStateHandler stateHandler, + bool enableContentTypeValidation) + => projectionConfig.StopOnEof + ? new QueryProcessingStrategy( name, projectionVersion, stateHandler, projectionConfig, sourceDefinition, _logger, - _subscriptionDispatcher, + subscriptionDispatcher, enableContentTypeValidation, - _maxProjectionStateSize) + maxProjectionStateSize) : new ContinuousProjectionProcessingStrategy( name, projectionVersion, @@ -45,8 +35,7 @@ public ProjectionProcessingStrategy CreateProjectionProcessingStrategy( projectionConfig, sourceDefinition, _logger, - _subscriptionDispatcher, + subscriptionDispatcher, enableContentTypeValidation, - _maxProjectionStateSize); - } + maxProjectionStateSize); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProjectionProcessingStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProjectionProcessingStrategy.cs index 66fd78bc6da..3f39f054360 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProjectionProcessingStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ProjectionProcessingStrategy.cs @@ -14,18 +14,15 @@ namespace KurrentDB.Projections.Core.Services.Processing.Strategies; -public abstract class ProjectionProcessingStrategy { - protected readonly string _name; - protected readonly ProjectionVersion _projectionVersion; - protected readonly ILogger _logger; - protected readonly int _maxProjectionStateSize; - - protected ProjectionProcessingStrategy(string name, ProjectionVersion projectionVersion, ILogger logger, int maxProjectionStateSize) { - _name = name; - _projectionVersion = projectionVersion; - _logger = logger; - _maxProjectionStateSize = maxProjectionStateSize; - } +public abstract class ProjectionProcessingStrategy( + string name, + ProjectionVersion projectionVersion, + ILogger logger, + int maxProjectionStateSize) { + protected readonly string _name = name; + protected readonly ProjectionVersion _projectionVersion = projectionVersion; + protected readonly ILogger _logger = logger; + protected readonly int _maxProjectionStateSize = maxProjectionStateSize; public CoreProjection Create( Guid projectionCorrelationId, @@ -34,17 +31,11 @@ public CoreProjection Create( ClaimsPrincipal runAs, IPublisher publisher, IODispatcher ioDispatcher, - ReaderSubscriptionDispatcher subscriptionDispatcher, ITimeProvider timeProvider) { - if (inputQueue == null) - throw new ArgumentNullException("inputQueue"); - //if (runAs == null) throw new ArgumentNullException("runAs"); - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (ioDispatcher == null) - throw new ArgumentNullException("ioDispatcher"); - if (timeProvider == null) - throw new ArgumentNullException("timeProvider"); + ArgumentNullException.ThrowIfNull(inputQueue); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(ioDispatcher); + ArgumentNullException.ThrowIfNull(timeProvider); var namingBuilder = new ProjectionNamesBuilder(_name, GetSourceDefinition()); @@ -66,7 +57,6 @@ public CoreProjection Create( runAs, publisher, ioDispatcher, - subscriptionDispatcher, _logger, namingBuilder, coreProjectionCheckpointWriter, diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/QueryProcessingStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/QueryProcessingStrategy.cs index 2f12a40ef33..558d10f616e 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/QueryProcessingStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/QueryProcessingStrategy.cs @@ -16,38 +16,40 @@ namespace KurrentDB.Projections.Core.Services.Processing.Strategies; public class QueryProcessingStrategy : DefaultProjectionProcessingStrategy { public QueryProcessingStrategy( - string name, ProjectionVersion projectionVersion, IProjectionStateHandler stateHandler, - ProjectionConfig projectionConfig, IQuerySources sourceDefinition, ILogger logger, - ReaderSubscriptionDispatcher subscriptionDispatcher, bool enableContentTypeValidation, int maxProjectionStateSize) + string name, + ProjectionVersion projectionVersion, + IProjectionStateHandler stateHandler, + ProjectionConfig projectionConfig, + IQuerySources sourceDefinition, + ILogger logger, + ReaderSubscriptionDispatcher subscriptionDispatcher, + bool enableContentTypeValidation, + int maxProjectionStateSize) : base( name, projectionVersion, stateHandler, projectionConfig, sourceDefinition, logger, subscriptionDispatcher, enableContentTypeValidation, maxProjectionStateSize) { } - public override bool GetStopOnEof() { - return true; - } + public override bool GetStopOnEof() => true; - public override bool GetUseCheckpoints() { - return false; - } + public override bool GetUseCheckpoints() => false; - public override bool GetProducesRunningResults() { - return !_sourceDefinition.DefinesFold; - } + public override bool GetProducesRunningResults() => !_sourceDefinition.DefinesFold; protected override IProjectionProcessingPhase[] CreateProjectionProcessingPhases( - IPublisher publisher, IPublisher inputQueue, Guid projectionCorrelationId, + IPublisher publisher, + Guid projectionCorrelationId, ProjectionNamesBuilder namingBuilder, - PartitionStateCache partitionStateCache, CoreProjection coreProjection, IODispatcher ioDispatcher, + PartitionStateCache partitionStateCache, + CoreProjection coreProjection, + IODispatcher ioDispatcher, IProjectionProcessingPhase firstPhase) { var coreProjectionCheckpointWriter = - new CoreProjectionCheckpointWriter( - namingBuilder.MakeCheckpointStreamName(), ioDispatcher, _projectionVersion, _name); + new CoreProjectionCheckpointWriter(namingBuilder.MakeCheckpointStreamName(), ioDispatcher, _projectionVersion, _name); var checkpointManager2 = new DefaultCheckpointManager( publisher, projectionCorrelationId, _projectionVersion, SystemAccounts.System, ioDispatcher, - _projectionConfig, _name, new PhasePositionTagger(1), namingBuilder, GetUseCheckpoints(), false, - _sourceDefinition.DefinesFold, coreProjectionCheckpointWriter, _maxProjectionStateSize); + _projectionConfig, new PhasePositionTagger(1), namingBuilder, GetUseCheckpoints(), coreProjectionCheckpointWriter, + _maxProjectionStateSize); IProjectionProcessingPhase writeResultsPhase; if (GetProducesRunningResults()) @@ -71,10 +73,9 @@ protected override IProjectionProcessingPhase[] CreateProjectionProcessingPhases checkpointManager2, firstPhase.EmittedStreamsTracker); - return new[] { firstPhase, writeResultsPhase }; + return [firstPhase, writeResultsPhase]; } - protected override IResultEventEmitter CreateFirstPhaseResultEmitter(ProjectionNamesBuilder namingBuilder) { - return new ResultEventEmitter(namingBuilder); - } + protected override IResultEventEmitter CreateFirstPhaseResultEmitter(ProjectionNamesBuilder namingBuilder) + => new ResultEventEmitter(namingBuilder); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ReaderStrategy.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ReaderStrategy.cs index fbef37d017c..f5dca3efc28 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ReaderStrategy.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ReaderStrategy.cs @@ -7,7 +7,6 @@ using System.Security.Claims; using KurrentDB.Core.Bus; using KurrentDB.Core.Data; -using KurrentDB.Core.Helpers; using KurrentDB.Core.Services.TimerService; using KurrentDB.Projections.Core.Messages; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; @@ -30,22 +29,11 @@ public class ReaderStrategy : IReaderStrategy { private readonly bool _reorderEvents; private readonly ClaimsPrincipal _runAs; private readonly int _processingLag; - - - private readonly EventFilter _eventFilter; - private readonly PositionTagger _positionTagger; private readonly ITimeProvider _timeProvider; - private readonly string _tag; private readonly int _phase; - public static IReaderStrategy Create( - string tag, - int phase, - IQuerySources sources, - ITimeProvider timeProvider, - bool stopOnEof, - ClaimsPrincipal runAs) { + public static IReaderStrategy Create(string tag, int phase, IQuerySources sources, ITimeProvider timeProvider, ClaimsPrincipal runAs) { if (!sources.AllStreams && !sources.HasCategories() && !sources.HasStreams()) throw new InvalidOperationException("None of streams and categories are included"); if (!sources.AllEvents && !sources.HasEvents()) @@ -112,42 +100,31 @@ private ReaderStrategy( _tag = tag; _phase = phase; _allStreams = allStreams; - _categories = categories != null && categories.Length > 0 ? new HashSet(categories) : null; - _streams = streams != null && streams.Length > 0 ? new HashSet(streams) : null; + _categories = categories is { Length: > 0 } ? [..categories] : null; + _streams = streams is { Length: > 0 } ? [..streams] : null; _allEvents = allEvents; _includeLinks = includeLinks; - _events = events != null && events.Length > 0 ? new HashSet(events) : null; + _events = events is { Length: > 0 } ? [..events] : null; _includeStreamDeletedNotification = includeStreamDeletedNotification; _processingLag = processingLag.GetValueOrDefault(); _reorderEvents = reorderEvents; _runAs = runAs; - - _eventFilter = CreateEventFilter(); - _positionTagger = CreatePositionTagger(); + EventFilter = CreateEventFilter(); + PositionTagger = CreatePositionTagger(); _timeProvider = timeProvider; } - public bool IsReadingOrderRepeatable { - get { return !(_streams != null && _streams.Count > 1); } - } + public bool IsReadingOrderRepeatable => _streams is not { Count: > 1 }; - public EventFilter EventFilter { - get { return _eventFilter; } - } + public EventFilter EventFilter { get; } - public PositionTagger PositionTagger { - get { return _positionTagger; } - } - - public int Phase { - get { return _phase; } - } + public PositionTagger PositionTagger { get; } public IReaderSubscription CreateReaderSubscription( IPublisher publisher, CheckpointTag fromCheckpointTag, Guid subscriptionId, ReaderSubscriptionOptions readerSubscriptionOptions) { - if (_reorderEvents) - return new EventReorderingReaderSubscription( + return _reorderEvents + ? new EventReorderingReaderSubscription( publisher, subscriptionId, fromCheckpointTag, @@ -159,11 +136,8 @@ public IReaderSubscription CreateReaderSubscription( _processingLag, readerSubscriptionOptions.StopOnEof, readerSubscriptionOptions.StopAfterNEvents, - readerSubscriptionOptions.EnableContentTypeValidation); - else - return new ReaderSubscription( - _tag, - publisher, + readerSubscriptionOptions.EnableContentTypeValidation) + : new ReaderSubscription(publisher, subscriptionId, fromCheckpointTag, this, @@ -177,40 +151,38 @@ public IReaderSubscription CreateReaderSubscription( } public IEventReader CreatePausedEventReader( - Guid eventReaderId, IPublisher publisher, IODispatcher ioDispatcher, CheckpointTag checkpointTag, - bool stopOnEof, int? stopAfterNEvents) { - if (_allStreams && _events != null && _events.Count >= 1) { - //IEnumerable streams = GetEventIndexStreams(); - return CreatePausedEventIndexEventReader( - eventReaderId, ioDispatcher, publisher, checkpointTag, stopOnEof, stopAfterNEvents, true, _events, - _includeStreamDeletedNotification); - } - - if (_allStreams) { - var eventReader = new TransactionFileEventReader(publisher, eventReaderId, _runAs, - new TFPos(checkpointTag.CommitPosition.Value, checkpointTag.PreparePosition.Value), _timeProvider, - deliverEndOfTFPosition: true, stopOnEof: stopOnEof, resolveLinkTos: false); - return eventReader; + Guid eventReaderId, IPublisher publisher, CheckpointTag checkpointTag, + bool stopOnEof) { + switch (_allStreams) { + case true when _events is { Count: >= 1 }: + //IEnumerable streams = GetEventIndexStreams(); + return CreatePausedEventIndexEventReader( + eventReaderId, publisher, checkpointTag, stopOnEof, true, _events, + _includeStreamDeletedNotification); + case true: { + var eventReader = new TransactionFileEventReader(publisher, eventReaderId, _runAs, + new TFPos(checkpointTag.CommitPosition.Value, checkpointTag.PreparePosition.Value), _timeProvider, + deliverEndOfTFPosition: true, stopOnEof: stopOnEof, resolveLinkTos: false); + return eventReader; + } } - if (_streams != null && _streams.Count == 1) { + if (_streams is { Count: 1 }) { var streamName = checkpointTag.Streams.Keys.First(); //TODO: handle if not the same return CreatePausedStreamEventReader( - eventReaderId, ioDispatcher, publisher, checkpointTag, streamName, stopOnEof, resolveLinkTos: true, - stopAfterNEvents: stopAfterNEvents, produceStreamDeletes: _includeStreamDeletedNotification); + eventReaderId, publisher, checkpointTag, streamName, stopOnEof, resolveLinkTos: true, produceStreamDeletes: _includeStreamDeletedNotification); } - if (_categories != null && _categories.Count == 1) { + if (_categories is { Count: 1 }) { var streamName = checkpointTag.Streams.Keys.First(); return CreatePausedStreamEventReader( - eventReaderId, ioDispatcher, publisher, checkpointTag, streamName, stopOnEof, resolveLinkTos: true, - stopAfterNEvents: stopAfterNEvents, produceStreamDeletes: _includeStreamDeletedNotification); + eventReaderId, publisher, checkpointTag, streamName, stopOnEof, resolveLinkTos: true, produceStreamDeletes: _includeStreamDeletedNotification); } - if (_streams != null && _streams.Count > 1) { + if (_streams is { Count: > 1 }) { return CreatePausedMultiStreamEventReader( - eventReaderId, ioDispatcher, publisher, checkpointTag, stopOnEof, stopAfterNEvents, true, _streams); + eventReaderId, publisher, checkpointTag, stopOnEof, true, _streams); } throw new NotSupportedException(); @@ -219,65 +191,69 @@ public IEventReader CreatePausedEventReader( //TODO: clean up $deleted event notification vs $streamDeleted event private EventFilter CreateEventFilter() { - if (_allStreams && _events != null && _events.Count >= 1) - return new EventByTypeIndexEventFilter(_events); - if (_allStreams) + switch (_allStreams) { + case true when _events is { Count: >= 1 }: + return new EventByTypeIndexEventFilter(_events); //NOTE: a projection cannot handle both stream deleted notifications // and real stream tombstone/stream deleted events as they have the same position // and thus processing cannot be correctly checkpointed - return new TransactionFileEventFilter( - _allEvents, !_includeStreamDeletedNotification, _events, includeLinks: _includeLinks); - if (_categories != null && _categories.Count == 1) + case true: + return new TransactionFileEventFilter( + _allEvents, !_includeStreamDeletedNotification, _events, includeLinks: _includeLinks); + } + + if (_categories is { Count: 1 }) return new CategoryEventFilter(_categories.First(), _allEvents, _events); if (_categories != null) throw new NotSupportedException(); - if (_streams != null && _streams.Count == 1) - return new StreamEventFilter(_streams.First(), _allEvents, _events); - if (_streams != null && _streams.Count > 1) - return new MultiStreamEventFilter(_streams, _allEvents, _events); - throw new NotSupportedException(); + return _streams switch { + { Count: 1 } => new StreamEventFilter(_streams.First(), _allEvents, _events), + { Count: > 1 } => new MultiStreamEventFilter(_streams, _allEvents, _events), + _ => throw new NotSupportedException() + }; } private PositionTagger CreatePositionTagger() { - if (_allStreams && _events != null && _events.Count >= 1) - return new EventByTypeIndexPositionTagger(_phase, _events.ToArray(), _includeStreamDeletedNotification); - if (_allStreams && _reorderEvents) - return new PreparePositionTagger(_phase); - if (_allStreams) - return new TransactionFilePositionTagger(_phase); - if (_categories != null && _categories.Count == 1) - //TODO: '-' is a hardcoded separator - return new StreamPositionTagger(_phase, "$ce-" + _categories.First()); + switch (_allStreams) { + case true when _events is { Count: >= 1 }: + return new EventByTypeIndexPositionTagger(_phase, _events.ToArray(), _includeStreamDeletedNotification); + case true when _reorderEvents: + return new PreparePositionTagger(_phase); + case true: + return new TransactionFilePositionTagger(_phase); + } + + if (_categories is { Count: 1 }) + return new StreamPositionTagger(_phase, $"$ce-{_categories.First()}"); if (_categories != null) throw new NotSupportedException(); - if (_streams != null && _streams.Count == 1) - return new StreamPositionTagger(_phase, _streams.First()); - if (_streams != null && _streams.Count > 1) - return new MultiStreamPositionTagger(_phase, _streams.ToArray()); + return _streams switch { + { Count: 1 } => new StreamPositionTagger(_phase, _streams.First()), + { Count: > 1 } => new MultiStreamPositionTagger(_phase, _streams.ToArray()), + _ => throw new NotSupportedException() + }; //TODO: consider passing projection phase from outside (above) - throw new NotSupportedException(); } private IEventReader CreatePausedStreamEventReader( - Guid eventReaderId, IODispatcher ioDispatcher, IPublisher publisher, CheckpointTag checkpointTag, - string streamName, bool stopOnEof, int? stopAfterNEvents, bool resolveLinkTos, bool produceStreamDeletes) { + Guid eventReaderId, IPublisher publisher, CheckpointTag checkpointTag, + string streamName, bool stopOnEof, bool resolveLinkTos, bool produceStreamDeletes) { var lastProcessedSequenceNumber = checkpointTag.Streams.Values.First(); var fromSequenceNumber = lastProcessedSequenceNumber + 1; - var eventReader = new StreamEventReader(publisher, eventReaderId, _runAs, streamName, fromSequenceNumber, - _timeProvider, + var eventReader = new StreamEventReader(publisher, eventReaderId, _runAs, streamName, fromSequenceNumber, _timeProvider, resolveLinkTos, produceStreamDeletes, stopOnEof); return eventReader; } private IEventReader CreatePausedEventIndexEventReader( - Guid eventReaderId, IODispatcher ioDispatcher, IPublisher publisher, CheckpointTag checkpointTag, - bool stopOnEof, int? stopAfterNEvents, bool resolveLinkTos, IEnumerable eventTypes, + Guid eventReaderId, IPublisher publisher, CheckpointTag checkpointTag, + bool stopOnEof, bool resolveLinkTos, IEnumerable eventTypes, bool includeStreamDeletedNotification) { //NOTE: just optimization - anyway if reading from TF events may reappear long p; var nextPositions = eventTypes.ToDictionary( - v => "$et-" + v, v => checkpointTag.Streams.TryGetValue(v, out p) ? p + 1 : 0); + v => $"$et-{v}", v => checkpointTag.Streams.TryGetValue(v, out p) ? p + 1 : 0); if (includeStreamDeletedNotification) nextPositions.Add("$et-$deleted", checkpointTag.Streams.TryGetValue("$deleted", out p) ? p + 1 : 0); @@ -288,12 +264,11 @@ private IEventReader CreatePausedEventIndexEventReader( } private IEventReader CreatePausedMultiStreamEventReader( - Guid eventReaderId, IODispatcher ioDispatcher, IPublisher publisher, CheckpointTag checkpointTag, - bool stopOnEof, int? stopAfterNEvents, bool resolveLinkTos, IEnumerable streams) { + Guid eventReaderId, IPublisher publisher, CheckpointTag checkpointTag, + bool stopOnEof, bool resolveLinkTos, IEnumerable streams) { var nextPositions = checkpointTag.Streams.ToDictionary(v => v.Key, v => v.Value + 1); - return new MultiStreamEventReader( - ioDispatcher, publisher, eventReaderId, _runAs, Phase, streams.ToArray(), nextPositions, resolveLinkTos, - _timeProvider, stopOnEof, stopAfterNEvents); + return new MultiStreamEventReader(publisher, eventReaderId, _runAs, _phase, streams.ToArray(), nextPositions, resolveLinkTos, + _timeProvider, stopOnEof); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ResultWriter.cs b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ResultWriter.cs index 253b7e670b0..088ebfa0d4a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ResultWriter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Strategies/ResultWriter.cs @@ -8,39 +8,26 @@ namespace KurrentDB.Projections.Core.Services.Processing.Strategies; -public class ResultWriter : IResultWriter { - private readonly IResultEventEmitter _resultEventEmitter; - private readonly IEmittedEventWriter _coreProjectionCheckpointManager; - private readonly bool _producesRunningResults; - private readonly CheckpointTag _zeroCheckpointTag; - private readonly string _partitionCatalogStreamName; - - public ResultWriter( - IResultEventEmitter resultEventEmitter, IEmittedEventWriter coreProjectionCheckpointManager, - bool producesRunningResults, CheckpointTag zeroCheckpointTag, string partitionCatalogStreamName) { - _resultEventEmitter = resultEventEmitter; - _coreProjectionCheckpointManager = coreProjectionCheckpointManager; - _producesRunningResults = producesRunningResults; - _zeroCheckpointTag = zeroCheckpointTag; - _partitionCatalogStreamName = partitionCatalogStreamName; - } - - public void WriteEofResult( - Guid subscriptionId, string partition, string resultBody, CheckpointTag causedBy, Guid causedByGuid, - string correlationId) { - if (resultBody != null) - WriteResult(partition, resultBody, causedBy, causedByGuid, correlationId); - } - +public class ResultWriter( + IResultEventEmitter resultEventEmitter, + IEmittedEventWriter coreProjectionCheckpointManager, + bool producesRunningResults, + CheckpointTag zeroCheckpointTag, + string partitionCatalogStreamName) + : IResultWriter { private void WriteResult( - string partition, string resultBody, CheckpointTag causedBy, Guid causedByGuid, string correlationId) { + string partition, + string resultBody, + CheckpointTag causedBy, + Guid causedByGuid, + string correlationId) { var resultEvents = ResultUpdated(partition, resultBody, causedBy); if (resultEvents != null) - _coreProjectionCheckpointManager.EventsEmitted(resultEvents, causedByGuid, correlationId); + coreProjectionCheckpointManager.EventsEmitted(resultEvents, causedByGuid, correlationId); } public void WriteRunningResult(EventProcessedResult result) { - if (!_producesRunningResults) + if (!producesRunningResults) return; var oldState = result.OldState; var newState = result.NewState; @@ -48,41 +35,28 @@ public void WriteRunningResult(EventProcessedResult result) { if (oldState.Result != resultBody) { var partition = result.Partition; var causedBy = newState.CausedBy; - WriteResult( - partition, resultBody, causedBy, result.CausedBy, result.CorrelationId); + WriteResult(partition, resultBody, causedBy, result.CausedBy, result.CorrelationId); } } - private EmittedEventEnvelope[] ResultUpdated(string partition, string result, CheckpointTag causedBy) { - return _resultEventEmitter.ResultUpdated(partition, result, causedBy); - } + private EmittedEventEnvelope[] ResultUpdated(string partition, string result, CheckpointTag causedBy) + => resultEventEmitter.ResultUpdated(partition, result, causedBy); - protected EmittedEventEnvelope[] RegisterNewPartition(string partition, CheckpointTag at) { - return new[] { - new EmittedEventEnvelope( - new EmittedDataEvent( - _partitionCatalogStreamName, Guid.NewGuid(), "$partition", false, partition, - null, at, null)) - }; - } + private EmittedEventEnvelope[] RegisterNewPartition(string partition, CheckpointTag at) => [ + new(new EmittedDataEvent(partitionCatalogStreamName, Guid.NewGuid(), "$partition", false, partition, null, at, null)) + ]; public void AccountPartition(EventProcessedResult result) { - if (_producesRunningResults) - if (result.Partition != "" && result.OldState.CausedBy == _zeroCheckpointTag) { - var resultEvents = RegisterNewPartition(result.Partition, result.CheckpointTag); - if (resultEvents != null) - _coreProjectionCheckpointManager.EventsEmitted( - resultEvents, Guid.Empty, correlationId: null); - } - } + if (!producesRunningResults) return; - public void EventsEmitted( - EmittedEventEnvelope[] scheduledWrites, Guid causedBy, string correlationId) { - _coreProjectionCheckpointManager.EventsEmitted( - scheduledWrites, causedBy, correlationId); + if (result.Partition != "" && result.OldState.CausedBy == zeroCheckpointTag) { + var resultEvents = RegisterNewPartition(result.Partition, result.CheckpointTag); + if (resultEvents != null) + coreProjectionCheckpointManager.EventsEmitted(resultEvents, Guid.Empty, correlationId: null); + } } - public void WriteProgress(Guid subscriptionId, float progress) { - // intentionally does nothing + public void EventsEmitted(EmittedEventEnvelope[] scheduledWrites, Guid causedBy, string correlationId) { + coreProjectionCheckpointManager.EventsEmitted(scheduledWrites, causedBy, correlationId); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/EventReorderingReaderSubscription.cs b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/EventReorderingReaderSubscription.cs index 025b2aeb8b7..8a8d8744a4d 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/EventReorderingReaderSubscription.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/EventReorderingReaderSubscription.cs @@ -12,29 +12,22 @@ namespace KurrentDB.Projections.Core.Services.Processing.Subscriptions; -public class EventReorderingReaderSubscription : ReaderSubscriptionBase, IReaderSubscription { - private readonly SortedList _buffer = - new SortedList(); - - private readonly int _processingLagMs; - - public EventReorderingReaderSubscription( - IPublisher publisher, - Guid subscriptionId, - CheckpointTag @from, - IReaderStrategy readerStrategy, - ITimeProvider timeProvider, - long? checkpointUnhandledBytesThreshold, - int? checkpointProcessedEventsThreshold, - int checkpointAfterMs, - int processingLagMs, - bool stopOnEof, - int? stopAfterNEvents, - bool enableContentTypeValidation) - : base( - publisher, +public class EventReorderingReaderSubscription( + IPublisher publisher, + Guid subscriptionId, + CheckpointTag from, + IReaderStrategy readerStrategy, + ITimeProvider timeProvider, + long? checkpointUnhandledBytesThreshold, + int? checkpointProcessedEventsThreshold, + int checkpointAfterMs, + int processingLagMs, + bool stopOnEof, + int? stopAfterNEvents, + bool enableContentTypeValidation) + : ReaderSubscriptionBase(publisher, subscriptionId, - @from, + from, readerStrategy, timeProvider, checkpointUnhandledBytesThreshold, @@ -42,9 +35,9 @@ public EventReorderingReaderSubscription( checkpointAfterMs, stopOnEof, stopAfterNEvents, - enableContentTypeValidation) { - _processingLagMs = processingLagMs; - } + enableContentTypeValidation), + IReaderSubscription { + private readonly SortedList _buffer = new(); public void Handle(ReaderSubscriptionMessage.CommittedEventDistributed message) { if (message.Data == null) @@ -70,7 +63,7 @@ private bool ProcessFor(DateTime maxTimestamp) { if (_buffer.Count == 0) return false; var first = _buffer.ElementAt(0); - if ((maxTimestamp - first.Value.Data.Timestamp).TotalMilliseconds > _processingLagMs) { + if ((maxTimestamp - first.Value.Data.Timestamp).TotalMilliseconds > processingLagMs) { _buffer.RemoveAt(0); ProcessOne(first.Value); return true; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/IReaderSubscription.cs b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/IReaderSubscription.cs index 2ccd0cdcd5a..a3a7154c73d 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/IReaderSubscription.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/IReaderSubscription.cs @@ -3,7 +3,6 @@ using System; using KurrentDB.Core.Bus; -using KurrentDB.Core.Helpers; using KurrentDB.Projections.Core.Messages; namespace KurrentDB.Projections.Core.Services.Processing.Subscriptions; @@ -16,5 +15,5 @@ public interface IReaderSubscription : IHandle, IHandle { Guid SubscriptionId { get; } - IEventReader CreatePausedEventReader(IPublisher publisher, IODispatcher ioDispatcher, Guid forkedEventReaderId); + IEventReader CreatePausedEventReader(IPublisher publisher, Guid forkedEventReaderId); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscription.cs b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscription.cs index 3e5ec89ae3a..89f39add304 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscription.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscription.cs @@ -10,24 +10,21 @@ namespace KurrentDB.Projections.Core.Services.Processing.Subscriptions; -public class ReaderSubscription : ReaderSubscriptionBase, IReaderSubscription { - public ReaderSubscription( - string tag, - IPublisher publisher, - Guid subscriptionId, - CheckpointTag @from, - IReaderStrategy readerStrategy, - ITimeProvider timeProvider, - long? checkpointUnhandledBytesThreshold, - int? checkpointProcessedEventsThreshold, - int checkpointAfterMs, - bool stopOnEof, - int? stopAfterNEvents, - bool enableContentTypeValidation) - : base( - publisher, +public class ReaderSubscription( + IPublisher publisher, + Guid subscriptionId, + CheckpointTag from, + IReaderStrategy readerStrategy, + ITimeProvider timeProvider, + long? checkpointUnhandledBytesThreshold, + int? checkpointProcessedEventsThreshold, + int checkpointAfterMs, + bool stopOnEof, + int? stopAfterNEvents, + bool enableContentTypeValidation) + : ReaderSubscriptionBase(publisher, subscriptionId, - @from, + from, readerStrategy, timeProvider, checkpointUnhandledBytesThreshold, @@ -35,10 +32,8 @@ public ReaderSubscription( checkpointAfterMs, stopOnEof, stopAfterNEvents, - enableContentTypeValidation) { - _tag = tag; - } - + enableContentTypeValidation), + IReaderSubscription { public void Handle(ReaderSubscriptionMessage.CommittedEventDistributed message) { ProcessOne(message); } @@ -48,7 +43,7 @@ public void Handle(ReaderSubscriptionMessage.EventReaderIdle message) { } public void Handle(ReaderSubscriptionMessage.EventReaderPartitionDeleted message) { - if (!base._eventFilter.PassesDeleteNotification(message.PositionStreamId)) + if (!_eventFilter.PassesDeleteNotification(message.PositionStreamId)) return; var deletePosition = _positionTagger.MakeCheckpointTag(_positionTracker.LastTag, message); PublishPartitionDeleted(message.Partition, deletePosition); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionBase.cs b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionBase.cs index 21a7c960a16..3bd33722053 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionBase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionBase.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using KurrentDB.Core.Bus; -using KurrentDB.Core.Helpers; using KurrentDB.Core.Services.TimerService; using KurrentDB.Projections.Core.Messages; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; @@ -28,19 +27,17 @@ public class ReaderSubscriptionBase { private float _progress = -1; private long _subscriptionMessageSequenceNumber; private int _eventsSinceLastCheckpointSuggestedOrStart; - private readonly Guid _subscriptionId; private bool _eofReached; - protected string _tag; - private TimeSpan _checkpointAfter; + private readonly TimeSpan _checkpointAfter; private DateTime _lastCheckpointTime = DateTime.MinValue; - private bool _enableContentTypeValidation; - private ILogger _logger; + private readonly bool _enableContentTypeValidation; + private readonly ILogger _logger; private CheckpointTag _lastCheckpointTag; protected ReaderSubscriptionBase( IPublisher publisher, Guid subscriptionId, - CheckpointTag @from, + CheckpointTag from, IReaderStrategy readerStrategy, ITimeProvider timeProvider, long? checkpointUnhandledBytesThreshold, @@ -49,12 +46,9 @@ protected ReaderSubscriptionBase( bool stopOnEof, int? stopAfterNEvents, bool enableContentTypeValidation) { - if (publisher == null) - throw new ArgumentNullException("publisher"); - if (readerStrategy == null) - throw new ArgumentNullException("readerStrategy"); - if (timeProvider == null) - throw new ArgumentNullException("timeProvider"); + ArgumentNullException.ThrowIfNull(publisher); + ArgumentNullException.ThrowIfNull(readerStrategy); + ArgumentNullException.ThrowIfNull(timeProvider); if (checkpointProcessedEventsThreshold > 0 && stopAfterNEvents > 0) throw new ArgumentException("checkpointProcessedEventsThreshold > 0 && stopAfterNEvents > 0"); @@ -66,22 +60,18 @@ protected ReaderSubscriptionBase( _checkpointAfter = TimeSpan.FromMilliseconds(checkpointAfterMs); _stopOnEof = stopOnEof; _stopAfterNEvents = stopAfterNEvents; - _subscriptionId = subscriptionId; + SubscriptionId = subscriptionId; _lastPassedOrCheckpointedEventPosition = null; - _eventFilter = readerStrategy.EventFilter; - _positionTagger = readerStrategy.PositionTagger; - _positionTracker = new PositionTracker(_positionTagger); - _positionTracker.UpdateByCheckpointTagInitial(@from); + _positionTracker = new(_positionTagger); + _positionTracker.UpdateByCheckpointTagInitial(from); _lastCheckpointTag = _positionTracker.LastTag; _enableContentTypeValidation = enableContentTypeValidation; _logger = Log.ForContext(); } - public Guid SubscriptionId { - get { return _subscriptionId; } - } + public Guid SubscriptionId { get; } protected void ProcessOne(ReaderSubscriptionMessage.CommittedEventDistributed message) { if (_eofReached) @@ -92,11 +82,14 @@ protected void ProcessOne(ReaderSubscriptionMessage.CommittedEventDistributed me var roundedProgress = (float)Math.Round(message.Progress, 1); bool progressChanged = _progress != roundedProgress; - bool passesStreamSourceFilter = _eventFilter.PassesSource(message.Data.ResolvedLinkTo, message.Data.PositionStreamId, message.Data.EventType); - bool passesEventFilter = _eventFilter.Passes(message.Data.ResolvedLinkTo, message.Data.PositionStreamId, message.Data.EventType, message.Data.IsStreamDeletedEvent); - bool isValid = !_enableContentTypeValidation || _eventFilter.PassesValidation(message.Data.IsJson, message.Data.DataMemory); + bool passesStreamSourceFilter = + _eventFilter.PassesSource(message.Data.ResolvedLinkTo, message.Data.PositionStreamId, message.Data.EventType); + bool passesEventFilter = _eventFilter.Passes(message.Data.ResolvedLinkTo, message.Data.PositionStreamId, message.Data.EventType, + message.Data.IsStreamDeletedEvent); + bool isValid = !_enableContentTypeValidation || EventFilter.PassesValidation(message.Data.IsJson, message.Data.DataMemory); if (!isValid) { - _logger.Verbose($"Event {message.Data.EventSequenceNumber}@{message.Data.EventStreamId} is not valid json. Data: ({message.Data.Data})"); + _logger.Verbose( + $"Event {message.Data.EventSequenceNumber}@{message.Data.EventStreamId} is not valid json. Data: ({message.Data.Data})"); } CheckpointTag eventCheckpointTag = null; @@ -126,31 +119,30 @@ protected void ProcessOne(ReaderSubscriptionMessage.CommittedEventDistributed me var convertedMessage = EventReaderSubscriptionMessage.CommittedEventReceived.FromCommittedEventDistributed( message, eventCheckpointTag, _eventFilter.GetCategory(message.Data.PositionStreamId), - _subscriptionId, _subscriptionMessageSequenceNumber++); + SubscriptionId, _subscriptionMessageSequenceNumber++); _publisher.Publish(convertedMessage); _eventsSinceLastCheckpointSuggestedOrStart++; if (_checkpointProcessedEventsThreshold > 0 - && timeDifference > _checkpointAfter - && _eventsSinceLastCheckpointSuggestedOrStart >= _checkpointProcessedEventsThreshold - && _lastCheckpointTag != _positionTracker.LastTag) + && timeDifference > _checkpointAfter + && _eventsSinceLastCheckpointSuggestedOrStart >= _checkpointProcessedEventsThreshold + && _lastCheckpointTag != _positionTracker.LastTag) SuggestCheckpoint(message); if (_stopAfterNEvents > 0 && _eventsSinceLastCheckpointSuggestedOrStart >= _stopAfterNEvents) NEventsReached(); } else { if (_checkpointUnhandledBytesThreshold > 0 - && timeDifference > _checkpointAfter - && (_lastPassedOrCheckpointedEventPosition != null - && message.Data.Position.PreparePosition - _lastPassedOrCheckpointedEventPosition.Value - > _checkpointUnhandledBytesThreshold) - && _lastCheckpointTag != _positionTracker.LastTag) + && timeDifference > _checkpointAfter + && (_lastPassedOrCheckpointedEventPosition != null + && message.Data.Position.PreparePosition - _lastPassedOrCheckpointedEventPosition.Value + > _checkpointUnhandledBytesThreshold) + && _lastCheckpointTag != _positionTracker.LastTag) SuggestCheckpoint(message); else if (progressChanged) _progress = roundedProgress; } // initialize checkpointing based on first message - if (_lastPassedOrCheckpointedEventPosition == null) - _lastPassedOrCheckpointedEventPosition = message.Data.Position.PreparePosition; + _lastPassedOrCheckpointedEventPosition ??= message.Data.Position.PreparePosition; } private void NEventsReached() { @@ -159,7 +151,7 @@ private void NEventsReached() { protected void NotifyProgress() { _publisher.Publish(new EventReaderSubscriptionMessage.ProgressChanged( - _subscriptionId, + SubscriptionId, _positionTracker.LastTag, _progress, _subscriptionMessageSequenceNumber++)); @@ -176,13 +168,13 @@ protected void ForceProgressValue(float value) { protected void PublishPartitionDeleted(string partition, CheckpointTag deletePosition) { _publisher.Publish( new EventReaderSubscriptionMessage.PartitionDeleted( - _subscriptionId, deletePosition, partition, _subscriptionMessageSequenceNumber++)); + SubscriptionId, deletePosition, partition, _subscriptionMessageSequenceNumber++)); } private void PublishStartingAt(long startingLastCommitPosition) { _publisher.Publish( new EventReaderSubscriptionMessage.SubscriptionStarted( - _subscriptionId, _positionTracker.LastTag, startingLastCommitPosition, + SubscriptionId, _positionTracker.LastTag, startingLastCommitPosition, _subscriptionMessageSequenceNumber++)); } @@ -191,20 +183,16 @@ private void SuggestCheckpoint(ReaderSubscriptionMessage.CommittedEventDistribut _lastCheckpointTag = _positionTracker.LastTag; _publisher.Publish( new EventReaderSubscriptionMessage.CheckpointSuggested( - _subscriptionId, _positionTracker.LastTag, message.Progress, + SubscriptionId, _positionTracker.LastTag, message.Progress, _subscriptionMessageSequenceNumber++)); _eventsSinceLastCheckpointSuggestedOrStart = 0; _lastCheckpointTime = _timeProvider.UtcNow; } - public IEventReader CreatePausedEventReader(IPublisher publisher, IODispatcher ioDispatcher, - Guid eventReaderId) { - if (_eofReached) - throw new InvalidOperationException("Onetime projection has already reached the eof position"); - // _logger.Trace("Creating an event distribution point at '{lastTag}'", _positionTracker.LastTag); - return _readerStrategy.CreatePausedEventReader( - eventReaderId, publisher, ioDispatcher, _positionTracker.LastTag, _stopOnEof, _stopAfterNEvents); - } + public IEventReader CreatePausedEventReader(IPublisher publisher, Guid eventReaderId) + => _eofReached + ? throw new InvalidOperationException("Onetime projection has already reached the eof position") + : _readerStrategy.CreatePausedEventReader(eventReaderId, publisher, _positionTracker.LastTag, _stopOnEof); public void Handle(ReaderSubscriptionMessage.EventReaderEof message) { if (_eofReached) @@ -219,11 +207,11 @@ private void ProcessEofAndEmitEof() { EofReached(); _publisher.Publish( new EventReaderSubscriptionMessage.EofReached( - _subscriptionId, + SubscriptionId, _positionTracker.LastTag, _subscriptionMessageSequenceNumber++)); // self unsubscribe - _publisher.Publish(new ReaderSubscriptionManagement.Unsubscribe(_subscriptionId)); + _publisher.Publish(new ReaderSubscriptionManagement.Unsubscribe(SubscriptionId)); } public void Handle(ReaderSubscriptionMessage.EventReaderNotAuthorized message) { @@ -233,9 +221,10 @@ public void Handle(ReaderSubscriptionMessage.EventReaderNotAuthorized message) { if (_stopOnEof) { _eofReached = true; } + _publisher.Publish( new EventReaderSubscriptionMessage.NotAuthorized( - _subscriptionId, _positionTracker.LastTag, _progress, _subscriptionMessageSequenceNumber++)); + SubscriptionId, _positionTracker.LastTag, _progress, _subscriptionMessageSequenceNumber++)); } public void Handle(ReaderSubscriptionMessage.EventReaderStarting message) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionOptions.cs b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionOptions.cs index e04994359e2..93a69ae4fb9 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionOptions.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/Subscriptions/ReaderSubscriptionOptions.cs @@ -3,46 +3,22 @@ namespace KurrentDB.Projections.Core.Services.Processing.Subscriptions; -public class ReaderSubscriptionOptions { - private readonly long _checkpointUnhandledBytesThreshold; - private readonly int? _checkpointProcessedEventsThreshold; - private readonly int _checkpointAfterMs; - private readonly bool _stopOnEof; - private readonly int? _stopAfterNEvents; - private readonly bool _enableContentTypeValidation; +public class ReaderSubscriptionOptions( + long checkpointUnhandledBytesThreshold, + int? checkpointProcessedEventsThreshold, + int checkpointAfterMs, + bool stopOnEof, + int? stopAfterNEvents, + bool enableContentTypeValidation) { + public long CheckpointUnhandledBytesThreshold { get; } = checkpointUnhandledBytesThreshold; - public ReaderSubscriptionOptions( - long checkpointUnhandledBytesThreshold, int? checkpointProcessedEventsThreshold, int checkpointAfterMs, - bool stopOnEof, int? stopAfterNEvents, bool enableContentTypeValidation) { - _checkpointUnhandledBytesThreshold = checkpointUnhandledBytesThreshold; - _checkpointProcessedEventsThreshold = checkpointProcessedEventsThreshold; - _checkpointAfterMs = checkpointAfterMs; - _stopOnEof = stopOnEof; - _stopAfterNEvents = stopAfterNEvents; - _enableContentTypeValidation = enableContentTypeValidation; - } + public int? CheckpointProcessedEventsThreshold { get; } = checkpointProcessedEventsThreshold; - public long CheckpointUnhandledBytesThreshold { - get { return _checkpointUnhandledBytesThreshold; } - } + public int CheckpointAfterMs { get; } = checkpointAfterMs; - public int? CheckpointProcessedEventsThreshold { - get { return _checkpointProcessedEventsThreshold; } - } + public bool StopOnEof { get; } = stopOnEof; - public int CheckpointAfterMs { - get { return _checkpointAfterMs; } - } + public int? StopAfterNEvents { get; } = stopAfterNEvents; - public bool StopOnEof { - get { return _stopOnEof; } - } - - public int? StopAfterNEvents { - get { return _stopAfterNEvents; } - } - - public bool EnableContentTypeValidation { - get { return _enableContentTypeValidation; } - } + public bool EnableContentTypeValidation { get; } = enableContentTypeValidation; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TaggedResolvedEvent.cs b/src/KurrentDB.Projections.Core/Services/Processing/TaggedResolvedEvent.cs index d261d4ff4f5..89e5efb265c 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TaggedResolvedEvent.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TaggedResolvedEvent.cs @@ -5,12 +5,7 @@ namespace KurrentDB.Projections.Core.Services.Processing; -public sealed class TaggedResolvedEvent { - public readonly ResolvedEvent ResolvedEvent; - public readonly CheckpointTag ReaderPosition; - - public TaggedResolvedEvent(ResolvedEvent resolvedEvent, CheckpointTag readerPosition) { - ResolvedEvent = resolvedEvent; - ReaderPosition = readerPosition; - } +public sealed class TaggedResolvedEvent(ResolvedEvent resolvedEvent, CheckpointTag readerPosition) { + public readonly ResolvedEvent ResolvedEvent = resolvedEvent; + public readonly CheckpointTag ReaderPosition = readerPosition; } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.CommittedEventItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.CommittedEventItem.cs index 2e1cd40f133..82e38d091fa 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.CommittedEventItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.CommittedEventItem.cs @@ -7,24 +7,15 @@ namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; public partial class HeadingEventReader { - private class CommittedEventItem : Item { - public readonly ReaderSubscriptionMessage.CommittedEventDistributed Message; - - public CommittedEventItem(ReaderSubscriptionMessage.CommittedEventDistributed message) - : base(message.Data.Position) { - Message = message; - } + private class CommittedEventItem(ReaderSubscriptionMessage.CommittedEventDistributed message) : Item(message.Data.Position) { + public readonly ReaderSubscriptionMessage.CommittedEventDistributed Message = message; public override void Handle(IReaderSubscription subscription) { subscription.Handle(Message); } public override string ToString() { - return string.Format( - "{0} : {2}@{1}", - Message.Data.EventType, - Message.Data.PositionStreamId, - Message.Data.PositionSequenceNumber); + return $"{Message.Data.EventType} : {Message.Data.PositionSequenceNumber}@{Message.Data.PositionStreamId}"; } } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.Item.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.Item.cs index dfd9fecdb4d..77b6b0d4de6 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.Item.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.Item.cs @@ -7,12 +7,8 @@ namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; public partial class HeadingEventReader { - private abstract class Item { - public readonly TFPos Position; - - protected Item(TFPos position) { - Position = position; - } + private abstract class Item(TFPos position) { + public readonly TFPos Position = position; public abstract void Handle(IReaderSubscription subscription); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.PartitionDeletedItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.PartitionDeletedItem.cs index dd4c86f1a10..98fd7119c27 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.PartitionDeletedItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.PartitionDeletedItem.cs @@ -7,16 +7,10 @@ namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; public partial class HeadingEventReader { - private class PartitionDeletedItem : Item { - public readonly ReaderSubscriptionMessage.EventReaderPartitionDeleted Message; - - public PartitionDeletedItem(ReaderSubscriptionMessage.EventReaderPartitionDeleted message) - : base(message.DeleteLinkOrEventPosition.Value) { - Message = message; - } - + private class PartitionDeletedItem(ReaderSubscriptionMessage.EventReaderPartitionDeleted message) + : Item(message.DeleteLinkOrEventPosition.Value) { public override void Handle(IReaderSubscription subscription) { - subscription.Handle(Message); + subscription.Handle(message); } } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.cs index 080617128b9..539cf32aa9a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/HeadingEventReader.cs @@ -10,31 +10,19 @@ namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; -public partial class HeadingEventReader { +public partial class HeadingEventReader(int eventCacheSize, IPublisher publisher) { private IEventReader _headEventReader; private TFPos _subscribeFromPosition = new TFPos(long.MaxValue, long.MaxValue); - private readonly Queue _lastMessages = new Queue(); - - private readonly int _eventCacheSize; - - private readonly Dictionary _headSubscribers = - new Dictionary(); + private readonly Queue _lastMessages = new(); + private readonly Dictionary _headSubscribers = new(); private Guid _eventReaderId; - private bool _started; private TFPos _lastEventPosition = new TFPos(0, -1); private TFPos _lastDeletePosition = new TFPos(0, -1); - private IPublisher _publisher; - - public HeadingEventReader(int eventCacheSize, IPublisher publisher) { - _eventCacheSize = eventCacheSize; - _publisher = publisher; - } - public bool Handle(ReaderSubscriptionMessage.CommittedEventDistributed message) { EnsureStarted(); if (message.CorrelationId != _eventReaderId) @@ -55,8 +43,6 @@ public bool Handle(ReaderSubscriptionMessage.EventReaderPartitionDeleted message return false; ValidateEventOrder(message); - - CacheRecentMessage(message); DistributeMessage(message); return true; @@ -73,9 +59,7 @@ public bool Handle(ReaderSubscriptionMessage.EventReaderIdle message) { private void ValidateEventOrder(ReaderSubscriptionMessage.CommittedEventDistributed message) { if (_lastEventPosition >= message.Data.Position || _lastDeletePosition > message.Data.Position) throw new InvalidOperationException( - string.Format( - "Invalid committed event order. Last: '{0}' Received: '{1}' LastDelete: '{2}'", - _lastEventPosition, message.Data.Position, _lastEventPosition)); + $"Invalid committed event order. Last: '{_lastEventPosition}' Received: '{message.Data.Position}' LastDelete: '{_lastEventPosition}'"); _lastEventPosition = message.Data.Position; } @@ -83,9 +67,7 @@ private void ValidateEventOrder(ReaderSubscriptionMessage.EventReaderPartitionDe if (_lastEventPosition > message.DeleteLinkOrEventPosition.Value || _lastDeletePosition >= message.DeleteLinkOrEventPosition.Value) throw new InvalidOperationException( - string.Format( - "Invalid partition deleted event order. Last: '{0}' Received: '{1}' LastDelete: '{2}'", - _lastEventPosition, message.DeleteLinkOrEventPosition.Value, _lastEventPosition)); + $"Invalid partition deleted event order. Last: '{_lastEventPosition}' Received: '{message.DeleteLinkOrEventPosition.Value}' LastDelete: '{_lastEventPosition}'"); _lastDeletePosition = message.DeleteLinkOrEventPosition.Value; } @@ -107,12 +89,10 @@ public void Stop() { _started = false; } - public bool TrySubscribe( - Guid projectionId, IReaderSubscription readerSubscription, long fromTransactionFilePosition) { + public bool TrySubscribe(Guid projectionId, IReaderSubscription readerSubscription, long fromTransactionFilePosition) { EnsureStarted(); if (_headSubscribers.ContainsKey(projectionId)) - throw new InvalidOperationException( - string.Format("Projection '{0}' has been already subscribed", projectionId)); + throw new InvalidOperationException($"Projection '{projectionId}' has been already subscribed"); if (_subscribeFromPosition.CommitPosition <= fromTransactionFilePosition) { if (!DispatchRecentMessagesTo(readerSubscription, fromTransactionFilePosition)) { return false; @@ -127,10 +107,8 @@ public bool TrySubscribe( public void Unsubscribe(Guid projectionId) { EnsureStarted(); - if (!_headSubscribers.ContainsKey(projectionId)) - throw new InvalidOperationException( - string.Format("Projection '{0}' has not been subscribed", projectionId)); - _headSubscribers.Remove(projectionId); + if (!_headSubscribers.Remove(projectionId)) + throw new InvalidOperationException($"Projection '{projectionId}' has not been subscribed"); } private bool DispatchRecentMessagesTo(IReaderSubscription subscription, long fromTransactionFilePosition) { @@ -139,21 +117,11 @@ private bool DispatchRecentMessagesTo(IReaderSubscription subscription, long fro try { m.Handle(subscription); } catch (Exception ex) { - var item = m as CommittedEventItem; - string message; - if (item != null) { - message = string.Format( - "The heading subscription failed to handle a recently cached event {0}:{1}@{2} because {3}", - item.Message.Data.EventStreamId, item.Message.Data.EventType, - item.Message.Data.PositionSequenceNumber, ex.Message); - } else { - message = string.Format( - "The heading subscription failed to handle a recently cached deleted event at position {0} because {1}", - m.Position, ex.Message); - } - - _publisher.Publish( - new EventReaderSubscriptionMessage.Failed(subscription.SubscriptionId, message)); + var message = m is CommittedEventItem item + ? $"The heading subscription failed to handle a recently cached event {item.Message.Data.EventStreamId}:{item.Message.Data.EventType}@{item.Message.Data.PositionSequenceNumber} because {ex.Message}" + : $"The heading subscription failed to handle a recently cached deleted event at position {m.Position} because {ex.Message}"; + + publisher.Publish(new EventReaderSubscriptionMessage.Failed(subscription.SubscriptionId, message)); return false; } } @@ -167,10 +135,8 @@ private void DistributeMessage(ReaderSubscriptionMessage.CommittedEventDistribut try { subscriber.Handle(message); } catch (Exception ex) { - _publisher.Publish(new EventReaderSubscriptionMessage.Failed(subscriber.SubscriptionId, - string.Format("The heading subscription failed to handle an event {0}:{1}@{2} because {3}", - message.Data.EventStreamId, message.Data.EventType, message.Data.PositionSequenceNumber, - ex.Message))); + publisher.Publish(new EventReaderSubscriptionMessage.Failed(subscriber.SubscriptionId, + $"The heading subscription failed to handle an event {message.Data.EventStreamId}:{message.Data.EventType}@{message.Data.PositionSequenceNumber} because {ex.Message}")); } } } @@ -196,7 +162,7 @@ private void CacheRecentMessage(ReaderSubscriptionMessage.EventReaderPartitionDe } private void CleanUpCache() { - if (_lastMessages.Count > _eventCacheSize) { + if (_lastMessages.Count > eventCacheSize) { var removed = _lastMessages.Dequeue(); // as we may have multiple items at the same position it is important to // remove them together as we may subscribe in the middle otherwise diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/PreparePositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/PreparePositionTagger.cs index 1dac884cca8..ace64e24bf4 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/PreparePositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/PreparePositionTagger.cs @@ -2,34 +2,23 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using System; -using KurrentDB.Projections.Core.Messages; using KurrentDB.Projections.Core.Services.Processing.Checkpointing; +using static KurrentDB.Projections.Core.Messages.ReaderSubscriptionMessage; namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; -public class PreparePositionTagger : PositionTagger { - public PreparePositionTagger(int phase) - : base(phase) { +public class PreparePositionTagger(int phase) : PositionTagger(phase) { + public override bool IsMessageAfterCheckpointTag(CheckpointTag previous, CommittedEventDistributed committedEvent) { + return previous.Phase < Phase || committedEvent.Data.Position.PreparePosition > previous.PreparePosition; } - public override bool IsMessageAfterCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { - if (previous.Phase < Phase) - return true; - return committedEvent.Data.Position.PreparePosition > previous.PreparePosition; + public override CheckpointTag MakeCheckpointTag(CheckpointTag previous, CommittedEventDistributed committedEvent) { + return previous.Phase != Phase + ? throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}") + : CheckpointTag.FromPreparePosition(previous.Phase, committedEvent.Data.Position.PreparePosition); } - public override CheckpointTag MakeCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { - if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); - - return CheckpointTag.FromPreparePosition(previous.Phase, committedEvent.Data.Position.PreparePosition); - } - - public override CheckpointTag MakeCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.EventReaderPartitionDeleted partitionDeleted) { + public override CheckpointTag MakeCheckpointTag(CheckpointTag previous, EventReaderPartitionDeleted partitionDeleted) { throw new NotSupportedException(); } @@ -46,30 +35,19 @@ public override CheckpointTag AdjustTag(CheckpointTag tag) { return tag; if (tag.Phase > Phase) throw new ArgumentException( - string.Format( - "Invalid checkpoint tag phase. Expected less or equal to: {0} Was: {1}", Phase, tag.Phase), - "tag"); + $"Invalid checkpoint tag phase. Expected less or equal to: {Phase} Was: {tag.Phase}", + nameof(tag)); if (tag.Mode_ == CheckpointTag.Mode.PreparePosition) return tag; - switch (tag.Mode_) { - case CheckpointTag.Mode.EventTypeIndex: - throw new NotSupportedException( - "Conversion from EventTypeIndex to PreparePosition position tag is not supported"); - case CheckpointTag.Mode.Stream: - throw new NotSupportedException( - "Conversion from Stream to PreparePosition position tag is not supported"); - case CheckpointTag.Mode.MultiStream: - throw new NotSupportedException( - "Conversion from MultiStream to PreparePosition position tag is not supported"); - case CheckpointTag.Mode.Position: - throw new NotSupportedException( - "Conversion from Position to PreparePosition position tag is not supported"); - default: - throw new NotSupportedException(string.Format( - "The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {0}", - tag.ToString())); - } + throw tag.Mode_ switch { + CheckpointTag.Mode.EventTypeIndex => new("Conversion from EventTypeIndex to PreparePosition position tag is not supported"), + CheckpointTag.Mode.Stream => new("Conversion from Stream to PreparePosition position tag is not supported"), + CheckpointTag.Mode.MultiStream => new("Conversion from MultiStream to PreparePosition position tag is not supported"), + CheckpointTag.Mode.Position => new("Conversion from Position to PreparePosition position tag is not supported"), + _ => new NotSupportedException( + $"The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {tag}") + }; } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventFilter.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventFilter.cs index 32e15da0c78..bcc31cd0d24 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventFilter.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventFilter.cs @@ -6,26 +6,19 @@ namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; -public class TransactionFileEventFilter : EventFilter { - private readonly bool _includeLinks; - - public TransactionFileEventFilter( - bool allEvents, bool includeDeletedStreamEvents, HashSet events, bool includeLinks = false) - : base(allEvents, includeDeletedStreamEvents, events) { - _includeLinks = includeLinks; - } - +public class TransactionFileEventFilter(bool allEvents, bool includeDeletedStreamEvents, HashSet events, bool includeLinks = false) + : EventFilter(allEvents, includeDeletedStreamEvents, events) { protected override bool DeletedNotificationPasses(string positionStreamId) { return true; } public override bool PassesSource(bool resolvedFromLinkTo, string positionStreamId, string eventType) { - if (!_includeLinks && eventType == SystemEventTypes.LinkTo) + if (!includeLinks && eventType == SystemEventTypes.LinkTo) return false; - return (_includeLinks || !resolvedFromLinkTo) - && (!SystemStreams.IsSystemStream(positionStreamId) - || SystemStreams.IsMetastream(positionStreamId) - && !SystemStreams.IsSystemStream(SystemStreams.OriginalStreamOf(positionStreamId))); + return (includeLinks || !resolvedFromLinkTo) + && (!SystemStreams.IsSystemStream(positionStreamId) + || SystemStreams.IsMetastream(positionStreamId) + && !SystemStreams.IsSystemStream(SystemStreams.OriginalStreamOf(positionStreamId))); } public override string GetCategory(string positionStreamId) { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventReader.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventReader.cs index bbbddf0e070..d03ee1272c9 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventReader.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFileEventReader.cs @@ -19,7 +19,7 @@ public class TransactionFileEventReader : EventReader, IHandle, IHandle { private bool _eventsRequested; - private int _maxReadCount = 250; + private const int MaxReadCount = 250; private TFPos _from; private readonly bool _deliverEndOfTfPosition; private readonly bool _resolveLinkTos; @@ -32,15 +32,14 @@ public TransactionFileEventReader( IPublisher publisher, Guid eventReaderCorrelationId, ClaimsPrincipal readAs, - TFPos @from, + TFPos from, ITimeProvider timeProvider, bool stopOnEof = false, bool deliverEndOfTFPosition = true, bool resolveLinkTos = true) : base(publisher, eventReaderCorrelationId, readAs, stopOnEof) { - if (publisher == null) - throw new ArgumentNullException("publisher"); - _from = @from; + ArgumentNullException.ThrowIfNull(publisher); + _from = from; _deliverEndOfTfPosition = deliverEndOfTFPosition; _resolveLinkTos = resolveLinkTos; _timeProvider = timeProvider; @@ -106,8 +105,7 @@ public void Handle(ProjectionManagementMessage.Internal.ReadTimeout message) { } private void SendIdle() { - _publisher.Publish( - new ReaderSubscriptionMessage.EventReaderIdle(EventReaderCorrelationId, _timeProvider.UtcNow)); + _publisher.Publish(new ReaderSubscriptionMessage.EventReaderIdle(EventReaderCorrelationId, _timeProvider.UtcNow)); } protected override void RequestEvents() { @@ -151,7 +149,7 @@ private Message CreateReadTimeoutMessage(Guid correlationId, string streamId) { private Message CreateReadEventsMessage(Guid correlationId) { return new ClientMessage.ReadAllEventsForward( correlationId, correlationId, new SendToThisEnvelope(this), _from.CommitPosition, - _from.PreparePosition == -1 ? _from.CommitPosition : _from.PreparePosition, _maxReadCount, + _from.PreparePosition == -1 ? _from.CommitPosition : _from.PreparePosition, MaxReadCount, _resolveLinkTos, false, null, ReadAs, replyOnExpired: false); } @@ -168,35 +166,32 @@ private void DeliverEvent( KurrentDB.Core.Data.ResolvedEvent @event, long lastCommitPosition, TFPos currentFrom) { EventRecord linkEvent = @event.Link; EventRecord targetEvent = @event.Event ?? linkEvent; - EventRecord positionEvent = (linkEvent ?? targetEvent); + EventRecord positionEvent = linkEvent ?? targetEvent; TFPos receivedPosition = @event.OriginalPosition.Value; if (currentFrom > receivedPosition) throw new Exception( - string.Format( - "ReadFromTF returned events in incorrect order. Last known position is: {0}. Received position is: {1}", - currentFrom, receivedPosition)); + $"ReadFromTF returned events in incorrect order. Last known position is: {currentFrom}. Received position is: {receivedPosition}"); var resolvedEvent = new ResolvedEvent(@event, null); - string deletedPartitionStreamId; if (resolvedEvent.IsLinkToDeletedStream && !resolvedEvent.IsLinkToDeletedStreamTombstone) return; bool isDeletedStreamEvent = StreamDeletedHelper.IsStreamDeletedEventOrLinkToStreamDeletedEvent( - resolvedEvent, @event.ResolveResult, out deletedPartitionStreamId); + resolvedEvent, @event.ResolveResult, out var deletedPartitionStreamId); _publisher.Publish( new ReaderSubscriptionMessage.CommittedEventDistributed( EventReaderCorrelationId, resolvedEvent, - _stopOnEof ? (long?)null : receivedPosition.PreparePosition, + _stopOnEof ? null : receivedPosition.PreparePosition, 100.0f * positionEvent.LogPosition / lastCommitPosition, - source: this.GetType())); + source: GetType())); if (isDeletedStreamEvent) _publisher.Publish( new ReaderSubscriptionMessage.EventReaderPartitionDeleted( - EventReaderCorrelationId, deletedPartitionStreamId, source: this.GetType(), + EventReaderCorrelationId, deletedPartitionStreamId, source: GetType(), deleteEventOrLinkTargetPosition: resolvedEvent.EventOrLinkTargetPosition, deleteLinkOrEventPosition: resolvedEvent.LinkOrEventPosition, positionStreamId: positionEvent.EventStreamId, positionEventNumber: positionEvent.EventNumber)); diff --git a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFilePositionTagger.cs b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFilePositionTagger.cs index 0a4fee4ac7f..866f5221a69 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFilePositionTagger.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/TransactionFile/TransactionFilePositionTagger.cs @@ -7,11 +7,7 @@ namespace KurrentDB.Projections.Core.Services.Processing.TransactionFile; -public class TransactionFilePositionTagger : PositionTagger { - public TransactionFilePositionTagger(int phase) - : base(phase) { - } - +public class TransactionFilePositionTagger(int phase) : PositionTagger(phase) { public override bool IsCompatible(CheckpointTag checkpointTag) { return checkpointTag.Mode_ == CheckpointTag.Mode.Position; } @@ -21,29 +17,23 @@ public override CheckpointTag AdjustTag(CheckpointTag tag) { return tag; if (tag.Phase > Phase) throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected less or equal to: {0} Was: {1}", Phase, - tag.Phase), "tag"); + $"Invalid checkpoint tag phase. Expected less or equal to: {Phase} Was: {tag.Phase}", nameof(tag)); if (tag.Mode_ == CheckpointTag.Mode.Position) return tag; - switch (tag.Mode_) { - case CheckpointTag.Mode.EventTypeIndex: - return CheckpointTag.FromPosition( - tag.Phase, tag.Position.CommitPosition, tag.Position.PreparePosition); - case CheckpointTag.Mode.Stream: - throw new NotSupportedException("Conversion from Stream to Position position tag is not supported"); - case CheckpointTag.Mode.MultiStream: - throw new NotSupportedException( - "Conversion from MultiStream to Position position tag is not supported"); - case CheckpointTag.Mode.PreparePosition: - throw new NotSupportedException( - "Conversion from PreparePosition to Position position tag is not supported"); - default: - throw new NotSupportedException(string.Format( - "The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {0}", - tag.ToString())); - } + return tag.Mode_ switch { + CheckpointTag.Mode.EventTypeIndex => CheckpointTag.FromPosition(tag.Phase, tag.Position.CommitPosition, + tag.Position.PreparePosition), + CheckpointTag.Mode.Stream => + throw new NotSupportedException("Conversion from Stream to Position position tag is not supported"), + CheckpointTag.Mode.MultiStream => throw new NotSupportedException( + "Conversion from MultiStream to Position position tag is not supported"), + CheckpointTag.Mode.PreparePosition => throw new NotSupportedException( + "Conversion from PreparePosition to Position position tag is not supported"), + _ => throw new NotSupportedException( + $"The given checkpoint is invalid. Possible causes might include having written an event to the projection's managed stream. The bad checkpoint: {tag}") + }; } public override bool IsMessageAfterCheckpointTag( @@ -51,30 +41,20 @@ public override bool IsMessageAfterCheckpointTag( if (previous.Phase < Phase) return true; if (previous.Mode_ != CheckpointTag.Mode.Position) - throw new ArgumentException("Mode.Position expected", "previous"); + throw new ArgumentException("Mode.Position expected", nameof(previous)); return committedEvent.Data.Position > previous.Position; } - public override CheckpointTag MakeCheckpointTag( - CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { - if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); - - return CheckpointTag.FromPosition(previous.Phase, committedEvent.Data.Position); + public override CheckpointTag MakeCheckpointTag(CheckpointTag previous, ReaderSubscriptionMessage.CommittedEventDistributed committedEvent) { + return previous.Phase != Phase ? throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}") : CheckpointTag.FromPosition(previous.Phase, committedEvent.Data.Position); } public override CheckpointTag MakeCheckpointTag(CheckpointTag previous, ReaderSubscriptionMessage.EventReaderPartitionDeleted partitionDeleted) { if (previous.Phase != Phase) - throw new ArgumentException( - string.Format("Invalid checkpoint tag phase. Expected: {0} Was: {1}", Phase, previous.Phase)); - - if (partitionDeleted.DeleteLinkOrEventPosition == null) - throw new ArgumentException( - "Invalid partiton deleted message. deleteEventOrLinkTargetPosition required"); + throw new ArgumentException($"Invalid checkpoint tag phase. Expected: {Phase} Was: {previous.Phase}"); - return CheckpointTag.FromPosition(previous.Phase, partitionDeleted.DeleteLinkOrEventPosition.Value); + return partitionDeleted.DeleteLinkOrEventPosition == null ? throw new ArgumentException("Invalid partition deleted message. deleteEventOrLinkTargetPosition required") : CheckpointTag.FromPosition(previous.Phase, partitionDeleted.DeleteLinkOrEventPosition.Value); } public override CheckpointTag MakeZeroCheckpointTag() { diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointSuggestedWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointSuggestedWorkItem.cs index 6285fe967c2..9ffddafb8bb 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointSuggestedWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointSuggestedWorkItem.cs @@ -7,32 +7,22 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -public class CheckpointSuggestedWorkItem : CheckpointWorkItemBase { - private readonly IProjectionPhaseCheckpointManager _projectionPhase; - private readonly EventReaderSubscriptionMessage.CheckpointSuggested _message; - private readonly ICoreProjectionCheckpointManager _checkpointManager; - - private bool _completed = false; - private bool _completeRequested = false; - - public CheckpointSuggestedWorkItem( - IProjectionPhaseCheckpointManager projectionPhase, - EventReaderSubscriptionMessage.CheckpointSuggested message, - ICoreProjectionCheckpointManager checkpointManager) - : base() { - _projectionPhase = projectionPhase; - _message = message; - _checkpointManager = checkpointManager; - } +public class CheckpointSuggestedWorkItem( + IProjectionPhaseCheckpointManager projectionPhase, + EventReaderSubscriptionMessage.CheckpointSuggested message, + ICoreProjectionCheckpointManager checkpointManager) + : CheckpointWorkItemBase { + private bool _completed; + private bool _completeRequested; protected override void WriteOutput() { - _projectionPhase.SetCurrentCheckpointSuggestedWorkItem(this); - if (_checkpointManager.CheckpointSuggested(_message.CheckpointTag, _message.Progress)) { - _projectionPhase.SetCurrentCheckpointSuggestedWorkItem(null); + projectionPhase.SetCurrentCheckpointSuggestedWorkItem(this); + if (checkpointManager.CheckpointSuggested(message.CheckpointTag, message.Progress)) { + projectionPhase.SetCurrentCheckpointSuggestedWorkItem(null); _completed = true; } - _projectionPhase.NewCheckpointStarted(_message.CheckpointTag); + projectionPhase.NewCheckpointStarted(message.CheckpointTag); NextStage(); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointWorkItemBase.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointWorkItemBase.cs index 137102a33af..050d5d2ee4a 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointWorkItemBase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CheckpointWorkItemBase.cs @@ -4,14 +4,12 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; public class CheckpointWorkItemBase : WorkItem { - private static readonly object _correlationId = new object(); + private static readonly object CorrelationId = new(); - protected CheckpointWorkItemBase() - : base(_correlationId) { + protected CheckpointWorkItemBase() : base(CorrelationId) { _requiresRunning = true; } - protected CheckpointWorkItemBase(object correlation) - : base(correlation) { + protected CheckpointWorkItemBase(object correlation) : base(correlation) { } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CommittedEventWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CommittedEventWorkItem.cs index bc623b9e2ee..35cfb4dae83 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CommittedEventWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CommittedEventWorkItem.cs @@ -2,13 +2,12 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using KurrentDB.Projections.Core.Messages; -using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Projections.Core.Services.Processing.Partitioning; using KurrentDB.Projections.Core.Services.Processing.Phases; namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -class CommittedEventWorkItem : WorkItem { +internal class CommittedEventWorkItem : WorkItem { private readonly EventReaderSubscriptionMessage.CommittedEventReceived _message; private string _partition; private readonly IEventProcessingProjectionPhase _projection; @@ -38,7 +37,7 @@ protected override void GetStatePartition() { NextStage(_partition); } - protected override void Load(CheckpointTag checkpointTag) { + protected override void Load() { if (_partition == null) { NextStage(); return; diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CompletedWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CompletedWorkItem.cs index 30c999dc835..38358b7af3c 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CompletedWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/CompletedWorkItem.cs @@ -5,16 +5,9 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -class CompletedWorkItem : CheckpointWorkItemBase { - private readonly IProjectionPhaseCompleter _projection; - - public CompletedWorkItem(IProjectionPhaseCompleter projection) - : base() { - _projection = projection; - } - +internal class CompletedWorkItem(IProjectionPhaseCompleter projection) : CheckpointWorkItemBase { protected override void WriteOutput() { - _projection.Complete(); + projection.Complete(); NextStage(); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetDataWorkItemBase.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetDataWorkItemBase.cs index 25eda0f360f..b40bfbff153 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetDataWorkItemBase.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetDataWorkItemBase.cs @@ -25,8 +25,7 @@ protected GetDataWorkItemBase( IProjectionPhaseStateManager projection, string partition) : base(null) { - if (partition == null) - throw new ArgumentNullException("partition"); + ArgumentNullException.ThrowIfNull(partition); _publisher = publisher; _partition = partition; _correlationId = correlationId; @@ -38,7 +37,7 @@ protected override void GetStatePartition() { NextStage(_partition); } - protected override void Load(CheckpointTag checkpointTag) { + protected override void Load() { _lastProcessedCheckpointTag = _projection.LastProcessedEventPosition; _projection.BeginGetPartitionStateAt( _partition, diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetResultWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetResultWorkItem.cs index 83544ffb886..2c170bf05a3 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetResultWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetResultWorkItem.cs @@ -10,16 +10,13 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -class GetResultWorkItem : GetDataWorkItemBase { - public GetResultWorkItem( - IPublisher publisher, - Guid correlationId, - Guid projectionId, - IProjectionPhaseStateManager projection, - string partition) - : base(publisher, correlationId, projectionId, projection, partition) { - } - +class GetResultWorkItem( + IPublisher publisher, + Guid correlationId, + Guid projectionId, + IProjectionPhaseStateManager projection, + string partition) + : GetDataWorkItemBase(publisher, correlationId, projectionId, projection, partition) { protected override void Reply(PartitionState state, CheckpointTag checkpointTag) { if (state == null) _publisher.Publish( diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetStateWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetStateWorkItem.cs index 73d247bb028..ca043c680f9 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetStateWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/GetStateWorkItem.cs @@ -10,32 +10,19 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -class GetStateWorkItem : GetDataWorkItemBase { - public GetStateWorkItem( - IPublisher publisher, - Guid correlationId, - Guid projectionId, - IProjectionPhaseStateManager projection, - string partition) - : base(publisher, correlationId, projectionId, projection, partition) { - } - +internal class GetStateWorkItem( + IPublisher publisher, + Guid correlationId, + Guid projectionId, + IProjectionPhaseStateManager projection, + string partition) + : GetDataWorkItemBase(publisher, correlationId, projectionId, projection, partition) { protected override void Reply(PartitionState state, CheckpointTag checkpointTag) { - if (state == null) - _publisher.Publish( - new CoreProjectionStatusMessage.StateReport( - _correlationId, - _projectionId, - _partition, - null, - checkpointTag)); - else - _publisher.Publish( - new CoreProjectionStatusMessage.StateReport( - _correlationId, - _projectionId, - _partition, - state.State, - checkpointTag)); + _publisher.Publish(new CoreProjectionStatusMessage.StateReport( + _correlationId, + _projectionId, + _partition, + state?.State, + checkpointTag)); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/NotAuthorizedWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/NotAuthorizedWorkItem.cs index f7756b77f94..5fbb19cf9e9 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/NotAuthorizedWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/NotAuthorizedWorkItem.cs @@ -5,11 +5,7 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -class NotAuthorizedWorkItem : CheckpointWorkItemBase { - public NotAuthorizedWorkItem() - : base(null) { - } - +internal class NotAuthorizedWorkItem() : CheckpointWorkItemBase(null) { protected override void ProcessEvent() { throw new Exception("Projection cannot read its source. Not authorized."); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/PartitionDeletedWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/PartitionDeletedWorkItem.cs index 0c8573adf1c..db631ca6d07 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/PartitionDeletedWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/PartitionDeletedWorkItem.cs @@ -2,7 +2,6 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using KurrentDB.Projections.Core.Messages; -using KurrentDB.Projections.Core.Services.Processing.Checkpointing; using KurrentDB.Projections.Core.Services.Processing.Partitioning; using KurrentDB.Projections.Core.Services.Processing.Phases; @@ -27,7 +26,7 @@ protected override void GetStatePartition() { NextStage(_partition); } - protected override void Load(CheckpointTag checkpointTag) { + protected override void Load() { // we load partition state even if stopping etc. should we skip? _projection.BeginGetPartitionStateAt(_partition, _message.CheckpointTag, LoadCompleted, lockLoaded: true); } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/ProgressWorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/ProgressWorkItem.cs index 03cc09a1511..7a1028e8548 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/ProgressWorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/ProgressWorkItem.cs @@ -2,26 +2,13 @@ // Kurrent, Inc licenses this file to you under the Kurrent License v1 (see LICENSE.md). using KurrentDB.Projections.Core.Services.Processing.Checkpointing; -using KurrentDB.Projections.Core.Services.Processing.Phases; namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -class ProgressWorkItem : CheckpointWorkItemBase { - private readonly ICoreProjectionCheckpointManager _checkpointManager; - private readonly IProgressResultWriter _resultWriter; - private readonly float _progress; - - public ProgressWorkItem(ICoreProjectionCheckpointManager checkpointManager, IProgressResultWriter resultWriter, - float progress) - : base(null) { - _checkpointManager = checkpointManager; - _resultWriter = resultWriter; - _progress = progress; - } - +internal class ProgressWorkItem(ICoreProjectionCheckpointManager checkpointManager, float progress) + : CheckpointWorkItemBase(null) { protected override void WriteOutput() { - _checkpointManager.Progress(_progress); - _resultWriter.WriteProgress(_progress); + checkpointManager.Progress(progress); NextStage(); } } diff --git a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/WorkItem.cs b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/WorkItem.cs index 49d78826413..3ff1b7e30ae 100644 --- a/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/WorkItem.cs +++ b/src/KurrentDB.Projections.Core/Services/Processing/WorkItems/WorkItem.cs @@ -6,23 +6,14 @@ namespace KurrentDB.Projections.Core.Services.Processing.WorkItems; -public abstract class WorkItem : StagedTask { - private readonly int _lastStage; +public abstract class WorkItem(object initialCorrelationId) : StagedTask(initialCorrelationId) { + private const int LastStage = 5; private Action _complete; private int _onStage; private CheckpointTag _checkpointTag; private object _lastStageCorrelationId; - private CoreProjectionQueue _queue; protected bool _requiresRunning; - - protected WorkItem(object initialCorrelationId) - : base(initialCorrelationId) { - _lastStage = 5; - } - - protected CoreProjectionQueue Queue { - get { return _queue; } - } + private CoreProjectionQueue _queue; public override void Process(int onStage, Action readyForStage) { if (_checkpointTag == null) @@ -30,7 +21,7 @@ public override void Process(int onStage, Action readyForStage) { _complete = readyForStage; _onStage = onStage; //TODO: - if (_requiresRunning && !Queue.IsRunning) + if (_requiresRunning && !_queue.IsRunning) NextStage(); else { switch (onStage) { @@ -41,7 +32,7 @@ public override void Process(int onStage, Action readyForStage) { GetStatePartition(); break; case 2: - Load(_checkpointTag); + Load(); break; case 3: ProcessEvent(); @@ -66,7 +57,7 @@ protected virtual void GetStatePartition() { NextStage(); } - protected virtual void Load(CheckpointTag checkpointTag) { + protected virtual void Load() { NextStage(); } @@ -85,7 +76,7 @@ protected virtual void CompleteItem() { protected void NextStage(object newCorrelationId = null) { _lastStageCorrelationId = newCorrelationId ?? _lastStageCorrelationId ?? InitialCorrelationId; - _complete(_onStage == _lastStage ? -1 : _onStage + 1, _lastStageCorrelationId); + _complete(_onStage == LastStage ? -1 : _onStage + 1, _lastStageCorrelationId); } public void SetCheckpointTag(CheckpointTag checkpointTag) { diff --git a/src/KurrentDB.Projections.Core/Services/ProjectionConfig.cs b/src/KurrentDB.Projections.Core/Services/ProjectionConfig.cs index 567bc662a4a..b07b41fd60e 100644 --- a/src/KurrentDB.Projections.Core/Services/ProjectionConfig.cs +++ b/src/KurrentDB.Projections.Core/Services/ProjectionConfig.cs @@ -8,34 +8,17 @@ namespace KurrentDB.Projections.Core.Services; public class ProjectionConfig { - private readonly ClaimsPrincipal _runAs; - private readonly int _checkpointHandledThreshold; - private readonly int _checkpointUnhandledBytesThreshold; - private readonly int _pendingEventsThreshold; - private readonly int _maxWriteBatchLength; - private readonly bool _emitEventEnabled; - private readonly bool _checkpointsEnabled; - private readonly bool _createTempStreams; - private readonly bool _stopOnEof; - private readonly bool _trackEmittedStreams; - private readonly int _checkpointAfterMs; - private readonly int _maximumAllowedWritesInFlight; - public ProjectionConfig(ClaimsPrincipal runAs, int checkpointHandledThreshold, int checkpointUnhandledBytesThreshold, int pendingEventsThreshold, int maxWriteBatchLength, bool emitEventEnabled, bool checkpointsEnabled, bool createTempStreams, bool stopOnEof, bool trackEmittedStreams, int checkpointAfterMs, int maximumAllowedWritesInFlight, int? projectionExecutionTimeout) { if (checkpointsEnabled) { - if (checkpointHandledThreshold <= 0) - throw new ArgumentOutOfRangeException("checkpointHandledThreshold"); + ArgumentOutOfRangeException.ThrowIfNegativeOrZero(checkpointHandledThreshold); if (checkpointUnhandledBytesThreshold < checkpointHandledThreshold) - throw new ArgumentException( - "Checkpoint threshold cannot be less than checkpoint handled threshold"); + throw new ArgumentException("Checkpoint threshold cannot be less than checkpoint handled threshold"); } else { - if (checkpointHandledThreshold != 0) - throw new ArgumentOutOfRangeException("checkpointHandledThreshold must be 0"); - if (checkpointUnhandledBytesThreshold != 0) - throw new ArgumentException("checkpointUnhandledBytesThreshold must be 0"); + ArgumentOutOfRangeException.ThrowIfNotEqual(checkpointHandledThreshold, 0); + ArgumentOutOfRangeException.ThrowIfNotEqual(checkpointUnhandledBytesThreshold, 0); } if (maximumAllowedWritesInFlight < AllowedWritesInFlight.Unbounded) { @@ -43,73 +26,36 @@ public ProjectionConfig(ClaimsPrincipal runAs, int checkpointHandledThreshold, i $"The Maximum Number of Allowed Writes in Flight cannot be less than {AllowedWritesInFlight.Unbounded}"); } - if (projectionExecutionTimeout is not null && projectionExecutionTimeout <= 0) { - throw new ArgumentException( - $"The projection execution timeout should be positive. Found : {projectionExecutionTimeout}"); + if (projectionExecutionTimeout <= 0) { + throw new ArgumentException($"The projection execution timeout should be positive. Found: {projectionExecutionTimeout}"); } - _runAs = runAs; - _checkpointHandledThreshold = checkpointHandledThreshold; - _checkpointUnhandledBytesThreshold = checkpointUnhandledBytesThreshold; - _pendingEventsThreshold = pendingEventsThreshold; - _maxWriteBatchLength = maxWriteBatchLength; - _emitEventEnabled = emitEventEnabled; - _checkpointsEnabled = checkpointsEnabled; - _createTempStreams = createTempStreams; - _stopOnEof = stopOnEof; - _trackEmittedStreams = trackEmittedStreams; - _checkpointAfterMs = checkpointAfterMs; - _maximumAllowedWritesInFlight = maximumAllowedWritesInFlight; + RunAs = runAs; + CheckpointHandledThreshold = checkpointHandledThreshold; + CheckpointUnhandledBytesThreshold = checkpointUnhandledBytesThreshold; + PendingEventsThreshold = pendingEventsThreshold; + MaxWriteBatchLength = maxWriteBatchLength; + EmitEventEnabled = emitEventEnabled; + CheckpointsEnabled = checkpointsEnabled; + CreateTempStreams = createTempStreams; + StopOnEof = stopOnEof; + TrackEmittedStreams = trackEmittedStreams; + CheckpointAfterMs = checkpointAfterMs; + MaximumAllowedWritesInFlight = maximumAllowedWritesInFlight; ProjectionExecutionTimeout = projectionExecutionTimeout; } - public int CheckpointHandledThreshold { - get { return _checkpointHandledThreshold; } - } - - public int CheckpointUnhandledBytesThreshold { - get { return _checkpointUnhandledBytesThreshold; } - } - - public int MaxWriteBatchLength { - get { return _maxWriteBatchLength; } - } - - public bool EmitEventEnabled { - get { return _emitEventEnabled; } - } - - public bool CheckpointsEnabled { - get { return _checkpointsEnabled; } - } - - public int PendingEventsThreshold { - get { return _pendingEventsThreshold; } - } - - public bool CreateTempStreams { - get { return _createTempStreams; } - } - - public bool StopOnEof { - get { return _stopOnEof; } - } - - public ClaimsPrincipal RunAs { - get { return _runAs; } - } - - public bool TrackEmittedStreams { - get { return _trackEmittedStreams; } - } - - public int CheckpointAfterMs { - get { return _checkpointAfterMs; } - } - - public int MaximumAllowedWritesInFlight { - get { return _maximumAllowedWritesInFlight; } - } - + public int CheckpointHandledThreshold { get; } + public int CheckpointUnhandledBytesThreshold { get; } + public int MaxWriteBatchLength { get; } + public bool EmitEventEnabled { get; } + public bool CheckpointsEnabled { get; } + public int PendingEventsThreshold { get; } + public bool CreateTempStreams { get; } + public bool StopOnEof { get; } + public ClaimsPrincipal RunAs { get; } + public bool TrackEmittedStreams { get; } + public int CheckpointAfterMs { get; } + public int MaximumAllowedWritesInFlight { get; } public int? ProjectionExecutionTimeout { get; } } diff --git a/src/KurrentDB.Projections.Core/Services/ReaderSubscriptionDispatcher.cs b/src/KurrentDB.Projections.Core/Services/ReaderSubscriptionDispatcher.cs index a69e5042823..e3d023b719a 100644 --- a/src/KurrentDB.Projections.Core/Services/ReaderSubscriptionDispatcher.cs +++ b/src/KurrentDB.Projections.Core/Services/ReaderSubscriptionDispatcher.cs @@ -57,10 +57,12 @@ public IHandle CreateSubscriber() where T : EventReaderSubscriptionMessage private void Handle(T message) where T : EventReaderSubscriptionMessageBase { var correlationId = message.SubscriptionId; if (_map.TryGetValue(correlationId, out var subscriber)) { - if (subscriber is IHandle h) { - h.Handle(message); - } else if (subscriber is IAsyncHandle) { - throw new Exception($"ReaderSubscriptionDispatcher does not support asynchronous subscribers. Subscriber: {subscriber}"); + switch (subscriber) { + case IHandle h: + h.Handle(message); + break; + case IAsyncHandle: + throw new Exception($"ReaderSubscriptionDispatcher does not support asynchronous subscribers. Subscriber: {subscriber}"); } } } @@ -69,16 +71,10 @@ public void Subscribed(Guid correlationId, object subscriber) { _map.TryAdd(correlationId, subscriber); } - private class Subscriber : IHandle where T : EventReaderSubscriptionMessageBase { - private readonly ReaderSubscriptionDispatcher _host; - - public Subscriber( - ReaderSubscriptionDispatcher host) { - _host = host; - } - + private class Subscriber(ReaderSubscriptionDispatcher host) : IHandle + where T : EventReaderSubscriptionMessageBase { public void Handle(T message) { - _host.Handle(message); + host.Handle(message); } } } diff --git a/src/KurrentDB.Projections.Core/Standard/ByCorrelationId.cs b/src/KurrentDB.Projections.Core/Standard/ByCorrelationId.cs index fca693de72f..40827c98f96 100644 --- a/src/KurrentDB.Projections.Core/Standard/ByCorrelationId.cs +++ b/src/KurrentDB.Projections.Core/Standard/ByCorrelationId.cs @@ -18,10 +18,9 @@ namespace KurrentDB.Projections.Core.Standard; public class ByCorrelationId : IProjectionStateHandler { private readonly string _corrIdStreamPrefix; - public ByCorrelationId(string source, Action logger) { + public ByCorrelationId(string source, [UsedImplicitly] Action logger) { if (!string.IsNullOrWhiteSpace(source)) { - string correlationIdProperty; - if (!TryParseCorrelationIdProperty(source, out correlationIdProperty)) { + if (!TryParseCorrelationIdProperty(source, out var correlationIdProperty)) { throw new InvalidOperationException( "Could not parse projection source. Please make sure the source is a valid JSON string with a property: 'correlationIdProperty' having a string value"); } @@ -32,7 +31,7 @@ public ByCorrelationId(string source, Action logger) { _corrIdStreamPrefix = "$bc-"; } - private bool TryParseCorrelationIdProperty(string source, out string correlationIdProperty) { + private static bool TryParseCorrelationIdProperty(string source, out string correlationIdProperty) { correlationIdProperty = null; try { var obj = JObject.Parse(source); @@ -40,19 +39,14 @@ private bool TryParseCorrelationIdProperty(string source, out string correlation if (prop != null) { correlationIdProperty = prop; return true; - } else { - return false; } + + return false; } catch (Exception) { return false; } } - public void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { - builder.FromAll(); - builder.AllEvents(); - } - public void Load(string state) { } @@ -71,8 +65,13 @@ public string GetStatePartition(CheckpointTag eventPosition, string category, Re } public bool ProcessEvent( - string partition, CheckpointTag eventPosition, string category1, ResolvedEvent data, - out string newState, out string newSharedState, out EmittedEventEnvelope[] emittedEvents) { + string partition, + CheckpointTag eventPosition, + string category1, + ResolvedEvent data, + out string newState, + out string newSharedState, + out EmittedEventEnvelope[] emittedEvents) { newSharedState = null; emittedEvents = null; newState = null; @@ -97,38 +96,32 @@ public bool ProcessEvent( if (correlationId == null) return false; - string linkTarget; - if (data.EventType == SystemEventTypes.LinkTo) - linkTarget = data.Data; - else - linkTarget = data.EventSequenceNumber + "@" + data.EventStreamId; + var linkTarget = data.EventType == SystemEventTypes.LinkTo ? data.Data : $"{data.EventSequenceNumber}@{data.EventStreamId}"; - var metadataDict = new Dictionary(); - metadataDict.Add("$eventTimestamp", "\"" + data.Timestamp.ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ") + "\""); + var metadataDict = new Dictionary + { { "$eventTimestamp", "\"" + data.Timestamp.ToString("yyyy-MM-ddTHH:mm:ss.ffffffZ") + "\"" } }; if (data.EventType == SystemEventTypes.LinkTo) { - JObject linkObj = new JObject(); - linkObj.Add("eventId", data.EventId); - linkObj.Add("metadata", metadata); + JObject linkObj = new JObject { + { "eventId", data.EventId }, + { "metadata", metadata } + }; metadataDict.Add("$link", linkObj.ToJson()); } var linkMetadata = new ExtraMetaData(metadataDict); - emittedEvents = new[] { - new EmittedEventEnvelope( - new EmittedDataEvent( - _corrIdStreamPrefix + correlationId, Guid.NewGuid(), "$>", false, - linkTarget, - linkMetadata, - eventPosition, - expectedTag: null)) - }; + emittedEvents = [ + new(new EmittedDataEvent(_corrIdStreamPrefix + correlationId, Guid.NewGuid(), "$>", false, linkTarget, linkMetadata, + eventPosition, expectedTag: null)) + ]; return true; } - public bool ProcessPartitionCreated(string partition, CheckpointTag createPosition, ResolvedEvent data, + public bool ProcessPartitionCreated(string partition, + CheckpointTag createPosition, + ResolvedEvent data, out EmittedEventEnvelope[] emittedEvents) { emittedEvents = null; return false; @@ -147,5 +140,10 @@ public void Dispose() { public IQuerySources GetSourceDefinition() { return SourceDefinitionBuilder.From(ConfigureSourceProcessingStrategy); + + static void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { + builder.FromAll(); + builder.AllEvents(); + } } } diff --git a/src/KurrentDB.Projections.Core/Standard/CategorizeEventsByStreamPath.cs b/src/KurrentDB.Projections.Core/Standard/CategorizeEventsByStreamPath.cs index 7752f4835d2..dd01d1145db 100644 --- a/src/KurrentDB.Projections.Core/Standard/CategorizeEventsByStreamPath.cs +++ b/src/KurrentDB.Projections.Core/Standard/CategorizeEventsByStreamPath.cs @@ -13,17 +13,16 @@ namespace KurrentDB.Projections.Core.Standard; public class CategorizeEventsByStreamPath : IProjectionStateHandler { - private readonly string _categoryStreamPrefix; + private const string CategoryStreamPrefix = "$ce-"; private readonly StreamCategoryExtractor _streamCategoryExtractor; public CategorizeEventsByStreamPath(string source, Action logger) { var extractor = StreamCategoryExtractor.GetExtractor(source, logger); // we will need to declare event types we are interested in - _categoryStreamPrefix = "$ce-"; _streamCategoryExtractor = extractor; } - public void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { + private static void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { builder.FromAll(); builder.AllEvents(); builder.SetIncludeLinks(); @@ -47,37 +46,44 @@ public string GetStatePartition(CheckpointTag eventPosition, string category, Re } public bool ProcessEvent( - string partition, CheckpointTag eventPosition, string category1, ResolvedEvent data, - out string newState, out string newSharedState, out EmittedEventEnvelope[] emittedEvents) { + string partition, + CheckpointTag eventPosition, + string category1, + ResolvedEvent data, + out string newState, + out string newSharedState, + out EmittedEventEnvelope[] emittedEvents) { newSharedState = null; emittedEvents = null; newState = null; - string deletedStreamId; - var isStreamDeletedEvent = StreamDeletedHelper.IsStreamDeletedEvent( - data.PositionStreamId, data.EventType, data.Data, out deletedStreamId); + var isStreamDeletedEvent = + StreamDeletedHelper.IsStreamDeletedEvent(data.PositionStreamId, data.EventType, data.Data, out var deletedStreamId); var category = _streamCategoryExtractor.GetCategoryByStreamId(isStreamDeletedEvent ? deletedStreamId : data.PositionStreamId); if (category == null) return true; // handled but not interesting + var linkTarget = data.EventType == SystemEventTypes.LinkTo ? data.Data : $"{data.EventSequenceNumber}@{data.EventStreamId}"; - string linkTarget; - if (data.EventType == SystemEventTypes.LinkTo) - linkTarget = data.Data; - else - linkTarget = data.EventSequenceNumber + "@" + data.EventStreamId; - - emittedEvents = new[] { - new EmittedEventEnvelope( + emittedEvents = [ + new( new EmittedLinkToWithRecategorization( - _categoryStreamPrefix + category, Guid.NewGuid(), linkTarget, eventPosition, expectedTag: null, - originalStreamId: isStreamDeletedEvent?deletedStreamId:data.PositionStreamId, streamDeletedAt: isStreamDeletedEvent ? -1 : (int?)null)) - }; + $"{CategoryStreamPrefix}{category}", Guid.NewGuid(), + linkTarget, + eventPosition, + expectedTag: null, + originalStreamId: isStreamDeletedEvent ? deletedStreamId : data.PositionStreamId, + streamDeletedAt: isStreamDeletedEvent ? -1 : null + ) + ) + ]; return true; } - public bool ProcessPartitionCreated(string partition, CheckpointTag createPosition, ResolvedEvent data, + public bool ProcessPartitionCreated(string partition, + CheckpointTag createPosition, + ResolvedEvent data, out EmittedEventEnvelope[] emittedEvents) { emittedEvents = null; return false; diff --git a/src/KurrentDB.Projections.Core/Standard/CategorizeStreamByPath.cs b/src/KurrentDB.Projections.Core/Standard/CategorizeStreamByPath.cs index 70b2ed2d40c..35982d16ee5 100644 --- a/src/KurrentDB.Projections.Core/Standard/CategorizeStreamByPath.cs +++ b/src/KurrentDB.Projections.Core/Standard/CategorizeStreamByPath.cs @@ -20,7 +20,7 @@ public CategorizeStreamByPath(string source, Action logger) { _streamCategoryExtractor = extractor; } - public void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { + private static void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { builder.FromAll(); builder.AllEvents(); builder.SetIncludeLinks(); @@ -57,12 +57,12 @@ public bool ProcessEvent( if (category == null) return true; // handled but not interesting - emittedEvents = new[] { - new EmittedEventEnvelope( + emittedEvents = [ + new( new EmittedDataEvent( - "$category" + "-" + category, Guid.NewGuid(), SystemEventTypes.StreamReference, false, + $"$category-{category}", Guid.NewGuid(), SystemEventTypes.StreamReference, false, data.PositionStreamId, null, eventPosition, expectedTag: null)) - }; + ]; return true; } diff --git a/src/KurrentDB.Projections.Core/Standard/IndexEventsByEventType.cs b/src/KurrentDB.Projections.Core/Standard/IndexEventsByEventType.cs index 92b9710ea0a..ea4ab0e427e 100644 --- a/src/KurrentDB.Projections.Core/Standard/IndexEventsByEventType.cs +++ b/src/KurrentDB.Projections.Core/Standard/IndexEventsByEventType.cs @@ -16,7 +16,7 @@ public class IndexEventsByEventType : IProjectionStateHandler, IProjectionCheckp private readonly string _indexStreamPrefix; private readonly string _indexCheckpointStream; - public IndexEventsByEventType(string source, Action logger) { + public IndexEventsByEventType(string source, [UsedImplicitly] Action logger) { if (!string.IsNullOrWhiteSpace(source)) throw new InvalidOperationException("Empty source expected"); @@ -25,11 +25,6 @@ public IndexEventsByEventType(string source, Action logger) { _indexCheckpointStream = "$et"; } - public void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { - builder.FromAll(); - builder.AllEvents(); - } - public void Load(string state) { } @@ -48,8 +43,13 @@ public string GetStatePartition(CheckpointTag eventPosition, string category, Re } public bool ProcessEvent( - string partition, CheckpointTag eventPosition, string category1, ResolvedEvent data, - out string newState, out string newSharedState, out EmittedEventEnvelope[] emittedEvents) { + string partition, + CheckpointTag eventPosition, + string category1, + ResolvedEvent data, + out string newState, + out string newSharedState, + out EmittedEventEnvelope[] emittedEvents) { newSharedState = null; emittedEvents = null; newState = null; @@ -59,26 +59,25 @@ public bool ProcessEvent( if (indexedEventType == "$>") return false; - string positionStreamId; var isStreamDeletedEvent = StreamDeletedHelper.IsStreamDeletedEvent( - data.PositionStreamId, data.EventType, data.Data, out positionStreamId); + data.PositionStreamId, data.EventType, data.Data, out var positionStreamId); if (isStreamDeletedEvent) indexedEventType = "$deleted"; - emittedEvents = new[] { - new EmittedEventEnvelope( - new EmittedDataEvent( - _indexStreamPrefix + indexedEventType, Guid.NewGuid(), "$>", false, - data.EventSequenceNumber + "@" + positionStreamId, - isStreamDeletedEvent - ? new ExtraMetaData(new Dictionary {{"$deleted", new JRaw(-1)}}) - : null, eventPosition, expectedTag: null)) - }; + emittedEvents = [ + new(new EmittedDataEvent(_indexStreamPrefix + indexedEventType, Guid.NewGuid(), "$>", false, + $"{data.EventSequenceNumber}@{positionStreamId}", + isStreamDeletedEvent + ? new ExtraMetaData(new Dictionary { { "$deleted", new JRaw(-1) } }) + : null, eventPosition, expectedTag: null)) + ]; return true; } - public bool ProcessPartitionCreated(string partition, CheckpointTag createPosition, ResolvedEvent data, + public bool ProcessPartitionCreated(string partition, + CheckpointTag createPosition, + ResolvedEvent data, out EmittedEventEnvelope[] emittedEvents) { emittedEvents = null; return false; @@ -96,15 +95,18 @@ public void Dispose() { } public void ProcessNewCheckpoint(CheckpointTag checkpointPosition, out EmittedEventEnvelope[] emittedEvents) { - emittedEvents = new[] { - new EmittedEventEnvelope( - new EmittedDataEvent( - _indexCheckpointStream, Guid.NewGuid(), ProjectionEventTypes.PartitionCheckpoint, - true, checkpointPosition.ToJsonString(), null, checkpointPosition, expectedTag: null)) - }; + emittedEvents = [ + new(new EmittedDataEvent(_indexCheckpointStream, Guid.NewGuid(), ProjectionEventTypes.PartitionCheckpoint, true, + checkpointPosition.ToJsonString(), null, checkpointPosition, expectedTag: null)) + ]; } public IQuerySources GetSourceDefinition() { return SourceDefinitionBuilder.From(ConfigureSourceProcessingStrategy); + + static void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { + builder.FromAll(); + builder.AllEvents(); + } } } diff --git a/src/KurrentDB.Projections.Core/Standard/IndexStreams.cs b/src/KurrentDB.Projections.Core/Standard/IndexStreams.cs index 2599cce80ce..25aea26c0b8 100644 --- a/src/KurrentDB.Projections.Core/Standard/IndexStreams.cs +++ b/src/KurrentDB.Projections.Core/Standard/IndexStreams.cs @@ -12,20 +12,8 @@ namespace KurrentDB.Projections.Core.Standard; public class IndexStreams : IProjectionStateHandler { - public IndexStreams(string source, Action logger) { - var trimmedSource = source == null ? null : source.Trim(); - if (!string.IsNullOrEmpty(trimmedSource)) - throw new InvalidOperationException( - "Cannot initialize categorize stream projection handler. No source is allowed."); - if (logger != null) { - // logger(string.Format("Index streams projection handler has been initialized")); - } - } - - public void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { - builder.FromAll(); - builder.AllEvents(); - builder.SetIncludeLinks(); + public IndexStreams(string source, [UsedImplicitly] Action logger) { + ArgumentException.ThrowIfNullOrWhiteSpace(source, "Cannot initialize categorize stream projection handler. No source is allowed."); } public void Load(string state) { @@ -46,26 +34,30 @@ public string GetStatePartition(CheckpointTag eventPosition, string category, Re } public bool ProcessEvent( - string partition, CheckpointTag eventPosition, string category1, ResolvedEvent data, - out string newState, out string newSharedState, out EmittedEventEnvelope[] emittedEvents) { + string partition, + CheckpointTag eventPosition, + string category1, + ResolvedEvent data, + out string newState, + out string newSharedState, + out EmittedEventEnvelope[] emittedEvents) { newSharedState = null; emittedEvents = null; newState = null; if (data.PositionSequenceNumber != 0) return false; // not our event - emittedEvents = new[] { - new EmittedEventEnvelope( - new EmittedDataEvent( - SystemStreams.StreamsStream, Guid.NewGuid(), SystemEventTypes.LinkTo, false, - data.PositionSequenceNumber + "@" + data.PositionStreamId, null, eventPosition, - expectedTag: null)) - }; + emittedEvents = [ + new(new EmittedDataEvent(SystemStreams.StreamsStream, Guid.NewGuid(), SystemEventTypes.LinkTo, false, + $"{data.PositionSequenceNumber}@{data.PositionStreamId}", null, eventPosition, expectedTag: null)) + ]; return true; } - public bool ProcessPartitionCreated(string partition, CheckpointTag createPosition, ResolvedEvent data, + public bool ProcessPartitionCreated(string partition, + CheckpointTag createPosition, + ResolvedEvent data, out EmittedEventEnvelope[] emittedEvents) { emittedEvents = null; return false; @@ -84,5 +76,11 @@ public void Dispose() { public IQuerySources GetSourceDefinition() { return SourceDefinitionBuilder.From(ConfigureSourceProcessingStrategy); + + static void ConfigureSourceProcessingStrategy(SourceDefinitionBuilder builder) { + builder.FromAll(); + builder.AllEvents(); + builder.SetIncludeLinks(); + } } } diff --git a/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractor.cs b/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractor.cs index af094f70917..6cebe86e098 100644 --- a/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractor.cs +++ b/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractor.cs @@ -10,8 +10,8 @@ public abstract class StreamCategoryExtractor { public abstract string GetCategoryByStreamId(string streamId); - public static StreamCategoryExtractor GetExtractor(string source, Action logger) { - var trimmedSource = source == null ? null : source.Trim(); + public static StreamCategoryExtractor GetExtractor(string source, [UsedImplicitly] Action logger) { + var trimmedSource = source?.Trim(); if (string.IsNullOrEmpty(source)) throw new InvalidOperationException( "Cannot initialize categorization projection handler. " @@ -20,19 +20,11 @@ public static StreamCategoryExtractor GetExtractor(string source, Action new StreamCategoryExtractorByFirstSeparator(separatorLine[0]), + "last" => new StreamCategoryExtractorByLastSeparator(separatorLine[0]), + _ => throw new Exception() + }; } } diff --git a/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByFirstSeparator.cs b/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByFirstSeparator.cs index c30848f6ced..45816b7cb1e 100644 --- a/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByFirstSeparator.cs +++ b/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByFirstSeparator.cs @@ -3,19 +3,13 @@ namespace KurrentDB.Projections.Core.Standard; -public class StreamCategoryExtractorByFirstSeparator : StreamCategoryExtractor { - private readonly char _separator; - - public StreamCategoryExtractorByFirstSeparator(char separator) { - _separator = separator; - } - +public class StreamCategoryExtractorByFirstSeparator(char separator) : StreamCategoryExtractor { public override string GetCategoryByStreamId(string streamId) { string category = null; - if (!streamId.StartsWith("$")) { - var lastSeparatorPosition = streamId.IndexOf(_separator); + if (!streamId.StartsWith('$')) { + var lastSeparatorPosition = streamId.IndexOf(separator); if (lastSeparatorPosition > 0) - category = streamId.Substring(0, lastSeparatorPosition); + category = streamId[..lastSeparatorPosition]; } return category; diff --git a/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByLastSeparator.cs b/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByLastSeparator.cs index c1051c428d1..f4b7acb5dba 100644 --- a/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByLastSeparator.cs +++ b/src/KurrentDB.Projections.Core/Standard/StreamCategoryExtractorByLastSeparator.cs @@ -3,19 +3,13 @@ namespace KurrentDB.Projections.Core.Standard; -public class StreamCategoryExtractorByLastSeparator : StreamCategoryExtractor { - private readonly char _separator; - - public StreamCategoryExtractorByLastSeparator(char separator) { - _separator = separator; - } - +public class StreamCategoryExtractorByLastSeparator(char separator) : StreamCategoryExtractor { public override string GetCategoryByStreamId(string streamId) { string category = null; - if (!streamId.StartsWith("$")) { - var lastSeparatorPosition = streamId.LastIndexOf(_separator); + if (!streamId.StartsWith('$')) { + var lastSeparatorPosition = streamId.LastIndexOf(separator); if (lastSeparatorPosition > 0) - category = streamId.Substring(0, lastSeparatorPosition); + category = streamId[..lastSeparatorPosition]; } return category; diff --git a/src/KurrentDB.Projections.Core/Standard/StreamDeletedHelper.cs b/src/KurrentDB.Projections.Core/Standard/StreamDeletedHelper.cs index 6bff39aac30..b5df54c3429 100644 --- a/src/KurrentDB.Projections.Core/Standard/StreamDeletedHelper.cs +++ b/src/KurrentDB.Projections.Core/Standard/StreamDeletedHelper.cs @@ -39,7 +39,7 @@ public static bool IsStreamDeletedEvent( bool isMetaStream; if (SystemStreams.IsMetastream(streamOrMetaStreamId)) { isMetaStream = true; - deletedPartitionStreamId = streamOrMetaStreamId.Substring("$$".Length); + deletedPartitionStreamId = streamOrMetaStreamId["$$".Length..]; } else { isMetaStream = false; deletedPartitionStreamId = streamOrMetaStreamId; diff --git a/src/KurrentDB.SecondaryIndexing.Tests/IntegrationTests/ReadTests.cs b/src/KurrentDB.SecondaryIndexing.Tests/IntegrationTests/ReadTests.cs index 57157e5c167..51b45b64607 100644 --- a/src/KurrentDB.SecondaryIndexing.Tests/IntegrationTests/ReadTests.cs +++ b/src/KurrentDB.SecondaryIndexing.Tests/IntegrationTests/ReadTests.cs @@ -7,7 +7,6 @@ using KurrentDB.Core.Services.Storage.ReaderIndex; using KurrentDB.Core.Services.Transport.Common; using KurrentDB.Core.Services.Transport.Enumerators; -using KurrentDB.SecondaryIndexing.Indexes; using KurrentDB.SecondaryIndexing.Indexes.Category; using KurrentDB.SecondaryIndexing.Indexes.Default; using KurrentDB.SecondaryIndexing.Indexes.EventType;