From cdbee49f14f9aa4d3eb97f922a0a02e067f0fbe8 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 12 Sep 2026 04:28:12 -0400 Subject: [PATCH 1/4] fix(transport): preserve behavior across the gRPC boundary Signed-off-by: Yordis Prieto --- .../workflows/build-container-ubuntu-lts.yml | 3 - .gitignore | 2 - Dockerfile | 6 +- docker-compose.yml | 6 +- docs/README.md | 7 +- docs/admin-ui.md | 5 +- docs/architecture.md | 10 + docs/cluster.md | 17 +- docs/diagnostics/README.md | 13 +- docs/installation.md | 3 + docs/networking.md | 160 +- docs/security.md | 34 +- docs/upgrade-guide.md | 37 +- docs/whatsnew.md | 8 +- proto.lock | 1632 +---------------- samples/server/docker-compose-cluster.yaml | 10 +- scripts/test.sh | 50 +- shared.env | 1 - src/Directory.Packages.props | 4 +- .../Components/Pages/Cluster.razor | 27 +- .../Components/Pages/Observability.razor | 209 ++- .../Services/ClusterStatusService.cs | 11 +- .../Services/NodeConnectionTracker.cs | 278 +++ .../Services/QueueDashboardService.cs | 231 +-- src/EventStore.ClusterNode/Program.cs | 33 +- src/EventStore.ClusterNode/metricsconfig.json | 4 - .../ui-assets/js/queue-dashboard.js | 278 +-- .../Authorization/LegacyPolicyVerification.cs | 1 - .../concurrent_queue_wrapper_should.cs | 115 -- .../MiniNodeWithExistingRecords.cs | 181 -- ...th_event_numbers_greater_than_2_billion.cs | 55 - ...th_event_numbers_greater_than_2_billion.cs | 63 - ...th_event_numbers_greater_than_2_billion.cs | 80 - ...th_event_numbers_greater_than_2_billion.cs | 124 -- ..._event_number_greater_than_int_maxvalue.cs | 90 - ..._event_number_greater_than_int_maxvalue.cs | 64 - ...th_event_numbers_greater_than_2_billion.cs | 115 -- ...th_event_numbers_greater_than_2_billion.cs | 59 - .../ClientAPI/Helpers/EventDataComparer.cs | 46 - .../ClientAPI/Helpers/EventsStream.cs | 25 - .../ClientAPI/Helpers/TcpType.cs | 7 - .../ClientAPI/Helpers/TestConnection.cs | 72 - .../Helpers/TestConnectionLifecycle.cs | 77 - .../ClientAPI/Helpers/TestEvent.cs | 22 - .../ClientAPI/Helpers/Writer.cs | 98 - .../Security/AuthenticationTestBase.cs | 268 --- .../all_stream_with_no_acl_security.cs | 87 - ...authorized_default_credentials_security.cs | 100 - .../Security/delete_stream_security.cs | 220 --- .../Security/multiple_role_security.cs | 45 - .../overriden_system_stream_security.cs | 138 -- ...verriden_system_stream_security_for_all.cs | 117 -- .../overriden_user_stream_security.cs | 140 -- .../ClientAPI/Security/read_all_security.cs | 45 - .../Security/read_stream_meta_security.cs | 93 - .../Security/read_stream_security.cs | 122 -- .../Security/stream_security_inheritance.cs | 126 -- .../Security/subscribe_to_all_security.cs | 40 - .../Security/subscribe_to_stream_security.cs | 93 - .../Security/system_stream_security.cs | 282 --- .../transactional_write_stream_security.cs | 181 -- .../Security/write_stream_meta_security.cs | 94 - .../Security/write_stream_security.cs | 101 - .../SpecificationWithLinkToToDeletedEvents.cs | 29 - ...cationWithLinkToToMaxCountDeletedEvents.cs | 33 - .../ClientAPI/SpecificationWithMiniNode.cs | 102 -- .../SystemData/clientapi_tcp_package.cs | 199 -- .../ClientAPI/UserManagement/TestWithNode.cs | 77 - .../ClientAPI/append_to_stream.cs | 798 -------- .../append_to_stream_with_hash_collision.cs | 86 - .../appending_to_implicitly_created_stream.cs | 323 ---- ...icitly_created_stream_using_transaction.cs | 251 --- .../appending_to_streams_across_restart.cs | 187 -- .../catch_up_subscription_handles_errors.cs | 753 -------- .../catchup_filtered_subscription.cs | 358 ---- ..._subscription_handles_small_batch_sizes.cs | 106 -- .../ClientAPI/connect.cs | 166 -- ...connecting_to_a_persistent_subscription.cs | 980 ---------- ...ting_to_a_persistent_subscription_async.cs | 412 ----- .../connecting_with_connection_string.cs | 68 - .../ClientAPI/connection_string.cs | 69 - .../create_persistent_subscription.cs | 305 --- .../deleting_persistent_subscription.cs | 173 -- .../ClientAPI/deleting_stream.cs | 106 -- .../event_store_connection_should.cs | 114 -- .../ClientAPI/isjson_flag_on_event.cs | 94 - .../persistent_connect_integration_tests.cs | 487 ----- ...ead_all_events_backward_filtered_should.cs | 97 - .../read_all_events_backward_should.cs | 95 - .../read_all_events_filtered_paging_should.cs | 150 -- ...read_all_events_forward_filtered_should.cs | 108 -- .../read_all_events_forward_should.cs | 94 - ...forward_with_hard_deleted_stream_should.cs | 57 - ...ts_forward_with_linkto_passed_max_count.cs | 24 - ...ts_forward_with_linkto_to_deleted_event.cs | 41 - ...forward_with_soft_deleted_stream_should.cs | 59 - ...ents_backward_with_linkto_deleted_event.cs | 71 - .../ClientAPI/read_event_should.cs | 122 -- .../read_event_stream_backward_should.cs | 243 --- .../read_event_stream_forward_should.cs | 219 --- .../read_event_with_hash_collision.cs | 82 - ...ion_when_stream_name_contains_at_symbol.cs | 48 - ...eam_events_backward_with_hash_collision.cs | 84 - ...ream_events_forward_with_hash_collision.cs | 83 - ...ad_stream_events_with_unresolved_linkto.cs | 54 - .../ClientAPI/soft_delete.cs | 385 ---- .../ClientAPI/subscribe_should.cs | 129 -- .../subscribe_to_all_catching_up_should.cs | 325 ---- .../subscribe_to_all_filtered_should.cs | 388 ---- .../ClientAPI/subscribe_to_all_should.cs | 119 -- .../subscribe_to_stream_catching_up_should.cs | 361 ---- .../ClientAPI/transaction.cs | 282 --- .../update_persistent_subscription.cs | 140 -- .../when_committing_empty_transaction.cs | 126 -- .../when_having_max_count_set_for_stream.cs | 136 -- ...en_having_truncatebefore_set_for_stream.cs | 379 ---- .../ClientAPI/when_working_with_metadata.cs | 60 - ...king_with_stream_metadata_as_byte_array.cs | 182 -- ...with_stream_metadata_as_structured_info.cs | 384 ---- .../specification_with_bare_vnode.cs | 6 +- ...when_committing_a_transaction_with_data.cs | 2 +- .../Cluster/MemberInfoTests.cs | 13 +- src/EventStore.Core.Tests/DefaultData.cs | 2 - .../EventStore.Core.Tests.csproj | 4 - .../Helpers/ClientApiLoggerBridge.cs | 92 - .../Helpers/MiniClusterNode.cs | 43 +- src/EventStore.Core.Tests/Helpers/MiniNode.cs | 57 +- .../Helpers/TestCertificates.cs | 66 + .../Helpers/TestFixtureWithExistingEvents.cs | 2 +- .../when_performing_a_live_check.cs | 8 - .../when_archiving_and_restoring_a_cluster.cs | 64 +- ...enticated_requests_made_from_a_follower.cs | 36 - .../specification_with_a_single_node.cs | 3 - .../Integration/specification_with_cluster.cs | 40 +- ...single_node_is_restarted_multiple_times.cs | 1 - .../when_cluster_nodes_are_restarted.cs | 2 +- ...node_becomes_leader_with_unindexed_data.cs | 370 ---- .../Regression/GrpcOnlySurfaceParityTests.cs | 81 + .../ClusterSettingsFactory.cs | 8 +- .../ElectionsService/ClusterVNodeSettings.cs | 12 +- .../ElectionsService/ElectionServiceUnit.cs | 22 +- .../ElectionsService/ElectionsServiceTests.cs | 46 +- .../LeaderNode/ElectionsServiceUnitTests.cs | 4 +- .../Randomized/RandomizedElectionsTestCase.cs | 5 +- .../Randomized/UpdateGossipProcessor.cs | 4 +- ...wn_when_started_and_set_full_imediately.cs | 4 +- ...nown_when_started_and_set_to_full_later.cs | 4 +- .../GossipService/NodeGossipServiceTests.cs | 25 +- .../PersistentSubscriptionTests.cs | 56 +- ...bscribes.cs => when_replica_subscribes.cs} | 2 +- .../LogReplication/LogReplicationFixture.cs | 5 - .../connecting_to_read_only_replica.cs | 102 +- .../GrpcRequestForwardingServiceTests.cs | 5 - ...RequestForwardingTransportSecurityTests.cs | 7 +- .../RequestForwardingServiceTests.cs | 6 +- .../RequestManagerServiceSpecification.cs | 2 +- .../when_writing_and_deposed_as_leader.cs | 6 +- ...sed_as_leader_and_replica_moves_forward.cs | 6 +- ...hen_reading_all_with_disallowed_streams.cs | 41 +- .../when_reading_all_with_filtering.cs | 41 +- .../HashCollisions/with_hash_collisions.cs | 2 +- ...nning_a_scavenge_from_storage_scavenger.cs | 74 +- ...erator.AllSubscription.CombinationTests.cs | 35 +- ...llSubscriptionFiltered.CombinationTests.cs | 54 +- .../Enumerator.CombinationTests.cs | 153 +- ...tor.StreamSubscription.CombinationTests.cs | 24 +- .../Forwarding/ForwardingGrpcCodecTests.cs | 28 +- .../Grpc/MonitoringTests/TcpStatsTests.cs | 136 -- .../GrpcReplicaServiceSupervisorTests.cs | 5 - .../Grpc/StreamsTests/DeleteTests.cs | 3 +- .../StreamsTests/ReadStreamsForwardTests.cs | 5 +- .../Http/Authorization/authorization_tests.cs | 8 - .../Transport/Tcp/TcpClientDispatcherTests.cs | 317 ---- .../Tcp/TcpConnectionManagerTests.cs | 348 ---- .../Transport/Tcp/TcpConnectionSslTests.cs | 176 -- .../Transport/Tcp/TcpConnectionTests.cs | 156 -- .../Transport/Tcp/core_tcp_package.cs | 199 -- .../Services/Transport/Tcp/ssl_connection.cs | 174 -- .../Tcp/ssl_connections_mutual_auth.cs | 149 -- .../Transport/Tcp/test_certificates/ca/ca.crt | 33 - .../Transport/Tcp/test_certificates/ca/ca.key | 52 - .../Tcp/test_certificates/ca_untrusted/ca.crt | 34 - .../Tcp/test_certificates/ca_untrusted/ca.key | 52 - .../Tcp/test_certificates/generate_ca.sh | 2 - .../Tcp/test_certificates/generate_cert.sh | 51 - .../Tcp/test_certificates/node1/node1.crt | 27 - .../Tcp/test_certificates/node1/node1.key | 27 - .../Tcp/test_certificates/node2/node2.crt | 27 - .../Tcp/test_certificates/node2/node2.key | 27 - .../Tcp/test_certificates/node3/node3.crt | 27 - .../Tcp/test_certificates/node3/node3.key | 27 - .../test_certificates/untrusted/untrusted.crt | 28 - .../test_certificates/untrusted/untrusted.key | 28 - .../Tcp/when_invalid_data_is_sent_over_tcp.cs | 90 - .../Tcp/with_intermediate_certificates.cs | 156 -- .../user_management_service.cs | 2 +- .../InaugurationManagerTests.cs | 3 +- .../Services/VNode/ShutdownServiceTests.cs | 5 - .../Services/VNode/leader_info_provider.cs | 291 +-- .../Services/VNode/startup_should.cs | 23 +- .../PublicTcpApiTestService.cs | 123 -- .../TcpApiTestPlugin/TcpApiTestOptions.cs | 12 - .../TcpApiTestPlugin/TcpApiTestPlugin.cs | 38 - .../Truncation/when_truncating_database.cs | 13 +- .../Transforms/TransformTests.cs | 87 +- src/EventStore.Core.Tests/copying_metadata.cs | 78 - .../ClusterVNodeOptionsScenarios.cs | 10 +- .../with_cluster_node_and_custom_settings.cs | 37 +- .../when_building/with_default_settings.cs | 16 +- .../with_single_node_and_custom_settings.cs | 28 +- .../{with_secure_tcp.cs => with_tls.cs} | 46 +- ...hutting_down_an_isolated_cluster_member.cs | 6 +- .../Metrics/ElectionsCounterTrackerTests.cs | 3 +- .../InMemory/GossipListenerServiceTests.cs | 5 - .../Telemetry/TelemetryServiceTests.cs | 5 - .../LegacyPolicySelectorFactory.cs | 1 - .../Cluster/ClientClusterInfo.cs | 29 - src/EventStore.Core/Cluster/ClusterInfo.cs | 35 +- src/EventStore.Core/Cluster/MemberInfo.cs | 95 +- src/EventStore.Core/ClusterVNode.cs | 121 +- .../Configuration/ClusterVNodeOptions.cs | 30 +- .../ClusterVNodeOptionsExtensions.cs | 40 - .../ClusterVNodeOptionsValidator.cs | 5 - .../Data/GossipAdvertiseInfo.cs | 33 +- src/EventStore.Core/Data/VNodeInfo.cs | 32 +- src/EventStore.Core/EventStore.Core.csproj | 7 - .../Helpers/IAsyncMessageFramer.cs | 16 + .../Helpers/LengthPrefixSuffixFramer.cs | 3 +- .../Helpers/MessageFramingException.cs | 10 + src/EventStore.Core/Messages/ClientMessage.cs | 19 +- .../Messages/ClusterInfoDto.cs | 32 - src/EventStore.Core/Messages/MemberInfoDto.cs | 95 - .../Messages/MonitoringMessage.cs | 38 - .../Messages/TcpClientMessageDto.cs | 670 ------- src/EventStore.Core/Messages/TcpMessage.cs | 127 -- src/EventStore.Core/Messaging/CoreMessage.cs | 1 - src/EventStore.Core/NodeTcpOptions.cs | 9 - .../Services/AuthorizationGateway.cs | 4 +- .../Services/ElectionsService.cs | 4 +- .../Services/Gossip/GossipServiceBase.cs | 8 +- .../Services/Gossip/NodeGossipService.cs | 5 - .../Services/Monitoring/MonitoringService.cs | 92 +- .../Services/Monitoring/SystemStatsHelper.cs | 14 - .../PersistentSubscriptionService.cs | 19 - .../Services/Replication/LogRecordFramer.cs | 2 +- .../Services/Replication/TransactionFramer.cs | 2 +- .../GrpcRequestForwardingSupervisor.cs | 2 +- .../Services/RequestForwardingService.cs | 6 +- .../Storage/ReaderIndex/EventFilter.cs | 67 +- .../Services/SubscriptionsService.cs | 36 - .../Services/TcpSendService.cs | 14 - .../Grpc/Forwarding/ForwardingGrpcCodec.cs | 10 +- .../Grpc/Forwarding/ForwardingService.cs | 2 +- .../Services/Transport/Grpc/Monitoring.cs | 37 - .../Transport/Tcp/ClientTcpDispatcher.cs | 600 ------ .../Transport/Tcp/ClientWriteTcpDispatcher.cs | 439 ----- .../Services/Transport/Tcp/ITcpDispatcher.cs | 14 - .../Transport/Tcp/ProtobufExtensions.cs | 112 -- .../Transport/Tcp/SendOverTcpEnvelope.cs | 29 - .../Services/Transport/Tcp/TcpCommand.cs | 73 - .../Transport/Tcp/TcpConnectionManager.cs | 682 ------- .../Services/Transport/Tcp/TcpDispatcher.cs | 134 -- .../Services/Transport/Tcp/TcpPackage.cs | 236 --- .../Services/Transport/Tcp/TcpService.cs | 160 -- .../Tcp/UnableToAcquireStreamException.cs | 8 - .../Services/VNode/ClusterVNodeController.cs | 33 +- .../Services/VNode/LeaderInfoProvider.cs | 29 +- src/EventStore.Core/Settings/VNodeSettings.cs | 105 -- src/EventStore.Core/Util/Opts.cs | 9 - ...tion_with_standard_projections_runnning.cs | 357 ---- .../ClientAPI/RecordedEventExtensions.cs | 10 - .../ClientAPI/event_by_type_index.cs | 83 - .../when_reverting_after_index_catches_up.cs | 57 - .../ClientAPI/list_projections.cs | 39 - ...tion_with_standard_projections_runnning.cs | 325 ---- .../when_running_and_events_are_posted.cs | 45 - .../when_running_and_events_are_indexed.cs | 45 - .../when_running_and_events_are_indexed.cs | 46 - ...d_a_stream_gets_deleted_before_recovery.cs | 52 - .../when_running_and_events_are_posted.cs | 51 - .../when_running_and_events_are_posted.cs | 45 - ...ed_but_a_stream_and_tombstone_postponed.cs | 48 - ...ing_and_events_are_posted_but_tombstone.cs | 44 - ...d_events_are_posted_including_tombstone.cs | 48 - ...other_events_tombstone_ant_other_events.cs | 53 - .../when_running_and_events_are_indexed.cs | 57 - ..._and_events_get_indexed_before_recovery.cs | 58 - .../when_running_and_events_are_indexed.cs | 60 - ..._are_indexed_but_a_stream_and_tombstone.cs | 60 - ...e_indexed_but_more_events_and_tombstone.cs | 61 - ...ng_and_events_are_indexed_but_tombstone.cs | 60 - .../when_running_and_no_indexing.cs | 46 - ...unning_and_no_indexing_and_other_events.cs | 52 - .../when_running_and_events_are_indexed.cs | 61 - ..._and_events_get_indexed_before_recovery.cs | 57 - .../when_running_and_events_are_indexed.cs | 48 - ...ed_but_a_stream_and_tombstone_postponed.cs | 64 - ...ng_and_events_are_indexed_but_tombstone.cs | 48 - ..._events_are_indexed_including_tombstone.cs | 51 - ...other_events_tombstone_ant_other_events.cs | 57 - .../with_standard_projections_running.cs | 125 -- .../EventStore.Projections.Core.Tests.csproj | 2 - .../Playground/Launchpad.cs | 86 - .../Playground/Launchpad2.cs | 68 - ...tionWithEmittedStreamsTrackerAndDeleter.cs | 123 +- ...with_an_existing_emitted_streams_stream.cs | 35 +- .../with_multiple_tracked_streams.cs | 38 +- .../when_tracking/with_tracking_disabled.cs | 16 +- .../when_tracking/with_tracking_enabled.cs | 22 +- ...ng_enabled_with_duplicate_event_streams.cs | 22 +- .../include_everything_event_filter.cs | 2 +- ...ling_deleted_notifications_event_filter.cs | 2 +- ...ificationWithNodeAndProjectionSubsystem.cs | 72 +- .../when_deleting_a_system_projection.cs | 2 +- .../projections_system/when_starting_up.cs | 6 +- .../Services/Management/ManagedProjection.cs | 2 +- .../Emitting/EmittedStreamsDeleter.cs | 4 +- .../CommandInventoryTests.cs | 37 + .../EventStore.TestClient.Tests.csproj | 19 + src/EventStore.TestClient/Client.cs | 67 +- .../ClientApiLoggerBridge.cs | 92 - .../WriteFloodProcessor.cs | 267 --- .../ClientApiTcpTestClient.cs | 44 - src/EventStore.TestClient/ClientOptions.cs | 10 - .../CommandProcessorContext.cs | 11 +- .../Commands/DeleteProcessor.cs | 89 - .../DvuBasic/BankAccountBasicProducer.cs | 292 --- .../DvuBasic/BankAccountEventFactory.cs | 51 - .../Commands/DvuBasic/DvuBasicProcessor.cs | 491 ----- .../Commands/DvuBasic/IBasicProducer.cs | 12 - .../Commands/DvuBasic/Status.cs | 95 - .../Commands/DvuBasic/StreamNamesGenerator.cs | 16 - .../Commands/MultiWriteFloodWaiting.cs | 168 -- .../Commands/MultiWriteProcessor.cs | 106 -- .../Commands/PingFloodProcessor.cs | 127 -- .../Commands/PingFloodWaitingProcessor.cs | 116 -- .../Commands/PingProcessor.cs | 49 - .../Commands/ReadAllProcessor.cs | 123 -- .../Commands/ReadFloodProcessor.cs | 204 --- .../Commands/ReadProcessor.cs | 108 -- .../Commands/RequestMonitor.cs | 4 - .../RunTestScenarios/BankAccountEvent.cs | 25 - .../Commands/RunTestScenarios/IScenario.cs | 9 - .../RunTestScenarios/JsonEventContainer.cs | 24 - .../LoopingProjTranWriteScenario.cs | 269 --- .../LoopingProjectionKillScenario.cs | 142 -- .../RunTestScenarios/LoopingScenario.cs | 201 -- .../MassProjectionsScenario.cs | 219 --- .../RunTestScenarios/NodeConnectionInfo.cs | 22 - .../ProjForeachForcedCommonNameScenario.cs | 286 --- .../ProjectionWrongTagCheck.cs | 151 -- .../ProjectionsKillScenario.cs | 192 -- .../ProjectionsScenarioBase.cs | 212 --- .../Commands/RunTestScenarios/ScenarioBase.cs | 705 ------- .../Commands/RunTestScenarios/TestEvent.cs | 57 - .../Commands/RunTestScenarios/WriteMode.cs | 8 - .../Commands/RunTestScenariosProcessor.cs | 280 --- .../Commands/ScavengeProcessor.cs | 43 - .../Commands/SubscribeToStreamProcessor.cs | 107 -- .../SubscriptionStressTestProcessor.cs | 74 - .../Commands/TcpSanitazationCheckProcessor.cs | 135 -- .../Commands/TestClientJson.cs | 37 - .../Commands/TransactionWriteProcessor.cs | 199 -- .../Commands/WriteFloodClientApiProcessor.cs | 182 -- .../Commands/WriteFloodProcessor.cs | 264 --- .../Commands/WriteFloodWaitingProcessor.cs | 167 -- .../Commands/WriteJsonProcessor.cs | 138 -- .../Commands/WriteLongTermProcessor.cs | 253 --- .../Commands/WriteProcessor.cs | 119 -- src/EventStore.TestClient/ConsoleTable.cs | 71 - .../EventStore.TestClient.csproj | 8 +- .../GrpcCommands/CompatibilityProcessors.cs | 628 +++++++ src/EventStore.TestClient/GrpcTestClient.cs | 12 +- src/EventStore.TestClient/PortsHelper.cs | 180 -- src/EventStore.TestClient/Program.cs | 18 +- src/EventStore.TestClient/TcpTestClient.cs | 205 --- .../EventStore.Transport.Tcp.csproj | 10 - .../Formatting/FormatterBase.cs | 92 - .../Formatting/IMessageFormatter.cs | 49 - .../Formatting/RawMessageFormatter.cs | 91 - .../Framing/IMessageFramer.cs | 32 - .../Framing/LengthPrefixMessageFramer.cs | 129 -- ...LengthPrefixMessageFramerWithBufferPool.cs | 126 -- .../Framing/PackageFramingException.cs | 11 - src/EventStore.Transport.Tcp/Helper.cs | 31 - .../IMonitoredTcpConnection.cs | 25 - .../ITcpConnection.cs | 26 - .../SocketArgsPool.cs | 52 - .../TcpClientConnector.cs | 241 --- .../TcpConfiguration.cs | 16 - src/EventStore.Transport.Tcp/TcpConnection.cs | 563 ------ .../TcpConnectionBase.cs | 240 --- .../TcpConnectionMonitor.cs | 213 --- .../TcpConnectionSsl.cs | 763 -------- .../TcpServerListener.cs | 139 -- src/EventStore.Transport.Tcp/TcpStats.cs | 76 - .../TcpTypedConnection.cs | 122 -- src/EventStore.sln | 17 +- src/Protos/ClientAPI/ClientMessageDtos.proto | 450 ----- src/Protos/Grpc/cluster.proto | 13 +- src/Protos/Grpc/forwarding.proto | 4 +- src/Protos/Grpc/monitoring.proto | 21 - 402 files changed, 2616 insertions(+), 39364 deletions(-) create mode 100644 src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/DataStructures/concurrent_queue_wrapper_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/MiniNodeWithExistingRecords.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/append_to_stream_with_event_numbers_greater_than_2_billion.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/catchup_subscription_to_all_with_event_numbers_greater_than_2_billion.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/persistent_subscription_with_event_numbers_greater_than_2_billion.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_event_numbers_greater_than_2_billion.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscribe_to_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscriptions_on_stream_with_event_numbers_greater_than_2_billion.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/transactions_on_stream_with_event_numbers_greater_than_2_billion.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/AuthenticationTestBase.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/all_stream_with_no_acl_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/authorized_default_credentials_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/delete_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/multiple_role_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security_for_all.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/overriden_user_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/read_all_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/read_stream_meta_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/read_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/stream_security_inheritance.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_all_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/system_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/transactional_write_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/write_stream_meta_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/Security/write_stream_security.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToDeletedEvents.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToMaxCountDeletedEvents.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/SystemData/clientapi_tcp_package.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/UserManagement/TestWithNode.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/append_to_stream.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/append_to_stream_with_hash_collision.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream_using_transaction.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/appending_to_streams_across_restart.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/catch_up_subscription_handles_errors.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/catchup_filtered_subscription.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/catchup_subscription_handles_small_batch_sizes.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/connect.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription_async.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/connecting_with_connection_string.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/connection_string.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/create_persistent_subscription.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/deleting_persistent_subscription.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/deleting_stream.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/event_store_connection_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/isjson_flag_on_event.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/persistent_connect_integration_tests.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_filtered_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_filtered_paging_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_filtered_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_hard_deleted_stream_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_passed_max_count.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_to_deleted_event.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_soft_deleted_stream_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_allevents_backward_with_linkto_deleted_event.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_event_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_event_stream_backward_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_event_stream_forward_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_event_with_hash_collision.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_from_persistent_subscription_with_link_resolution_when_stream_name_contains_at_symbol.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_stream_events_backward_with_hash_collision.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_stream_events_forward_with_hash_collision.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/read_stream_events_with_unresolved_linkto.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/soft_delete.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/subscribe_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_catching_up_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_filtered_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/subscribe_to_stream_catching_up_should.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/transaction.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/update_persistent_subscription.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/when_committing_empty_transaction.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/when_having_max_count_set_for_stream.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/when_having_truncatebefore_set_for_stream.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/when_working_with_metadata.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_byte_array.cs delete mode 100644 src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_structured_info.cs delete mode 100644 src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs create mode 100644 src/EventStore.Core.Tests/Helpers/TestCertificates.cs delete mode 100644 src/EventStore.Core.Tests/Integration/when_node_becomes_leader_with_unindexed_data.cs create mode 100644 src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs rename src/EventStore.Core.Tests/Services/Replication/LeaderReplication/{when_non_tcp_replica_subscribes.cs => when_replica_subscribes.cs} (96%) delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Grpc/MonitoringTests/TcpStatsTests.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/TcpClientDispatcherTests.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionManagerTests.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionSslTests.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionTests.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.crt delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.key delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.crt delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.key delete mode 100755 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_ca.sh delete mode 100755 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_cert.sh delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.crt delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.key delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.crt delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.key delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.crt delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.key delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.crt delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.key delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/when_invalid_data_is_sent_over_tcp.cs delete mode 100644 src/EventStore.Core.Tests/Services/Transport/Tcp/with_intermediate_certificates.cs delete mode 100644 src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs delete mode 100644 src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs delete mode 100644 src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs delete mode 100644 src/EventStore.Core.Tests/copying_metadata.cs rename src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/{with_secure_tcp.cs => with_tls.cs} (70%) create mode 100644 src/EventStore.Core/Helpers/IAsyncMessageFramer.cs create mode 100644 src/EventStore.Core/Helpers/MessageFramingException.cs delete mode 100644 src/EventStore.Core/Messages/ClusterInfoDto.cs delete mode 100644 src/EventStore.Core/Messages/MemberInfoDto.cs delete mode 100755 src/EventStore.Core/Messages/TcpClientMessageDto.cs delete mode 100644 src/EventStore.Core/Messages/TcpMessage.cs delete mode 100644 src/EventStore.Core/NodeTcpOptions.cs delete mode 100644 src/EventStore.Core/Services/TcpSendService.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/ClientTcpDispatcher.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/ClientWriteTcpDispatcher.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/ITcpDispatcher.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/ProtobufExtensions.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/SendOverTcpEnvelope.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/TcpCommand.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/TcpConnectionManager.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/TcpDispatcher.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/TcpPackage.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/TcpService.cs delete mode 100644 src/EventStore.Core/Services/Transport/Tcp/UnableToAcquireStreamException.cs delete mode 100644 src/EventStore.Core/Settings/VNodeSettings.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/Cluster/specification_with_standard_projections_runnning.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/RecordedEventExtensions.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index/when_reverting_after_index_catches_up.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/list_projections.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/specification_with_standard_projections_runnning.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_a_stream_gets_deleted_before_recovery.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_events_are_posted.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_a_stream_and_tombstone_postponed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_including_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_are_indexed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_more_events_and_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing_and_other_events.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_are_indexed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone_postponed.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_including_tombstone.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs delete mode 100644 src/EventStore.Projections.Core.Tests/ClientAPI/with_standard_projections_running.cs delete mode 100644 src/EventStore.Projections.Core.Tests/Playground/Launchpad.cs delete mode 100644 src/EventStore.Projections.Core.Tests/Playground/Launchpad2.cs create mode 100644 src/EventStore.TestClient.Tests/CommandInventoryTests.cs create mode 100644 src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj delete mode 100644 src/EventStore.TestClient/ClientApiLoggerBridge.cs delete mode 100644 src/EventStore.TestClient/ClientApiTcpCommands/WriteFloodProcessor.cs delete mode 100644 src/EventStore.TestClient/ClientApiTcpTestClient.cs delete mode 100644 src/EventStore.TestClient/Commands/DeleteProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/DvuBasic/BankAccountBasicProducer.cs delete mode 100644 src/EventStore.TestClient/Commands/DvuBasic/BankAccountEventFactory.cs delete mode 100644 src/EventStore.TestClient/Commands/DvuBasic/DvuBasicProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/DvuBasic/IBasicProducer.cs delete mode 100644 src/EventStore.TestClient/Commands/DvuBasic/Status.cs delete mode 100644 src/EventStore.TestClient/Commands/DvuBasic/StreamNamesGenerator.cs delete mode 100644 src/EventStore.TestClient/Commands/MultiWriteFloodWaiting.cs delete mode 100644 src/EventStore.TestClient/Commands/MultiWriteProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/PingFloodProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/PingFloodWaitingProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/PingProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/ReadAllProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/ReadFloodProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/ReadProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/BankAccountEvent.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/IScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/JsonEventContainer.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjTranWriteScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjectionKillScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/LoopingScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/MassProjectionsScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/NodeConnectionInfo.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/ProjForeachForcedCommonNameScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionWrongTagCheck.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsKillScenario.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsScenarioBase.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/ScenarioBase.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/TestEvent.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenarios/WriteMode.cs delete mode 100644 src/EventStore.TestClient/Commands/RunTestScenariosProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/ScavengeProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/SubscribeToStreamProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/SubscriptionStressTestProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/TcpSanitazationCheckProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/TestClientJson.cs delete mode 100644 src/EventStore.TestClient/Commands/TransactionWriteProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/WriteFloodClientApiProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/WriteFloodProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/WriteFloodWaitingProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/WriteJsonProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/WriteLongTermProcessor.cs delete mode 100644 src/EventStore.TestClient/Commands/WriteProcessor.cs delete mode 100644 src/EventStore.TestClient/ConsoleTable.cs create mode 100644 src/EventStore.TestClient/GrpcCommands/CompatibilityProcessors.cs delete mode 100644 src/EventStore.TestClient/PortsHelper.cs delete mode 100644 src/EventStore.TestClient/TcpTestClient.cs delete mode 100644 src/EventStore.Transport.Tcp/EventStore.Transport.Tcp.csproj delete mode 100644 src/EventStore.Transport.Tcp/Formatting/FormatterBase.cs delete mode 100644 src/EventStore.Transport.Tcp/Formatting/IMessageFormatter.cs delete mode 100644 src/EventStore.Transport.Tcp/Formatting/RawMessageFormatter.cs delete mode 100644 src/EventStore.Transport.Tcp/Framing/IMessageFramer.cs delete mode 100644 src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramer.cs delete mode 100644 src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramerWithBufferPool.cs delete mode 100644 src/EventStore.Transport.Tcp/Framing/PackageFramingException.cs delete mode 100644 src/EventStore.Transport.Tcp/Helper.cs delete mode 100644 src/EventStore.Transport.Tcp/IMonitoredTcpConnection.cs delete mode 100644 src/EventStore.Transport.Tcp/ITcpConnection.cs delete mode 100644 src/EventStore.Transport.Tcp/SocketArgsPool.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpClientConnector.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpConfiguration.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpConnection.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpConnectionBase.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpConnectionMonitor.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpConnectionSsl.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpServerListener.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpStats.cs delete mode 100644 src/EventStore.Transport.Tcp/TcpTypedConnection.cs delete mode 100755 src/Protos/ClientAPI/ClientMessageDtos.proto diff --git a/.github/workflows/build-container-ubuntu-lts.yml b/.github/workflows/build-container-ubuntu-lts.yml index 91ffc103fb..dbb6ccb855 100644 --- a/.github/workflows/build-container-ubuntu-lts.yml +++ b/.github/workflows/build-container-ubuntu-lts.yml @@ -35,9 +35,6 @@ jobs: fail-fast: true matrix: include: - - test-group-name: core-clientapi-persistent - - test-group-name: core-clientapi-security - - test-group-name: core-clientapi-streams - test-group-name: core-http - test-group-name: core-services - test-group-name: core-cluster-services diff --git a/.gitignore b/.gitignore index eb5a6fc474..a427c779b6 100644 --- a/.gitignore +++ b/.gitignore @@ -33,8 +33,6 @@ ipch/ .DS_Store src/EventStore/EventStore.Common/Properties/AssemblyVersion.cs -src/EventStore/EventStore.ClientAPI/Properties/AssemblyVersion.cs - *.o *.ii *.s diff --git a/Dockerfile b/Dockerfile index 064281c153..2e2d9dd03e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,7 +42,6 @@ FROM mcr.microsoft.com/dotnet/sdk:10.0-${CONTAINER_RUNTIME} AS test WORKDIR /build COPY --from=build ./build/published-tests ./published-tests COPY --from=build ./build/ci ./ci -COPY --from=build ./build/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.crt /usr/local/share/ca-certificates/ca_eventstore_test.crt COPY ./scripts/test.sh /build/test.sh RUN mkdir ./test-results RUN chmod +x /build/test.sh @@ -84,12 +83,11 @@ RUN mkdir -p /var/lib/eventstore && \ USER eventstore -RUN printf "NodeIp: 0.0.0.0\n\ -ReplicationIp: 0.0.0.0" >> /etc/eventstore/eventstore.conf +RUN printf "NodeIp: 0.0.0.0" >> /etc/eventstore/eventstore.conf VOLUME /var/lib/eventstore /var/log/eventstore -EXPOSE 1112/tcp 1113/tcp 2113/tcp +EXPOSE 2113/tcp HEALTHCHECK --interval=5s --timeout=5s --retries=24 \ CMD curl --fail --insecure https://localhost:2113/-/liveness || curl --fail http://localhost:2113/-/liveness || exit 1 diff --git a/docker-compose.yml b/docker-compose.yml index 0896f11148..4c7b8f72ef 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - shared.env environment: - EVENTSTORE_GOSSIP_SEED=172.30.240.12:2113,172.30.240.13:2113 - - EVENTSTORE_REPLICATION_IP=172.30.240.11 + - EVENTSTORE_NODE_IP=172.30.240.11 - EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node/node.crt - EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node/node.key - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=127.0.0.1 @@ -44,7 +44,7 @@ services: - shared.env environment: - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.13:2113 - - EVENTSTORE_REPLICATION_IP=172.30.240.12 + - EVENTSTORE_NODE_IP=172.30.240.12 - EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node/node.crt - EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node/node.key - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=127.0.0.1 @@ -71,7 +71,7 @@ services: - shared.env environment: - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.12:2113 - - EVENTSTORE_REPLICATION_IP=172.30.240.13 + - EVENTSTORE_NODE_IP=172.30.240.13 - EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node/node.crt - EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node/node.key - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=127.0.0.1 diff --git a/docs/README.md b/docs/README.md index b2063b2c6b..3d39f74235 100644 --- a/docs/README.md +++ b/docs/README.md @@ -10,9 +10,12 @@ surfaces for running a node or cluster. TrogonEventStore keeps the database node focused on the durable event log: -- Application event access is gRPC-first. +- Database client APIs, cluster replication, and follower-to-leader forwarding + use gRPC over the node HTTP(S) endpoint. - HTTP is reserved for the Admin UI, health probes, metrics, and other infrastructure-level concerns. +- The server does not open a separate legacy EventStore TCP protocol listener + or support its TCP transport configuration. - The project is FOSS-only. The documentation does not describe unsupported proprietary server features. - Rich read models, user-defined query engines, connector runtimes, and @@ -37,7 +40,7 @@ For a production node, review: ## Protocols and clients -The supported application protocol is gRPC. Existing TrogonEventStore-compatible +The supported database protocol is gRPC. Existing TrogonEventStore-compatible gRPC clients can be useful while the TrogonDB client libraries continue to evolve, but the server documentation should be treated as authoritative for this repository. diff --git a/docs/admin-ui.md b/docs/admin-ui.md index f92600a963..0a19e0ae61 100644 --- a/docs/admin-ui.md +++ b/docs/admin-ui.md @@ -10,7 +10,7 @@ The TrogonEventStore Admin UI is available at `http://SERVER_IP:2113/ui` and hel The dashboard opens at `/ui` and combines the daily operational view in one place: -- _Cluster status_: live gossip membership, node state, checkpoints, TCP and HTTP endpoints, replica status, and a copy-friendly snapshot. +- _Cluster status_: live gossip membership, node state, checkpoints, HTTP(S) endpoints, replica status, and a copy-friendly snapshot. - _Queue pressure_: live queue length, throughput, processing time, and currently processed messages. - _Node probes_: inline Ping, Node info, and Gossip checks rendered inside the UI. @@ -24,7 +24,8 @@ The _Observability_ page focuses on runtime diagnostics: - queue groups and individual queue rows - current and last processed messages -- TCP connection statistics +- active connections on the shared HTTP and gRPC endpoint, including client identity, protocol, security, traffic rates, totals, and pending bytes +- live gRPC replication sessions, byte totals, pending bytes, and send queue depth - snapshot output for copy-paste debugging ## Configuration diff --git a/docs/architecture.md b/docs/architecture.md index 2b1ac2fc74..04aa6764f0 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -22,6 +22,16 @@ and sharding work. Features that need their own compute model, query model, or serving model should be separate components that consume the database through subscriptions or reads. +## Network protocol boundary + +gRPC carries database client APIs, cluster replication, and follower-to-leader +forwarding over the node HTTP(S) endpoint. Ordinary HTTP routes on that endpoint +serve the Admin UI, health probes, metrics, and other operator workflows. + +Keeping one advertised endpoint gives clients and cluster members the same TLS, +identity, and network-policy boundary. The node therefore has no separate legacy +EventStore TCP protocol listener or configuration surface. + ## Projection execution Projection execution is future external component work by default. diff --git a/docs/cluster.md b/docs/cluster.md index 9c326de56a..ee826e4a1e 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -55,15 +55,21 @@ The multi-address DNS name cluster discovery only works for clusters that use ce ### Internal communication -When setting up a cluster, the nodes must be able to reach each other over both the HTTP channel, and the internal TCP channel. You should ensure that these ports are open on firewalls on the machines and between the machines. +Cluster nodes use gRPC over each node's HTTP(S) endpoint for replication and +follower-to-leader request forwarding. Ensure every node can reach every other +node at its advertised HTTP(S) endpoint. There is no separate replication +listener or TCP port to expose. -Learn more about [internal TCP configuration](networking.md#replication-protocol) and [HTTP configuration](networking.md#http-configuration) to set up the cluster properly. +Learn more about the shared [HTTP(S) configuration](networking.md#http-configuration) +before configuring cluster firewall or network policy rules. ## Cluster with DNS When you tell TrogonEventStore to use DNS for its gossip, the server will resolve the DNS name to a list of IP addresses and connect to each of those addresses to find other nodes. This method is very flexible because you can change the list of nodes on your DNS server without changing the cluster configuration. The DNS method is also useful in automated deployment scenarios when you control both the cluster deployment and the DNS server from your infrastructure-as-code scripts. -To use DNS discovery, you need to set the `ClusterDns` option to the DNS name that allows making an HTTP call to it. When the server starts, it will attempt to make a gRPC call using the `https://:` URL (`http` if the cluster is insecure). +To use DNS discovery, set the `ClusterDns` option to a DNS name that resolves to +the cluster nodes. When the server starts, it attempts a gRPC call over +`https://:` (`http` if the cluster is insecure). When using a certificate signed by a publicly trusted CA, you'd normally use the wildcard certificate. Ensure that the cluster DNS name fits the wildcard, otherwise the request will fail on SSL check. @@ -107,7 +113,8 @@ The setting accepts a comma-separated list of IP addresses or host names with th TrogonEventStore uses a quorum-based replication model. When working normally, a cluster has one node known as a leader, and the remaining nodes are followers. The leader node is responsible for coordinating writes while it is the leader. Cluster nodes use a consensus algorithm to determine which node should be the leader and which should be followers. TrogonEventStore bases the decision as to which node should be the leader on a number of factors. -For a cluster node to have this information available to them, the nodes gossip with other nodes in the cluster. Gossip runs over HTTP interfaces of cluster nodes. +For a cluster node to have this information available to them, the nodes gossip +with other nodes in the cluster over the shared HTTP(S) endpoint. The gossip protocol configuration can be changed using the settings listed below. Pay attention to the settings related to time, like intervals and timeouts, when running in a cloud environment. @@ -229,7 +236,7 @@ candidate. ### Follower -A cluster assigns the follower role based on an election process. A cluster uses one or more nodes with the follower role to form the quorum, or the majority of nodes necessary to confirm that the write is persisted. +A cluster assigns the follower role based on an election process. A cluster uses one or more nodes with the follower role to form the quorum, or the majority of nodes necessary to confirm that the write is persisted. When a follower accepts a request that must run on the leader, it forwards the request to the leader over gRPC on the leader's HTTP(S) endpoint. ### Read-only replica diff --git a/docs/diagnostics/README.md b/docs/diagnostics/README.md index adc52604bc..d945c4bbde 100644 --- a/docs/diagnostics/README.md +++ b/docs/diagnostics/README.md @@ -49,17 +49,6 @@ type `$statsCollected`. "proc-gc-largeHeapSize": 0, "proc-gc-timeInGc": 0.0, "proc-gc-totalBytesInHeaps": 0, - "proc-tcp-connections": 0, - "proc-tcp-receivingSpeed": 0.0, - "proc-tcp-sendingSpeed": 0.0, - "proc-tcp-inSend": 0, - "proc-tcp-measureTime": "00:00:19.0534210", - "proc-tcp-pendingReceived": 0, - "proc-tcp-pendingSend": 0, - "proc-tcp-receivedBytesSinceLastRun": 0, - "proc-tcp-receivedBytesTotal": 0, - "proc-tcp-sentBytesSinceLastRun": 0, - "proc-tcp-sentBytesTotal": 0, "es-checksum": 1613144, "es-checksumNonFlushed": 1613144, "sys-drive-/System/Volumes/Data-availableBytes": 545628151808, @@ -104,7 +93,7 @@ type `$statsCollected`. "es-queue-MonitoringQueue-lengthLifetimePeak": 0, "es-queue-MonitoringQueue-totalItemsProcessed": 14, "es-queue-MonitoringQueue-inProgressMessage": "", - "es-queue-MonitoringQueue-lastProcessedMessage": "GetFreshTcpConnectionStats", + "es-queue-MonitoringQueue-lastProcessedMessage": "GetFreshStats", "es-queue-PersistentSubscriptions-queueName": "PersistentSubscriptions", "es-queue-PersistentSubscriptions-groupName": "", "es-queue-PersistentSubscriptions-avgItemsPerSecond": 1, diff --git a/docs/installation.md b/docs/installation.md index 8e4a2a9166..cbd8d1c7ac 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -76,6 +76,9 @@ Before running a durable node or cluster: - Store data, index, and logs on durable volumes. - Expose `/-/liveness`, `/-/readiness`, and `/-/metrics` to the platform. - Use gRPC clients for application reads and writes. +- Expose only the node HTTP(S) endpoint. Database client APIs, replication, and + follower-to-leader forwarding use gRPC on that endpoint; no separate legacy + EventStore TCP protocol listener is required or supported. ## Linux service notes diff --git a/docs/networking.md b/docs/networking.md index 84b1416497..0af003372a 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -4,22 +4,22 @@ title: Networking ## Network configuration -TrogonEventStore provides two interfaces: -- HTTP(S) for gRPC communication, the Admin UI, and operational endpoints such as health checks and metrics -- TCP for cluster replication (internal) +TrogonEventStore uses one HTTP(S) endpoint for all network traffic: -Nodes in the cluster replicate with each other using the TCP protocol, but use gRPC for [discovering other cluster nodes](cluster.md#discovering-cluster-members). +- gRPC carries database client APIs, cluster replication, and follower-to-leader request forwarding. +- HTTP carries the Admin UI, health probes, metrics, and other operator endpoints. -Server nodes use a single HTTP binding for gRPC, the Admin UI, health, metrics, -and supported diagnostics. Replication between cluster nodes is internal and can -be placed on a private network interface. Keep public client access on gRPC and -avoid exposing replication ports outside the cluster network. - -For gRPC and HTTP, there's no internal vs external separation of traffic. +The node does not open a separate legacy EventStore TCP protocol listener or +accept its TCP transport configuration. gRPC and HTTP still use TCP at the +operating-system transport layer. In a cluster, every node must be able to reach +every other node at its advertised HTTP(S) endpoint. Use network policy and +ingress rules to control which clients can reach the shared listener. ## HTTP configuration -HTTP is the primary protocol for TrogonEventStore. It carries gRPC communication, the Admin UI, health checks, metrics, and supported diagnostics endpoints. +HTTP(S) is the node's only network endpoint. HTTP/2 carries gRPC traffic for +database clients and internal cluster operations, while regular HTTP routes +serve the Admin UI, health checks, metrics, and supported diagnostics. The HTTP endpoint always binds to the IP address configured in the `NodeIp` setting (previously referred to as `ExtIp`). | Format | Syntax | @@ -145,51 +145,15 @@ Value must be greater than or equal to 65,535 and less than 2^31. See the docs [ This is configured with `Kestrel.Limits.Http2.InitialStreamWindowSize` in the settings file. -## Replication protocol - -Replication between cluster nodes uses an internal TCP-based protocol. Options for configuring the internal replication protocol are described below. - -### Interface and port - -Internal TCP binds to the IP address specified in the `ReplicationIp` setting (previously `IntIp`). It must be configured if you run a multi-node cluster. - -By default, TrogonEventStore binds its internal networking on the loopback interface only (`127.0.0.1`). You can change this behaviour and tell TrogonEventStore to listen on a specific internal IP address. To do that set the `ReplicationIp` to `0.0.0.0` or the IP address of the network interface. - -| Format | Syntax | -|:---------------------|:----------------------------| -| Command line | `--replication-ip` | -| YAML | `ReplicationIp` | -| Environment variable | `EVENTSTORE_REPLICATION_IP` | - -**Default**: `127.0.0.1` (loopback). - -If you keep this setting to its default value, cluster nodes won't be able to talk to each other. - -::: warning -Please note that the `IntIp` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `ReplicationIp` parameter instead. -::: - -By default, TrogonEventStore uses port `1112` for internal TCP. You can change this by specifying the `ReplicationPort` setting (previously `IntTcpPort` setting). +## Internal cluster traffic -| Format | Syntax | -|:---------------------|:------------------------------| -| Command line | `--replication-port` | -| YAML | `ReplicationPort` | -| Environment variable | `EVENTSTORE_REPLICATION_PORT` | +Cluster replication and follower-to-leader request forwarding use gRPC over +the same `NodeIp` and `NodePort` binding used by client gRPC calls. There is no +separate replication interface or port. -**Default**: `1112` - -::: warning -Please note that the `IntTcpPort` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `ReplicationPort` parameter instead. -::: - -### Security - -When TLS is enabled, replication uses the configured node certificate. Replication TLS cannot be disabled -independently. `DisableTls` disables TLS for both HTTP and replication while preserving authentication and -authorization. - -If your network setup requires any kind of IP address, DNS name and port translation for internal communication, you can use available [address translation](#network-address-translation) settings. +When TLS is enabled, internal gRPC connections use the configured node +certificate. `DisableTls` disables encryption for the shared HTTP(S) endpoint +while preserving authentication and authorization. ## Network address translation @@ -197,13 +161,14 @@ Due to NAT (network address translation), or other reasons a node may not be bou Options described below allow you to tell the node that even though it is bound to a given address it should not gossip that address. When returning links over HTTP, TrogonEventStore will also use the specified addresses instead of physical addresses, so the clients that use HTTP can follow those links. -Another case when you might want to specify the advertised address although there's no address translation involved. When you configure TrogonEventStore to bind to `0.0.0.0`, it will use the first non-loopback address for gossip. It might or might not be the address you want it to use. Whilst the best way to avoid such a situation is to configure the binding properly using the `NodeIp` and `ReplicationIp` settings, you can also use address translation setting with the correct IP address or DNS name. +Another case when you might want to specify the advertised address although there's no address translation involved. When you configure TrogonEventStore to bind to `0.0.0.0`, it will use the first non-loopback address for gossip. It might or might not be the address you want it to use. Configure `NodeHostAdvertiseAs` when other nodes and clients must connect using a specific IP address or hostname. -Also, even if you specified the `NodeIp` and `ReplicationIp` settings in the configuration, you might still want to override the advertised address if you want to use hostnames and not IP addresses. That might be needed when running a secure cluster with certificates that only contain DNS names of the nodes. +You might also override the advertised address when secure cluster certificates +contain DNS names rather than IP addresses. The only place where these settings make any effect is the [gossip](cluster.md#gossip-protocol) endpoint response. -## HTTP translations +## HTTP(S) endpoint advertisement By default, a cluster node will advertise itself using `NodeIp` and `NodePort`. You can override the advertised HTTP port using the `NodePortAdvertiseAs` setting (previously `HttpPortAdvertiseAs` setting). @@ -229,32 +194,6 @@ If you want the node to advertise itself using the hostname rather than its IP a Please note that the `ExtHostAdvertiseAs` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `NodeHostAdvertiseAs` parameter instead. ::: -### TCP translations - -TCP ports used for replication can be advertised using custom values: - -| Format | Syntax | -|:---------------------|:-----------------------------------------------| -| Command line | `--replication-tcp-port-advertise-as` | -| YAML | `ReplicationTcpPortAdvertiseAs` | -| Environment variable | `EVENTSTORE_REPLICATION_TCP_PORT_ADVERTISE_AS` | - -::: warning -Please note that the `IntTcpPortAdvertiseAs` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `ReplicationTcpPortAdvertiseAs` and `NodeTcpPortAdvertiseAs` parameters instead, respectively. -::: - -If you want to change how the node TCP address is advertised internally, use the `ReplicationHostAdvertiseAs` setting (previously `IntHostAdvertiseAs` setting). You can use an IP address or a hostname. - -| Format | Syntax | -|:---------------------|:-------------------------------------------| -| Command line | `--replication-host-advertise-as` | -| YAML | `ReplicationHostAdvertiseAs` | -| Environment variable | `EVENTSTORE_REPLICATION_HOST_ADVERTISE_AS` | - -::: warning -Please note that the `IntHostAdvertiseAs` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `ReplicationHostAdvertiseAs` parameter instead. -::: - ### Advertise to clients In some cases, the cluster needs to advertise itself to clients using a completely different set of addresses and ports. Usually, you need to do it because addresses and ports configured for the HTTP protocol are not available as-is to the outside world. One of the examples is running a cluster in Docker Compose. In such environment, HTTP uses internal hostnames in the Docker network, which isn't accessible on the host. So, in order to connect to the cluster from the host machine, you need to use `localhost` and translated HTTP ports to reach the cluster nodes. @@ -281,49 +220,6 @@ Specify the advertised HTTP(S) port (previously `AdvertiseHttpPortToClientAs` se Please note that the `AdvertiseHttpPortToClientAs` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `AdvertiseNodePortToClientAs` parameter instead. ::: -## Heartbeat timeouts - -TrogonEventStore uses heartbeats over all TCP connections to discover dead clients and nodes. Heartbeat timeouts should not be too short, as short timeouts will produce false positives. At the same time, setting too long timeouts will prevent discovering dead nodes and clients in time. - -Each heartbeat has two points of configuration. The first is the _interval;_ this represents how often the system should consider a heartbeat. TrogonEventStore doesn't send a heartbeat for every interval, but only if it has not heard from a node within the configured interval. In a busy cluster, you may never see any heartbeats. - -The second point of configuration is the _timeout_. This determines how long TrogonEventStore server waits for a client or node to respond to a heartbeat request. - -Different environments need different values for these settings. The defaults are likely fine on a LAN. If you experience frequent elections in your environment, you can try to increase both interval and timeout, for example: - -- An interval of 5000ms. -- A timeout of 1000ms. - -::: tip -If in doubt, choose higher numbers. This adds a small period of time to discover a dead client or node and is better than the alternative, which is false positives. -::: - -Replication/Internal TCP heartbeat (between cluster nodes): - -| Format | Syntax | -|:---------------------|:--------------------------------------------| -| Command line | `--replication-heartbeat-interval` | -| YAML | `ReplicationHeartbeatInterval` | -| Environment variable | `EVENTSTORE_REPLICATION_HEARTBEAT_INTERVAL` | - -**Default**: `700` (ms) - -| Format | Syntax | -|:---------------------|:-------------------------------------------| -| Command line | `--replication-heartbeat-timeout` | -| YAML | `ReplicationHeartbeatTimeout` | -| Environment variable | `EVENTSTORE_REPLICATION_HEARTBEAT_TIMEOUT` | - -**Default**: `700` (ms) - -::: warning -Please note that the `IntTcpHeartbeatInterval` and `IntTcpHeartbeatTimeout` parameters have been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `ReplicationHeartbeatInterval` and `ReplicationHeartbeatTimeout` parameters instead, respectively. -::: - -### gRPC heartbeats - -For the gRPC heartbeats, TrogonEventStore and its gRPC clients use the protocol feature called _Keepalive ping_. Read more about it on the [HTTP configuration page](#keep-alive-pings). - ## Exposing endpoints If you need to reduce the HTTP surface, you can disable the browser-facing Admin UI and the Prometheus metrics endpoint. Health probes and gRPC remain part of the supported HTTP listener. @@ -348,11 +244,9 @@ You can disable the Prometheus metrics endpoint by setting `DisableStatsOnHttp` **Default**: `false`, the Prometheus metrics endpoint is enabled on `/-/metrics`. -## Application protocol boundary - -TrogonEventStore does not document an external TCP client protocol. Application -reads and writes should use gRPC clients. +## Protocol boundary -Internal replication can still use node-to-node transport that is not part of -the public client API. Treat those settings as cluster internals, not as a -client integration surface. +Database client APIs, replication, and follower-to-leader forwarding use gRPC. +The remaining HTTP routes are operator surfaces, not an application event API. +The server has no legacy EventStore TCP protocol listener, TCP client protocol, +replication TCP port, or TCP heartbeat configuration. diff --git a/docs/security.md b/docs/security.md index d33e3a7a41..d83d6ffabd 100644 --- a/docs/security.md +++ b/docs/security.md @@ -15,14 +15,14 @@ Security features of TrogonEventStore include: ### Protocol security -TrogonEventStore supports gRPC for client communication and internal TCP for -cluster replication. It also has HTTP endpoints for the Admin UI, health, -metrics, and supported operator workflows. -TrogonEventStore also uses HTTP for the gossip seed endpoint, both internally for the cluster gossip, and -internally for clients that connect to the cluster using discovery mode. +TrogonEventStore uses a single HTTP(S) endpoint. gRPC over that endpoint carries +database client APIs, cluster replication, follower-to-leader forwarding, and +cluster discovery. Regular HTTP routes serve the Admin UI, health, metrics, and +supported operator workflows. -All those protocols support encryption with TLS and SSL. Each protocol has its own security configuration, but -you can only use one set of certificates for both TLS and HTTPS. +The same TLS configuration and node certificate protect every surface on the +listener. The server does not open a separate legacy EventStore TCP protocol +listener or support separate TCP security settings. The protocol security configuration depends a lot on the deployment topology and platform. We have created an interactive [configuration tool](installation.md), which also has instructions on how to generate and install @@ -162,7 +162,8 @@ If the domains are `node1.esdb.mycompany.org`, `node2.esdb.mycompany.org` and `n | Environment variable | `EVENTSTORE_CERTIFICATE_RESERVED_NODE_COMMON_NAME` | ::: warning -Server certificates **must** have the internal and external IP addresses (`ReplicationIp` and `NodeIp` respectively) or DNS names as subject alternative names. +Server certificates **must** contain every IP address or DNS name that clients +or peer nodes use to reach the node as a subject alternative name. ::: #### Node certificate Client Authentication usage @@ -188,8 +189,7 @@ validity period, common-name policy, subject alternative names, key usage, and S required. This option does not relax user certificate authentication. ::: -This setting controls node identity authentication for HTTPS cluster traffic. It does not change legacy secure TCP -replication certificate handling. +This setting controls node identity authentication for HTTPS cluster traffic. #### Trusted root certificates @@ -512,11 +512,13 @@ Import-Certificate -FilePath .\ca.crt -CertStoreLocation Cert:\LocalMachine\CA ::: :::: -### Replication protocol security +### Internal gRPC security -When TLS is enabled, cluster replication uses the configured node certificate. Replication TLS cannot be -disabled independently. Use [`DisableTls`](#disable-tls) to disable transport encryption for both HTTP and -replication while preserving authentication and authorization. +When TLS is enabled, cluster replication and follower-to-leader forwarding use +the configured node certificate on the shared HTTPS endpoint. Internal gRPC TLS +cannot be disabled independently. Use [`DisableTls`](#disable-tls) to disable +transport encryption for the entire endpoint while preserving authentication +and authorization. ## Authentication @@ -530,7 +532,7 @@ content, and redirects. ### Password authentication admission limits Built-in password authentication shares a node-local admission budget across UI sign-in, HTTP and gRPC -credentials, TCP authentication, and forwarded credentials. Cached credentials still require password +credentials, and forwarded credentials. Cached credentials still require password verification and use the same budget. Attempts are admitted before account reads, including requests for nonexistent accounts, so changing usernames cannot bypass the node-wide limit. @@ -544,7 +546,7 @@ All values must be positive, and `BurstSize` must be at least `AttemptsPerSecond hold a full second's replenishment. Invalid limits prevent the password provider from starting. There is no waiting queue. When either budget is exhausted, requests receive the existing authentication-not-ready response: HTTP returns `503` with `Retry-After`, gRPC -returns `Unavailable`, TCP returns `NotReady`, and browser sign-in reports that the provider is not +returns `Unavailable`, and browser sign-in reports that the provider is not ready. Clients should use bounded retries with backoff and jitter. Limits are shared by all password users on a node, not per account or per IP address. They do not lock diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index b1f10f5a7b..689c5b3884 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -13,6 +13,9 @@ from this repository. - Record the current server version, configuration file, container image, and command-line arguments. - Check that clients are using gRPC. +- Remove legacy EventStore TCP protocol listener, replication port, + advertisement, and heartbeat settings. They are not supported by the current + server. - Review changed configuration keys before restarting a durable node. - Verify that health probes use `/-/liveness` and `/-/readiness`. - Verify that metrics scraping uses `/-/metrics`. @@ -49,9 +52,11 @@ During the rollout: Before upgrading, search for obsolete client and HTTP-management settings. The current product direction is: -- gRPC for application event access. +- gRPC for database client APIs. +- gRPC over the node HTTP(S) endpoint for cluster replication and + follower-to-leader forwarding. - HTTP for Admin UI, health, metrics, and infrastructure concerns. -- No external TCP client protocol. +- No legacy EventStore TCP protocol listener or TCP transport configuration. - No proprietary plugin configuration. If a setting is no longer documented, remove it rather than carrying it forward @@ -68,6 +73,34 @@ methods. Avoid depending on undocumented authentication plugins. Use [OpenTelemetry integration](diagnostics/integrations.md) for explicit OTLP export and [Metrics](diagnostics/metrics.md) for Prometheus scraping. +The Admin UI continues to show active connections after the legacy protocol is +removed. The connection table reports the shared HTTP and gRPC listener, while +the replication table reports the database replication sessions and their byte +and queue statistics. The shared-endpoint table keeps the live paging and +per-second traffic view while identifying HTTP, gRPC, TLS, and the observed +client. These replace the legacy listener-specific TCP table. + +The TestClient keeps the established command names that can preserve their +meaning over the supported APIs. Reads, writes, deletes, subscriptions, +scavenging, data verification, load operations, and sanitization checks now +exercise the gRPC APIs on `NodePort`. `CHKGRPC` replaces the protocol-specific +`CHKTCP` malformed-frame check. Historical write-flood aliases remain available +for script compatibility, but they also send gRPC traffic and do not require a +legacy listener. + +The historical `RT` suite is not an application API. It was an in-process +developer harness coupled to the legacy TCP client, direct TCP packages, local +node process control, and the old projections client. It has no equivalent in +this release. Reintroducing those projection and node-failure scenarios requires +a dedicated gRPC and HTTP implementation rather than routing the `RT` name to a +different workload. + +`ClientMessageDtos.proto` is not a gRPC application contract. It encoded the +payloads placed inside legacy TCP packages, so it is removed with that wire +protocol. The supported replacements are the service contracts under +`src/Protos/Grpc`, including streams, persistent subscriptions, operations, +monitoring, gossip, replication, and request forwarding. + Legacy usage telemetry is separate from OTLP observability. See [Usage telemetry](usage-telemetry.md) before running a node in an environment that should not make outbound telemetry calls. diff --git a/docs/whatsnew.md b/docs/whatsnew.md index 0957f75619..10d6f5df82 100644 --- a/docs/whatsnew.md +++ b/docs/whatsnew.md @@ -6,14 +6,20 @@ not a release-note mirror for another distribution. ## Current baseline - TrogonEventStore is documented as a FOSS-only server distribution. -- Application event access is documented as gRPC-first. +- Database client APIs, cluster replication, and follower-to-leader forwarding + are documented as gRPC-only. - HTTP documentation is limited to browser UI, health probes, metrics, and infrastructure concerns. +- The server has no documented legacy EventStore TCP protocol listener or TCP + transport configuration. - Health probes are exposed on `/-/liveness` and `/-/readiness`. - Prometheus scraping is exposed on `/-/metrics`. - OpenTelemetry documentation covers explicit OTLP export for logs, metrics, and traces. - The Admin UI is the embedded Blazor UI served by the node. +- The Admin UI reports shared-endpoint connections and gRPC replication sessions + without depending on the removed legacy listener. +- EventStore.TestClient operational commands exercise the supported gRPC APIs. - User-defined projection execution, connector runtimes, SQL-like query surfaces, and rich read models are documented as external component work by default. diff --git a/proto.lock b/proto.lock index 068485f54f..70d86541cd 100644 --- a/proto.lock +++ b/proto.lock @@ -1,1506 +1,5 @@ { "definitions": [ - { - "protopath": "ClientAPI:/:ClientMessageDtos.proto", - "def": { - "enums": [ - { - "name": "OperationResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "PrepareTimeout", - "integer": 1 - }, - { - "name": "CommitTimeout", - "integer": 2 - }, - { - "name": "ForwardTimeout", - "integer": 3 - }, - { - "name": "WrongExpectedVersion", - "integer": 4 - }, - { - "name": "StreamDeleted", - "integer": 5 - }, - { - "name": "InvalidTransaction", - "integer": 6 - }, - { - "name": "AccessDenied", - "integer": 7 - } - ] - }, - { - "name": "ReadEventCompleted.ReadEventResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "NotFound", - "integer": 1 - }, - { - "name": "NoStream", - "integer": 2 - }, - { - "name": "StreamDeleted", - "integer": 3 - }, - { - "name": "Error", - "integer": 4 - }, - { - "name": "AccessDenied", - "integer": 5 - } - ] - }, - { - "name": "ReadStreamEventsCompleted.ReadStreamResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "NoStream", - "integer": 1 - }, - { - "name": "StreamDeleted", - "integer": 2 - }, - { - "name": "NotModified", - "integer": 3 - }, - { - "name": "Error", - "integer": 4 - }, - { - "name": "AccessDenied", - "integer": 5 - } - ] - }, - { - "name": "ReadAllEventsCompleted.ReadAllResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "NotModified", - "integer": 1 - }, - { - "name": "Error", - "integer": 2 - }, - { - "name": "AccessDenied", - "integer": 3 - } - ] - }, - { - "name": "Filter.FilterContext", - "enum_fields": [ - { - "name": "StreamId" - }, - { - "name": "EventType", - "integer": 1 - } - ] - }, - { - "name": "Filter.FilterType", - "enum_fields": [ - { - "name": "Regex" - }, - { - "name": "Prefix", - "integer": 1 - } - ] - }, - { - "name": "FilteredReadAllEventsCompleted.FilteredReadAllResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "NotModified", - "integer": 1 - }, - { - "name": "Error", - "integer": 2 - }, - { - "name": "AccessDenied", - "integer": 3 - } - ] - }, - { - "name": "UpdatePersistentSubscriptionCompleted.UpdatePersistentSubscriptionResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "DoesNotExist", - "integer": 1 - }, - { - "name": "Fail", - "integer": 2 - }, - { - "name": "AccessDenied", - "integer": 3 - } - ] - }, - { - "name": "CreatePersistentSubscriptionCompleted.CreatePersistentSubscriptionResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "AlreadyExists", - "integer": 1 - }, - { - "name": "Fail", - "integer": 2 - }, - { - "name": "AccessDenied", - "integer": 3 - } - ] - }, - { - "name": "DeletePersistentSubscriptionCompleted.DeletePersistentSubscriptionResult", - "enum_fields": [ - { - "name": "Success" - }, - { - "name": "DoesNotExist", - "integer": 1 - }, - { - "name": "Fail", - "integer": 2 - }, - { - "name": "AccessDenied", - "integer": 3 - } - ] - }, - { - "name": "PersistentSubscriptionNakEvents.NakAction", - "enum_fields": [ - { - "name": "Unknown" - }, - { - "name": "Park", - "integer": 1 - }, - { - "name": "Retry", - "integer": 2 - }, - { - "name": "Skip", - "integer": 3 - }, - { - "name": "Stop", - "integer": 4 - } - ] - }, - { - "name": "SubscriptionDropped.SubscriptionDropReason", - "enum_fields": [ - { - "name": "Unsubscribed" - }, - { - "name": "AccessDenied", - "integer": 1 - }, - { - "name": "NotFound", - "integer": 2 - }, - { - "name": "PersistentSubscriptionDeleted", - "integer": 3 - }, - { - "name": "SubscriberMaxCountReached", - "integer": 4 - } - ] - }, - { - "name": "NotHandled.NotHandledReason", - "enum_fields": [ - { - "name": "NotReady" - }, - { - "name": "TooBusy", - "integer": 1 - }, - { - "name": "NotLeader", - "integer": 2 - }, - { - "name": "IsReadOnly", - "integer": 3 - } - ] - }, - { - "name": "ScavengeDatabaseResponse.ScavengeResult", - "enum_fields": [ - { - "name": "Started" - }, - { - "name": "InProgress", - "integer": 1 - }, - { - "name": "Unauthorized", - "integer": 2 - } - ] - } - ], - "messages": [ - { - "name": "NewEvent", - "fields": [ - { - "id": 1, - "name": "event_id", - "type": "bytes" - }, - { - "id": 2, - "name": "event_type", - "type": "string" - }, - { - "id": 3, - "name": "data_content_type", - "type": "int32" - }, - { - "id": 4, - "name": "metadata_content_type", - "type": "int32" - }, - { - "id": 5, - "name": "data", - "type": "bytes" - }, - { - "id": 6, - "name": "metadata", - "type": "bytes" - } - ] - }, - { - "name": "EventRecord", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "event_number", - "type": "int64" - }, - { - "id": 3, - "name": "event_id", - "type": "bytes" - }, - { - "id": 4, - "name": "event_type", - "type": "string" - }, - { - "id": 5, - "name": "data_content_type", - "type": "int32" - }, - { - "id": 6, - "name": "metadata_content_type", - "type": "int32" - }, - { - "id": 7, - "name": "data", - "type": "bytes" - }, - { - "id": 8, - "name": "metadata", - "type": "bytes" - }, - { - "id": 9, - "name": "created", - "type": "int64" - }, - { - "id": 10, - "name": "created_epoch", - "type": "int64" - } - ] - }, - { - "name": "ResolvedIndexedEvent", - "fields": [ - { - "id": 1, - "name": "event", - "type": "EventRecord" - }, - { - "id": 2, - "name": "link", - "type": "EventRecord" - } - ] - }, - { - "name": "ResolvedEvent", - "fields": [ - { - "id": 1, - "name": "event", - "type": "EventRecord" - }, - { - "id": 2, - "name": "link", - "type": "EventRecord" - }, - { - "id": 3, - "name": "commit_position", - "type": "int64" - }, - { - "id": 4, - "name": "prepare_position", - "type": "int64" - } - ] - }, - { - "name": "WriteEvents", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "expected_version", - "type": "int64" - }, - { - "id": 3, - "name": "events", - "type": "NewEvent", - "is_repeated": true - }, - { - "id": 4, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "WriteEventsCompleted", - "fields": [ - { - "id": 1, - "name": "result", - "type": "OperationResult" - }, - { - "id": 2, - "name": "message", - "type": "string" - }, - { - "id": 3, - "name": "first_event_number", - "type": "int64" - }, - { - "id": 4, - "name": "last_event_number", - "type": "int64" - }, - { - "id": 5, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 6, - "name": "commit_position", - "type": "int64" - }, - { - "id": 7, - "name": "current_version", - "type": "int64" - } - ] - }, - { - "name": "DeleteStream", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "expected_version", - "type": "int64" - }, - { - "id": 3, - "name": "require_leader", - "type": "bool" - }, - { - "id": 4, - "name": "hard_delete", - "type": "bool" - } - ] - }, - { - "name": "DeleteStreamCompleted", - "fields": [ - { - "id": 1, - "name": "result", - "type": "OperationResult" - }, - { - "id": 2, - "name": "message", - "type": "string" - }, - { - "id": 3, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 4, - "name": "commit_position", - "type": "int64" - }, - { - "id": 5, - "name": "current_version", - "type": "int64" - } - ] - }, - { - "name": "TransactionStart", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "expected_version", - "type": "int64" - }, - { - "id": 3, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "TransactionStartCompleted", - "fields": [ - { - "id": 1, - "name": "transaction_id", - "type": "int64" - }, - { - "id": 2, - "name": "result", - "type": "OperationResult" - }, - { - "id": 3, - "name": "message", - "type": "string" - } - ] - }, - { - "name": "TransactionWrite", - "fields": [ - { - "id": 1, - "name": "transaction_id", - "type": "int64" - }, - { - "id": 2, - "name": "events", - "type": "NewEvent", - "is_repeated": true - }, - { - "id": 3, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "TransactionWriteCompleted", - "fields": [ - { - "id": 1, - "name": "transaction_id", - "type": "int64" - }, - { - "id": 2, - "name": "result", - "type": "OperationResult" - }, - { - "id": 3, - "name": "message", - "type": "string" - } - ] - }, - { - "name": "TransactionCommit", - "fields": [ - { - "id": 1, - "name": "transaction_id", - "type": "int64" - }, - { - "id": 2, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "TransactionCommitCompleted", - "fields": [ - { - "id": 1, - "name": "transaction_id", - "type": "int64" - }, - { - "id": 2, - "name": "result", - "type": "OperationResult" - }, - { - "id": 3, - "name": "message", - "type": "string" - }, - { - "id": 4, - "name": "first_event_number", - "type": "int64" - }, - { - "id": 5, - "name": "last_event_number", - "type": "int64" - }, - { - "id": 6, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 7, - "name": "commit_position", - "type": "int64" - } - ] - }, - { - "name": "ReadEvent", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "event_number", - "type": "int64" - }, - { - "id": 3, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 4, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "ReadEventCompleted", - "fields": [ - { - "id": 1, - "name": "result", - "type": "ReadEventResult" - }, - { - "id": 2, - "name": "event", - "type": "ResolvedIndexedEvent" - }, - { - "id": 3, - "name": "error", - "type": "string" - } - ] - }, - { - "name": "ReadStreamEvents", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "from_event_number", - "type": "int64" - }, - { - "id": 3, - "name": "max_count", - "type": "int32" - }, - { - "id": 4, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 5, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "ReadStreamEventsCompleted", - "fields": [ - { - "id": 1, - "name": "events", - "type": "ResolvedIndexedEvent", - "is_repeated": true - }, - { - "id": 2, - "name": "result", - "type": "ReadStreamResult" - }, - { - "id": 3, - "name": "next_event_number", - "type": "int64" - }, - { - "id": 4, - "name": "last_event_number", - "type": "int64" - }, - { - "id": 5, - "name": "is_end_of_stream", - "type": "bool" - }, - { - "id": 6, - "name": "last_commit_position", - "type": "int64" - }, - { - "id": 7, - "name": "error", - "type": "string" - } - ] - }, - { - "name": "ReadAllEvents", - "fields": [ - { - "id": 1, - "name": "commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 3, - "name": "max_count", - "type": "int32" - }, - { - "id": 4, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 5, - "name": "require_leader", - "type": "bool" - } - ] - }, - { - "name": "ReadAllEventsCompleted", - "fields": [ - { - "id": 1, - "name": "commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 3, - "name": "events", - "type": "ResolvedEvent", - "is_repeated": true - }, - { - "id": 4, - "name": "next_commit_position", - "type": "int64" - }, - { - "id": 5, - "name": "next_prepare_position", - "type": "int64" - }, - { - "id": 6, - "name": "result", - "type": "ReadAllResult" - }, - { - "id": 7, - "name": "error", - "type": "string" - } - ] - }, - { - "name": "Filter", - "fields": [ - { - "id": 1, - "name": "context", - "type": "FilterContext" - }, - { - "id": 2, - "name": "type", - "type": "FilterType" - }, - { - "id": 3, - "name": "data", - "type": "string", - "is_repeated": true - } - ] - }, - { - "name": "FilteredReadAllEvents", - "fields": [ - { - "id": 1, - "name": "commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 3, - "name": "max_count", - "type": "int32" - }, - { - "id": 4, - "name": "max_search_window", - "type": "int32" - }, - { - "id": 5, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 6, - "name": "require_leader", - "type": "bool" - }, - { - "id": 7, - "name": "filter", - "type": "Filter" - } - ] - }, - { - "name": "FilteredReadAllEventsCompleted", - "fields": [ - { - "id": 1, - "name": "commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "prepare_position", - "type": "int64" - }, - { - "id": 3, - "name": "events", - "type": "ResolvedEvent", - "is_repeated": true - }, - { - "id": 4, - "name": "next_commit_position", - "type": "int64" - }, - { - "id": 5, - "name": "next_prepare_position", - "type": "int64" - }, - { - "id": 6, - "name": "is_end_of_stream", - "type": "bool" - }, - { - "id": 7, - "name": "result", - "type": "FilteredReadAllResult" - }, - { - "id": 8, - "name": "error", - "type": "string" - } - ] - }, - { - "name": "CreatePersistentSubscription", - "fields": [ - { - "id": 1, - "name": "subscription_group_name", - "type": "string" - }, - { - "id": 2, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 3, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 4, - "name": "start_from", - "type": "int64" - }, - { - "id": 5, - "name": "message_timeout_milliseconds", - "type": "int32" - }, - { - "id": 6, - "name": "record_statistics", - "type": "bool" - }, - { - "id": 7, - "name": "live_buffer_size", - "type": "int32" - }, - { - "id": 8, - "name": "read_batch_size", - "type": "int32" - }, - { - "id": 9, - "name": "buffer_size", - "type": "int32" - }, - { - "id": 10, - "name": "max_retry_count", - "type": "int32" - }, - { - "id": 11, - "name": "prefer_round_robin", - "type": "bool" - }, - { - "id": 12, - "name": "checkpoint_after_time", - "type": "int32" - }, - { - "id": 13, - "name": "checkpoint_max_count", - "type": "int32" - }, - { - "id": 14, - "name": "checkpoint_min_count", - "type": "int32" - }, - { - "id": 15, - "name": "subscriber_max_count", - "type": "int32" - }, - { - "id": 16, - "name": "named_consumer_strategy", - "type": "string" - } - ] - }, - { - "name": "DeletePersistentSubscription", - "fields": [ - { - "id": 1, - "name": "subscription_group_name", - "type": "string" - }, - { - "id": 2, - "name": "event_stream_id", - "type": "string" - } - ] - }, - { - "name": "UpdatePersistentSubscription", - "fields": [ - { - "id": 1, - "name": "subscription_group_name", - "type": "string" - }, - { - "id": 2, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 3, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 4, - "name": "start_from", - "type": "int64" - }, - { - "id": 5, - "name": "message_timeout_milliseconds", - "type": "int32" - }, - { - "id": 6, - "name": "record_statistics", - "type": "bool" - }, - { - "id": 7, - "name": "live_buffer_size", - "type": "int32" - }, - { - "id": 8, - "name": "read_batch_size", - "type": "int32" - }, - { - "id": 9, - "name": "buffer_size", - "type": "int32" - }, - { - "id": 10, - "name": "max_retry_count", - "type": "int32" - }, - { - "id": 11, - "name": "prefer_round_robin", - "type": "bool" - }, - { - "id": 12, - "name": "checkpoint_after_time", - "type": "int32" - }, - { - "id": 13, - "name": "checkpoint_max_count", - "type": "int32" - }, - { - "id": 14, - "name": "checkpoint_min_count", - "type": "int32" - }, - { - "id": 15, - "name": "subscriber_max_count", - "type": "int32" - }, - { - "id": 16, - "name": "named_consumer_strategy", - "type": "string" - } - ] - }, - { - "name": "UpdatePersistentSubscriptionCompleted", - "fields": [ - { - "id": 1, - "name": "result", - "type": "UpdatePersistentSubscriptionResult" - }, - { - "id": 2, - "name": "reason", - "type": "string" - } - ] - }, - { - "name": "CreatePersistentSubscriptionCompleted", - "fields": [ - { - "id": 1, - "name": "result", - "type": "CreatePersistentSubscriptionResult" - }, - { - "id": 2, - "name": "reason", - "type": "string" - } - ] - }, - { - "name": "DeletePersistentSubscriptionCompleted", - "fields": [ - { - "id": 1, - "name": "result", - "type": "DeletePersistentSubscriptionResult" - }, - { - "id": 2, - "name": "reason", - "type": "string" - } - ] - }, - { - "name": "ConnectToPersistentSubscription", - "fields": [ - { - "id": 1, - "name": "subscription_id", - "type": "string" - }, - { - "id": 2, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 3, - "name": "allowed_in_flight_messages", - "type": "int32" - } - ] - }, - { - "name": "PersistentSubscriptionAckEvents", - "fields": [ - { - "id": 1, - "name": "subscription_id", - "type": "string" - }, - { - "id": 2, - "name": "processed_event_ids", - "type": "bytes", - "is_repeated": true - } - ] - }, - { - "name": "PersistentSubscriptionNakEvents", - "fields": [ - { - "id": 1, - "name": "subscription_id", - "type": "string" - }, - { - "id": 2, - "name": "processed_event_ids", - "type": "bytes", - "is_repeated": true - }, - { - "id": 3, - "name": "message", - "type": "string" - }, - { - "id": 4, - "name": "action", - "type": "NakAction" - } - ] - }, - { - "name": "PersistentSubscriptionConfirmation", - "fields": [ - { - "id": 1, - "name": "last_commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "subscription_id", - "type": "string" - }, - { - "id": 3, - "name": "last_event_number", - "type": "int64" - } - ] - }, - { - "name": "PersistentSubscriptionStreamEventAppeared", - "fields": [ - { - "id": 1, - "name": "event", - "type": "ResolvedIndexedEvent" - }, - { - "id": 2, - "name": "retryCount", - "type": "int32" - } - ] - }, - { - "name": "SubscribeToStream", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "resolve_link_tos", - "type": "bool" - } - ] - }, - { - "name": "FilteredSubscribeToStream", - "fields": [ - { - "id": 1, - "name": "event_stream_id", - "type": "string" - }, - { - "id": 2, - "name": "resolve_link_tos", - "type": "bool" - }, - { - "id": 3, - "name": "filter", - "type": "Filter" - }, - { - "id": 4, - "name": "checkpoint_interval", - "type": "int32" - } - ] - }, - { - "name": "CheckpointReached", - "fields": [ - { - "id": 1, - "name": "commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "prepare_position", - "type": "int64" - } - ] - }, - { - "name": "SubscriptionConfirmation", - "fields": [ - { - "id": 1, - "name": "last_commit_position", - "type": "int64" - }, - { - "id": 2, - "name": "last_event_number", - "type": "int64" - } - ] - }, - { - "name": "StreamEventAppeared", - "fields": [ - { - "id": 1, - "name": "event", - "type": "ResolvedEvent" - } - ] - }, - { - "name": "UnsubscribeFromStream" - }, - { - "name": "SubscriptionDropped", - "fields": [ - { - "id": 1, - "name": "reason", - "type": "SubscriptionDropReason" - } - ] - }, - { - "name": "NotHandled", - "fields": [ - { - "id": 1, - "name": "reason", - "type": "NotHandledReason" - }, - { - "id": 2, - "name": "additional_info", - "type": "bytes" - } - ], - "messages": [ - { - "name": "LeaderInfo", - "fields": [ - { - "id": 1, - "name": "external_tcp_address", - "type": "string" - }, - { - "id": 2, - "name": "external_tcp_port", - "type": "int32" - }, - { - "id": 3, - "name": "http_address", - "type": "string" - }, - { - "id": 4, - "name": "http_port", - "type": "int32" - }, - { - "id": 5, - "name": "external_secure_tcp_address", - "type": "string" - }, - { - "id": 6, - "name": "external_secure_tcp_port", - "type": "int32" - } - ] - } - ] - }, - { - "name": "ScavengeDatabase" - }, - { - "name": "ScavengeDatabaseResponse", - "fields": [ - { - "id": 1, - "name": "result", - "type": "ScavengeResult" - }, - { - "id": 2, - "name": "scavengeId", - "type": "string" - } - ] - }, - { - "name": "IdentifyClient", - "fields": [ - { - "id": 1, - "name": "version", - "type": "int32" - }, - { - "id": 2, - "name": "connection_name", - "type": "string" - } - ] - }, - { - "name": "ClientIdentified" - } - ], - "package": { - "name": "EventStore.Client.Messages" - } - } - }, { "protopath": "Grpc:/:cluster.proto", "def": { @@ -1909,26 +408,6 @@ "name": "http_end_point", "type": "EndPoint" }, - { - "id": 6, - "name": "internal_tcp", - "type": "EndPoint" - }, - { - "id": 7, - "name": "external_tcp", - "type": "EndPoint" - }, - { - "id": 8, - "name": "internal_tcp_uses_tls", - "type": "bool" - }, - { - "id": 9, - "name": "external_tcp_uses_tls", - "type": "bool" - }, { "id": 10, "name": "last_commit_position", @@ -1979,16 +458,25 @@ "name": "advertise_http_port_to_client_as", "type": "uint32" }, - { - "id": 20, - "name": "advertise_tcp_port_to_client_as", - "type": "uint32" - }, { "id": 21, "name": "es_version", "type": "string" } + ], + "reserved_ids": [ + 6, + 7, + 8, + 9, + 20 + ], + "reserved_names": [ + "internal_tcp", + "external_tcp", + "internal_tcp_uses_tls", + "external_tcp_uses_tls", + "advertise_tcp_port_to_client_as" ] } ], @@ -2784,21 +1272,19 @@ { "name": "LeaderInfo", "fields": [ - { - "id": 1, - "name": "external_tcp", - "type": "EndPoint" - }, - { - "id": 2, - "name": "is_secure", - "type": "bool" - }, { "id": 3, "name": "http", "type": "EndPoint" } + ], + "reserved_ids": [ + 1, + 2 + ], + "reserved_names": [ + "external_tcp", + "is_secure" ] }, { @@ -3062,75 +1548,6 @@ } ] }, - { - "name": "TcpStatsReq" - }, - { - "name": "TcpStatsResp", - "fields": [ - { - "id": 1, - "name": "connections", - "type": "TcpConnectionStats", - "is_repeated": true - } - ] - }, - { - "name": "TcpConnectionStats", - "fields": [ - { - "id": 1, - "name": "remote_endpoint", - "type": "string" - }, - { - "id": 2, - "name": "local_endpoint", - "type": "string" - }, - { - "id": 3, - "name": "client_connection_name", - "type": "string" - }, - { - "id": 4, - "name": "connection_id", - "type": "string" - }, - { - "id": 5, - "name": "total_bytes_sent", - "type": "int64" - }, - { - "id": 6, - "name": "total_bytes_received", - "type": "int64" - }, - { - "id": 7, - "name": "pending_send_bytes", - "type": "int32" - }, - { - "id": 8, - "name": "pending_received_bytes", - "type": "int32" - }, - { - "id": 9, - "name": "is_external_connection", - "type": "bool" - }, - { - "id": 10, - "name": "is_ssl_connection", - "type": "bool" - } - ] - }, { "name": "ReplicationStatsReq" }, @@ -3201,11 +1618,6 @@ "out_type": "StatsResp", "out_streamed": true }, - { - "name": "TcpStats", - "in_type": "TcpStatsReq", - "out_type": "TcpStatsResp" - }, { "name": "ReplicationStats", "in_type": "ReplicationStatsReq", diff --git a/samples/server/docker-compose-cluster.yaml b/samples/server/docker-compose-cluster.yaml index 4b32fc779b..6101d3190f 100644 --- a/samples/server/docker-compose-cluster.yaml +++ b/samples/server/docker-compose-cluster.yaml @@ -1,6 +1,6 @@ # Before starting, provide certs/ca/ca.crt and node.crt/node.key under each node directory. -# Each node certificate must include localhost, its service DNS name, and its configured replication IP in its -# subject alternative names. The replication IPs are 172.30.240.11, 172.30.240.12, and 172.30.240.13. +# Each node certificate must include localhost, its service DNS name, and its configured node IP in its +# subject alternative names. The node IPs are 172.30.240.11, 172.30.240.12, and 172.30.240.13. # Keep each node private key readable only by the account running the container. services: node1.eventstore: &template @@ -10,7 +10,7 @@ services: env_file: - vars.env environment: - - EVENTSTORE_REPLICATION_IP=172.30.240.11 + - EVENTSTORE_NODE_IP=172.30.240.11 - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2111 - EVENTSTORE_GOSSIP_SEED=172.30.240.12:2113,172.30.240.13:2113 - EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/certs/ca @@ -43,7 +43,7 @@ services: <<: *template container_name: node2.eventstore environment: - - EVENTSTORE_REPLICATION_IP=172.30.240.12 + - EVENTSTORE_NODE_IP=172.30.240.12 - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2112 - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.13:2113 - EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/certs/ca @@ -75,7 +75,7 @@ services: <<: *template container_name: node3.eventstore environment: - - EVENTSTORE_REPLICATION_IP=172.30.240.13 + - EVENTSTORE_NODE_IP=172.30.240.13 - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2113 - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.12:2113 - EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/certs/ca diff --git a/scripts/test.sh b/scripts/test.sh index 54c3fc0363..a7ed1e5b29 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -5,10 +5,6 @@ set -o xtrace update-ca-certificates -core_clientapi_projects=( - EventStore.Core.Tests -) - core_rest_projects=( EventStore.Core.Tests ) @@ -52,6 +48,7 @@ misc_projects=( EventStore.Common.Tests EventStore.SourceGenerators.Tests EventStore.SystemRuntime.Tests + EventStore.TestClient.Tests ) declare -a requested_projects @@ -61,18 +58,6 @@ load_requested_projects() { all) requested_projects=() ;; - core-clientapi) - requested_projects=("${core_clientapi_projects[@]}") - ;; - core-clientapi-persistent) - requested_projects=("${core_clientapi_projects[@]}") - ;; - core-clientapi-security) - requested_projects=("${core_clientapi_projects[@]}") - ;; - core-clientapi-streams) - requested_projects=("${core_clientapi_projects[@]}") - ;; core-rest) requested_projects=("${core_rest_projects[@]}") ;; @@ -115,8 +100,7 @@ validate_shard_coverage() { declared_projects="$( printf '%s\n' \ - "${core_clientapi_projects[@]}" \ - "${core_rest_projects[@]}" \ + "${core_rest_projects[@]}" \ "${core_services_projects[@]}" \ "${core_cluster_services_projects[@]}" \ "${core_storage_projects[@]}" \ @@ -203,23 +187,11 @@ project_filter() { local proj="$1" case "${TEST_GROUP:-all}:$proj" in - core-clientapi:EventStore.Core.Tests) - printf '%s\n' "FullyQualifiedName~EventStore.Core.Tests.ClientAPI" - ;; - core-clientapi-persistent:EventStore.Core.Tests) - printf '%s\n' "FullyQualifiedName~EventStore.Core.Tests.ClientAPI&(FullyQualifiedName~persistent|FullyQualifiedName~Persistent)&FullyQualifiedName!~EventStore.Core.Tests.ClientAPI.Security" - ;; - core-clientapi-security:EventStore.Core.Tests) - printf '%s\n' "FullyQualifiedName~EventStore.Core.Tests.ClientAPI.Security" - ;; - core-clientapi-streams:EventStore.Core.Tests) - printf '%s\n' "FullyQualifiedName~EventStore.Core.Tests.ClientAPI&FullyQualifiedName!~persistent&FullyQualifiedName!~Persistent&FullyQualifiedName!~EventStore.Core.Tests.ClientAPI.Security" - ;; core-http:EventStore.Core.Tests) - printf '%s\n' "(FullyQualifiedName~EventStore.Core.Tests.Http|FullyQualifiedName~EventStore.Core.Tests.Services.Transport.Http)&FullyQualifiedName!~EventStore.Core.Tests.ClientAPI" + printf '%s\n' "(FullyQualifiedName~EventStore.Core.Tests.Http|FullyQualifiedName~EventStore.Core.Tests.Services.Transport.Http)" ;; core-services:EventStore.Core.Tests) - printf '%s\n' "((FullyQualifiedName~EventStore.Core.Tests.Services&FullyQualifiedName!~EventStore.Core.Tests.Services.Storage&FullyQualifiedName!~EventStore.Core.Tests.Services.Transport.Http&FullyQualifiedName!~EventStore.Core.Tests.Services.Transport.Grpc.ServerFeaturesTests&FullyQualifiedName!~EventStore.Core.Tests.Services.ElectionsService)|FullyQualifiedName~EventStore.Core.Tests.Bus|FullyQualifiedName~EventStore.Core.Tests.Helpers|FullyQualifiedName~EventStore.Core.Tests.ClientOperations|FullyQualifiedName~EventStore.Core.Tests.Authentication|FullyQualifiedName~EventStore.Core.Tests.Authorization|FullyQualifiedName~EventStore.Core.Tests.Certificates|FullyQualifiedName~EventStore.Core.Tests.AwakeService|FullyQualifiedName~EventStore.Core.Tests.Settings|FullyQualifiedName~EventStore.Core.Tests.TcpApiTestPlugin)" + printf '%s\n' "((FullyQualifiedName~EventStore.Core.Tests.Services&FullyQualifiedName!~EventStore.Core.Tests.Services.Storage&FullyQualifiedName!~EventStore.Core.Tests.Services.Transport.Http&FullyQualifiedName!~EventStore.Core.Tests.Services.Transport.Grpc.ServerFeaturesTests&FullyQualifiedName!~EventStore.Core.Tests.Services.ElectionsService)|FullyQualifiedName~EventStore.Core.Tests.Bus|FullyQualifiedName~EventStore.Core.Tests.Helpers|FullyQualifiedName~EventStore.Core.Tests.ClientOperations|FullyQualifiedName~EventStore.Core.Tests.Authentication|FullyQualifiedName~EventStore.Core.Tests.Authorization|FullyQualifiedName~EventStore.Core.Tests.Certificates|FullyQualifiedName~EventStore.Core.Tests.AwakeService|FullyQualifiedName~EventStore.Core.Tests.Settings)" ;; core-cluster-services:EventStore.Core.Tests) printf '%s\n' "(FullyQualifiedName~EventStore.Core.Tests.Integration|FullyQualifiedName~EventStore.Core.Tests.Cluster|FullyQualifiedName~EventStore.Core.Tests.Replication|FullyQualifiedName~EventStore.Core.Tests.Synchronization|FullyQualifiedName~EventStore.Core.Tests.Services.ElectionsService|FullyQualifiedName~EventStore.Core.Tests.Services.Transport.Grpc.ServerFeaturesTests)" @@ -234,7 +206,7 @@ project_filter() { printf '%s\n' "FullyQualifiedName~EventStore.Core.Tests.Transforms" ;; core-rest:EventStore.Core.Tests) - printf '%s\n' "FullyQualifiedName!~EventStore.Core.Tests.ClientAPI&FullyQualifiedName!~EventStore.Core.Tests.Http&FullyQualifiedName!~EventStore.Core.Tests.Services&FullyQualifiedName!~EventStore.Core.Tests.Integration&FullyQualifiedName!~EventStore.Core.Tests.Cluster&FullyQualifiedName!~EventStore.Core.Tests.Bus&FullyQualifiedName!~EventStore.Core.Tests.Helpers&FullyQualifiedName!~EventStore.Core.Tests.ClientOperations&FullyQualifiedName!~EventStore.Core.Tests.Authentication&FullyQualifiedName!~EventStore.Core.Tests.Authorization&FullyQualifiedName!~EventStore.Core.Tests.Certificates&FullyQualifiedName!~EventStore.Core.Tests.AwakeService&FullyQualifiedName!~EventStore.Core.Tests.Replication&FullyQualifiedName!~EventStore.Core.Tests.Settings&FullyQualifiedName!~EventStore.Core.Tests.Synchronization&FullyQualifiedName!~EventStore.Core.Tests.TcpApiTestPlugin&FullyQualifiedName!~EventStore.Core.Tests.Index&FullyQualifiedName!~EventStore.Core.Tests.TransactionLog&FullyQualifiedName!~EventStore.Core.Tests.Caching&FullyQualifiedName!~EventStore.Core.Tests.DataStructures&FullyQualifiedName!~EventStore.Core.Tests.Transforms&FullyQualifiedName!~EventStore.Core.Tests.Hashes" + printf '%s\n' "FullyQualifiedName!~EventStore.Core.Tests.Http&FullyQualifiedName!~EventStore.Core.Tests.Services&FullyQualifiedName!~EventStore.Core.Tests.Integration&FullyQualifiedName!~EventStore.Core.Tests.Cluster&FullyQualifiedName!~EventStore.Core.Tests.Bus&FullyQualifiedName!~EventStore.Core.Tests.Helpers&FullyQualifiedName!~EventStore.Core.Tests.ClientOperations&FullyQualifiedName!~EventStore.Core.Tests.Authentication&FullyQualifiedName!~EventStore.Core.Tests.Authorization&FullyQualifiedName!~EventStore.Core.Tests.Certificates&FullyQualifiedName!~EventStore.Core.Tests.AwakeService&FullyQualifiedName!~EventStore.Core.Tests.Replication&FullyQualifiedName!~EventStore.Core.Tests.Settings&FullyQualifiedName!~EventStore.Core.Tests.Synchronization&FullyQualifiedName!~EventStore.Core.Tests.Index&FullyQualifiedName!~EventStore.Core.Tests.TransactionLog&FullyQualifiedName!~EventStore.Core.Tests.Caching&FullyQualifiedName!~EventStore.Core.Tests.DataStructures&FullyQualifiedName!~EventStore.Core.Tests.Transforms&FullyQualifiedName!~EventStore.Core.Tests.Hashes" ;; esac } @@ -243,18 +215,6 @@ project_timeout() { local proj="$1" case "${TEST_GROUP:-all}:$proj" in - core-clientapi:EventStore.Core.Tests) - printf '%s\n' "20m" - ;; - core-clientapi-persistent:EventStore.Core.Tests) - printf '%s\n' "20m" - ;; - core-clientapi-security:EventStore.Core.Tests) - printf '%s\n' "20m" - ;; - core-clientapi-streams:EventStore.Core.Tests) - printf '%s\n' "20m" - ;; core-rest:EventStore.Core.Tests) printf '%s\n' "15m" ;; diff --git a/shared.env b/shared.env index 52fb417492..1bce5012ba 100644 --- a/shared.env +++ b/shared.env @@ -1,6 +1,5 @@ EVENTSTORE_CLUSTER_SIZE=3 EVENTSTORE_RUN_PROJECTIONS=All -EVENTSTORE_REPLICATION_PORT=1112 EVENTSTORE_NODE_PORT=2113 EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca EVENTSTORE_DISCOVER_VIA_DNS=false diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index cf693dd4db..252802db38 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -10,8 +10,8 @@ - - + + diff --git a/src/EventStore.ClusterNode/Components/Pages/Cluster.razor b/src/EventStore.ClusterNode/Components/Pages/Cluster.razor index 47d3c0279e..e72be103d8 100644 --- a/src/EventStore.ClusterNode/Components/Pages/Cluster.razor +++ b/src/EventStore.ClusterNode/Components/Pages/Cluster.razor @@ -43,8 +43,7 @@ Status Timestamp (UTC) Checkpoints - TCP - HTTP + HTTP / gRPC Actions @@ -52,7 +51,7 @@ @if (!ClusterMembers.Any()) { - @ClusterEmptyMessage + @ClusterEmptyMessage } else @@ -77,10 +76,6 @@

@EpochLabel(member)

} - -

Internal: @InternalTcpEndpoint(member)

-

External: @ExternalTcpEndpoint(member)

- @HttpEndpoint(member)
@@ -307,7 +302,7 @@
- +
@@ -370,9 +365,7 @@ .Append("Snapshot taken at ") .Append(TimestampLabel(ClusterReadAt ?? DateTime.UtcNow)) .AppendLine() - .Append(PadRight("Internal Tcp", 31)).Append(' ') - .Append(PadRight("External Tcp", 31)).Append(' ') - .Append(PadRight("Http", 23)).Append(' ') + .Append(PadRight("HTTP / gRPC", 23)).Append(' ') .Append(PadRight("Status", 11)).Append(' ') .Append(PadRight("State", 18)).Append(' ') .Append(PadRight("Timestamp (UTC)", 19)).Append(" Checkpoints"); @@ -380,8 +373,6 @@ foreach (var member in ClusterMembers) { builder.AppendLine() - .Append(PadRight(InternalTcpEndpoint(member), 31)).Append(' ') - .Append(PadRight(ExternalTcpEndpoint(member), 31)).Append(' ') .Append(PadRight(HttpEndpoint(member), 23)).Append(' ') .Append(PadRight(MemberStatus(member), 11)).Append(' ') .Append(PadRight(member.State.ToString(), 18)).Append(' ') @@ -500,16 +491,6 @@ private static string MemberStatus(ClientClusterInfo.ClientMemberInfo member) => member.IsAlive ? "Alive" : "Unreachable"; - private static string InternalTcpEndpoint(ClientClusterInfo.ClientMemberInfo member) => - Endpoint( - member.InternalTcpIp, - member.InternalSecureTcpPort != 0 ? member.InternalSecureTcpPort : member.InternalTcpPort); - - private static string ExternalTcpEndpoint(ClientClusterInfo.ClientMemberInfo member) => - Endpoint( - member.ExternalTcpIp, - member.ExternalSecureTcpPort != 0 ? member.ExternalSecureTcpPort : member.ExternalTcpPort); - private static string HttpEndpoint(ClientClusterInfo.ClientMemberInfo member) => Endpoint(member.HttpEndPointIp, member.HttpEndPointPort); diff --git a/src/EventStore.ClusterNode/Components/Pages/Observability.razor b/src/EventStore.ClusterNode/Components/Pages/Observability.razor index 8d3540afe8..00fd36a002 100644 --- a/src/EventStore.ClusterNode/Components/Pages/Observability.razor +++ b/src/EventStore.ClusterNode/Components/Pages/Observability.razor @@ -10,7 +10,7 @@

Observability

A map of the signals the node already emits.

-

Inspect live queue pressure, processing throughput, health, metrics, replication, TCP, and grouped runtime statistics.

+

Inspect live queue pressure, processing throughput, health, metrics, replication, and grouped runtime statistics.

@@ -20,6 +20,112 @@
+
+
+
+

Network boundary

+

Shared HTTP and gRPC connections

+

Active connections accepted by the node endpoint, including per-second traffic rates and pending bytes.

+
+
+ @NetworkStatusLabel + @NetworkPageStatusLabel + + +
+
+ +
+
+ + + + + + + + + + + + + + + + + @if (NetworkRows.Count == 0) + { + + } + else + { + foreach (var connection in NetworkRows.Take(5)) + { + + + + + + + + + + + + + } + } + +
ConnectionClientTypeRemote endpointSent rateSent currentSent pendingReceived rateReceived currentReceived pending
No active shared-endpoint connections.
@connection.ConnectionId@Display(connection.ClientName)@connection.Application · @Display(connection.Protocol) · @(connection.IsTls ? "TLS" : "Cleartext")@connection.RemoteEndPointWaiting@FormatBytes(connection.TotalBytesSent)@FormatBytes(connection.PendingSendBytes)Waiting@FormatBytes(connection.TotalBytesReceived)@FormatBytes(connection.PendingReceivedBytes)
+
+
+
+ +
+
+

Cluster transport

+

gRPC replication connections

+

Live database replication sessions on the shared HTTP/2 endpoint.

+
+ +
+
+ + + + + + + + + + + + + @if (Page is not null && Page.ReplicationConnections.Count > 0) + { + @foreach (var connection in Page.ReplicationConnections) + { + + + + + + + + + } + } + else + { + + } + +
EndpointConnectionSentReceivedPendingSend queue
@connection.Endpoint@connection.ConnectionId@FormatBytes(connection.TotalBytesSent)@FormatBytes(connection.TotalBytesReceived)@FormatBytes(connection.PendingSendBytes + connection.PendingReceivedBytes)@connection.SendQueueSize.ToString("N0", CultureInfo.InvariantCulture)
No active gRPC replication connections.
+
+
+
+
Dashboard snapshot @@ -87,58 +193,6 @@ }
-
-
-
-

TCP dashboard

-

Realtime connections

-

Shows active connections with per-second sent and received byte rates.

-
-
- @TcpStatusLabel - @TcpPageStatusLabel - - -
-
- -
-
- - - - - - - - - - - - - - - - - @if (TcpRows.Count == 0) - { - - - - } - else - { - foreach (var connection in TcpRows.Take(5)) - { - @RenderTcpRow(connection) - } - } - -
ConnectionClientTypeIP AddressSent rateSent currentSent pendingReceived rateReceived currentReceived pending
@TcpEmptyMessage
-
-
-
- @code { @@ -148,29 +202,21 @@ private string Expanded { get; set; } = ""; private string DashboardPayloadJson => Page?.ClientPayloadJson ?? "{}"; - private IReadOnlyList TcpRows => Page?.TcpConnections ?? Array.Empty(); - private string TcpErrorMessage => !string.IsNullOrWhiteSpace(Page?.TcpMessage) - ? Page.TcpMessage - : Page?.Message ?? ""; - private string TcpStatusLabel { - get { - if (Page is null) - return "Connecting TCP stats..."; - - if (!string.IsNullOrWhiteSpace(TcpErrorMessage)) - return "TCP unavailable"; - - return TcpRows.Count == 0 - ? "TCP live" - : string.Create(CultureInfo.InvariantCulture, $"TCP live · {TcpRows.Count} connection{(TcpRows.Count == 1 ? "" : "s")}"); - } - } - private string TcpPageStatusLabel => TcpRows.Count == 0 + private IReadOnlyList NetworkRows => + Page?.NodeConnections ?? Array.Empty(); + private string NetworkStatusLabel => NetworkRows.Count == 0 + ? "Network live" + : string.Create(CultureInfo.InvariantCulture, $"Network live · {NetworkRows.Count} connection{(NetworkRows.Count == 1 ? "" : "s")}"); + private string NetworkPageStatusLabel => NetworkRows.Count == 0 ? "No pages" - : string.Create(CultureInfo.InvariantCulture, $"Page 1 of {Math.Max(1, (int)Math.Ceiling(TcpRows.Count / 5.0))}"); - private string TcpEmptyMessage => string.IsNullOrWhiteSpace(TcpErrorMessage) - ? "No TCP connections are currently reported." - : TcpErrorMessage; + : string.Create(CultureInfo.InvariantCulture, $"Page 1 of {Math.Max(1, (int)Math.Ceiling(NetworkRows.Count / 5.0))}"); + + private static string FormatBytes(long value) => + value.ToString("N0", CultureInfo.InvariantCulture) + " B"; + private static string Display(string value) => string.IsNullOrWhiteSpace(value) ? "" : value; + private static string ConnectionDetails(NodeConnectionSnapshot connection) => + string.Create(CultureInfo.InvariantCulture, + $"Local {connection.LocalEndPoint}, connected {connection.ConnectedAt:u}"); protected override async Task OnParametersSetAsync() { Page = null; @@ -216,19 +262,6 @@ @row.CurrentLastMessageLabel ; - private RenderFragment RenderTcpRow(TcpConnectionRow connection) => @ - @connection.IdLabel - @connection.ClientLabel - @connection.TypeLabel - @connection.RemoteEndPointLabel - @connection.SentRateLabel - @connection.TotalBytesSentLabel - @connection.PendingSendBytesLabel - @connection.ReceivedRateLabel - @connection.TotalBytesReceivedLabel - @connection.PendingReceivedBytesLabel - ; - private bool IsExpanded(string groupName) => ParseExpandedGroups().Contains(groupName); diff --git a/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs b/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs index 5c753987ec..78ca5f48ac 100644 --- a/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs +++ b/src/EventStore.ClusterNode/Components/Services/ClusterStatusService.cs @@ -171,7 +171,7 @@ private ClusterReplicaRow ParseReplicaRow( : Guid.Empty; var totalBytesSent = row.TotalBytesSent; var previousRow = _previousReplicas.GetValueOrDefault(connectionId); - var replicaNode = FindMemberByInternalEndpoint(members, row.SubscriptionEndpoint); + var replicaNode = FindMemberByEndpoint(members, row.SubscriptionEndpoint); var isCatchingUp = replicaNode?.State == VNodeState.CatchingUp; var catchupStartTime = now; var catchupStartBytesSent = totalBytesSent; @@ -206,12 +206,12 @@ private ClusterReplicaRow ParseReplicaRow( private ClaimsPrincipal CurrentUser => httpContextAccessor.HttpContext?.User ?? new ClaimsPrincipal(new ClaimsIdentity()); - private static ClientClusterInfo.ClientMemberInfo FindMemberByInternalEndpoint( + private static ClientClusterInfo.ClientMemberInfo FindMemberByEndpoint( IReadOnlyList members, string endpoint) { var cleaned = endpoint.Replace("Unspecified/", "", StringComparison.OrdinalIgnoreCase); - return members.FirstOrDefault(x => string.Equals(InternalTcpEndpoint(x), cleaned, StringComparison.OrdinalIgnoreCase)); + return members.FirstOrDefault(x => string.Equals(HttpEndpoint(x), cleaned, StringComparison.OrdinalIgnoreCase)); } private static Uri BuildLeaderAddress( @@ -219,11 +219,6 @@ private static Uri BuildLeaderAddress( ClientClusterInfo.ClientMemberInfo leader) => new UriBuilder(request.Scheme, leader.HttpEndPointIp, leader.HttpEndPointPort).Uri; - private static string InternalTcpEndpoint(ClientClusterInfo.ClientMemberInfo member) => - Endpoint( - member.InternalTcpIp, - member.InternalSecureTcpPort != 0 ? member.InternalSecureTcpPort : member.InternalTcpPort); - private static string HttpEndpoint(ClientClusterInfo.ClientMemberInfo member) => Endpoint(member.HttpEndPointIp, member.HttpEndPointPort); diff --git a/src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs b/src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs new file mode 100644 index 0000000000..8b98cc7262 --- /dev/null +++ b/src/EventStore.ClusterNode/Components/Services/NodeConnectionTracker.cs @@ -0,0 +1,278 @@ +using System; +using System.Buffers; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO.Pipelines; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; + +namespace EventStore.ClusterNode.Components.Services; + +public sealed class NodeConnectionTracker +{ + private readonly ConcurrentDictionary _connections = new(); + + public IReadOnlyList Snapshot() => + _connections.Values.Select(x => x.Snapshot()) + .OrderBy(x => x.RemoteEndPoint, StringComparer.OrdinalIgnoreCase) + .ThenBy(x => x.ConnectionId, StringComparer.Ordinal) + .ToArray(); + + public async Task Track(ConnectionContext context, ConnectionDelegate next, bool isTls) + { + var state = new NodeConnectionState( + context.ConnectionId, + context.RemoteEndPoint?.ToString() ?? "", + context.LocalEndPoint?.ToString() ?? "", + isTls, + DateTimeOffset.UtcNow); + _connections[context.ConnectionId] = state; + context.Transport = new CountingDuplexPipe(context.Transport, state); + + try + { + await next(context); + } + finally + { + _connections.TryRemove(context.ConnectionId, out _); + } + } + + public void ObserveRequest( + string connectionId, + string protocol, + bool isGrpc, + string connectionName, + string userAgent) + { + if (_connections.TryGetValue(connectionId, out var connection)) + connection.ObserveRequest(protocol, isGrpc, connectionName, userAgent); + } +} + +public sealed record NodeConnectionSnapshot( + string ConnectionId, + string RemoteEndPoint, + string LocalEndPoint, + string ClientName, + string Application, + string Protocol, + bool IsTls, + DateTimeOffset ConnectedAt, + long TotalBytesSent, + long TotalBytesReceived, + long PendingSendBytes, + long PendingReceivedBytes); + +internal sealed class NodeConnectionState +{ + private readonly object _metadataLock = new(); + private string _clientName = ""; + private bool _hasExplicitConnectionName; + private bool _hasGrpcRequests; + private bool _hasHttpRequests; + private long _pendingReceivedBytes; + private long _pendingSendBytes; + private string _protocol = ""; + private long _totalBytesReceived; + private long _totalBytesSent; + + public NodeConnectionState( + string connectionId, + string remoteEndPoint, + string localEndPoint, + bool isTls, + DateTimeOffset connectedAt) + { + ConnectionId = connectionId; + RemoteEndPoint = remoteEndPoint; + LocalEndPoint = localEndPoint; + IsTls = isTls; + ConnectedAt = connectedAt; + } + + private string ConnectionId { get; } + private string RemoteEndPoint { get; } + private string LocalEndPoint { get; } + private bool IsTls { get; } + private DateTimeOffset ConnectedAt { get; } + + public void Received(long bytes, long pendingBytes) + { + Interlocked.Add(ref _totalBytesReceived, bytes); + Interlocked.Exchange(ref _pendingReceivedBytes, pendingBytes); + } + + public void Reading(long pendingBytes) => + Interlocked.Exchange(ref _pendingReceivedBytes, pendingBytes); + + public void Sending(int bytes) + { + Interlocked.Add(ref _totalBytesSent, bytes); + Interlocked.Add(ref _pendingSendBytes, bytes); + } + + public void Sent() => Interlocked.Exchange(ref _pendingSendBytes, 0); + + public void ObserveRequest( + string protocol, + bool isGrpc, + string connectionName, + string userAgent) + { + lock (_metadataLock) + { + _protocol = Merge(_protocol, protocol); + _hasGrpcRequests |= isGrpc; + _hasHttpRequests |= !isGrpc; + + if (!string.IsNullOrWhiteSpace(connectionName)) + { + _clientName = connectionName; + _hasExplicitConnectionName = true; + } + else if (!_hasExplicitConnectionName && !string.IsNullOrWhiteSpace(userAgent)) + { + _clientName = userAgent; + } + } + } + + public NodeConnectionSnapshot Snapshot() + { + lock (_metadataLock) + { + return new( + ConnectionId, + RemoteEndPoint, + LocalEndPoint, + _clientName, + ApplicationLabel(), + _protocol, + IsTls, + ConnectedAt, + Interlocked.Read(ref _totalBytesSent), + Interlocked.Read(ref _totalBytesReceived), + Interlocked.Read(ref _pendingSendBytes), + Interlocked.Read(ref _pendingReceivedBytes)); + } + } + + private string ApplicationLabel() => (_hasHttpRequests, _hasGrpcRequests) switch + { + (true, true) => "HTTP and gRPC", + (false, true) => "gRPC", + (true, false) => "HTTP", + _ => "Awaiting request" + }; + + private static string Merge(string current, string observed) + { + if (string.IsNullOrWhiteSpace(observed) || current == observed) + return current; + return string.IsNullOrWhiteSpace(current) ? observed : "Mixed"; + } +} + +internal sealed class CountingDuplexPipe : IDuplexPipe +{ + public CountingDuplexPipe(IDuplexPipe inner, NodeConnectionState state) + { + Input = new CountingPipeReader(inner.Input, state); + Output = new CountingPipeWriter(inner.Output, state); + } + + public PipeReader Input { get; } + public PipeWriter Output { get; } +} + +internal sealed class CountingPipeReader : PipeReader +{ + private readonly PipeReader _inner; + private readonly NodeConnectionState _state; + private ReadOnlySequence _currentBuffer; + + public CountingPipeReader(PipeReader inner, NodeConnectionState state) + { + _inner = inner; + _state = state; + } + + public override void AdvanceTo(SequencePosition consumed) => AdvanceTo(consumed, consumed); + + public override void AdvanceTo(SequencePosition consumed, SequencePosition examined) + { + var consumedBytes = _currentBuffer.IsEmpty ? 0 : _currentBuffer.Slice(0, consumed).Length; + var pendingBytes = _currentBuffer.IsEmpty ? 0 : _currentBuffer.Slice(consumed).Length; + _state.Received(consumedBytes, pendingBytes); + _currentBuffer = default; + _inner.AdvanceTo(consumed, examined); + } + + public override void CancelPendingRead() => _inner.CancelPendingRead(); + + public override void Complete(Exception exception = null) => _inner.Complete(exception); + + public override ValueTask CompleteAsync(Exception exception = null) => _inner.CompleteAsync(exception); + + public override async ValueTask ReadAsync(CancellationToken cancellationToken = default) + { + var result = await _inner.ReadAsync(cancellationToken); + Observe(result); + return result; + } + + public override bool TryRead(out ReadResult result) + { + if (!_inner.TryRead(out result)) + return false; + + Observe(result); + return true; + } + + private void Observe(ReadResult result) + { + _currentBuffer = result.Buffer; + _state.Reading(result.Buffer.Length); + } +} + +internal sealed class CountingPipeWriter : PipeWriter +{ + private readonly PipeWriter _inner; + private readonly NodeConnectionState _state; + + public CountingPipeWriter(PipeWriter inner, NodeConnectionState state) + { + _inner = inner; + _state = state; + } + + public override void Advance(int bytes) + { + _state.Sending(bytes); + _inner.Advance(bytes); + } + + public override void CancelPendingFlush() => _inner.CancelPendingFlush(); + + public override void Complete(Exception exception = null) => _inner.Complete(exception); + + public override ValueTask CompleteAsync(Exception exception = null) => _inner.CompleteAsync(exception); + + public override async ValueTask FlushAsync(CancellationToken cancellationToken = default) + { + var result = await _inner.FlushAsync(cancellationToken); + if (!result.IsCanceled) + _state.Sent(); + return result; + } + + public override Memory GetMemory(int sizeHint = 0) => _inner.GetMemory(sizeHint); + + public override Span GetSpan(int sizeHint = 0) => _inner.GetSpan(sizeHint); +} diff --git a/src/EventStore.ClusterNode/Components/Services/QueueDashboardService.cs b/src/EventStore.ClusterNode/Components/Services/QueueDashboardService.cs index 6cba5ae36b..84d2642921 100644 --- a/src/EventStore.ClusterNode/Components/Services/QueueDashboardService.cs +++ b/src/EventStore.ClusterNode/Components/Services/QueueDashboardService.cs @@ -18,23 +18,22 @@ public sealed class QueueDashboardService { private static readonly TimeSpan ReadTimeout = TimeSpan.FromSeconds(10); private static readonly Operation StatisticsOperation = new(Operations.Node.Statistics.Read); - private static readonly Operation TcpStatisticsOperation = new(Operations.Node.Statistics.Tcp); private readonly IAuthorizationProvider _authorizationProvider; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IPublisher _monitoringQueue; - private readonly object _tcpGate = new(); - private Dictionary _previousTcpConnections = new(); - private DateTime? _lastTcpRefresh; + private readonly NodeConnectionTracker _nodeConnectionTracker; public QueueDashboardService( IAuthorizationProvider authorizationProvider, IHttpContextAccessor httpContextAccessor, - StandardComponents standardComponents) + StandardComponents standardComponents, + NodeConnectionTracker nodeConnectionTracker) { _authorizationProvider = authorizationProvider; _httpContextAccessor = httpContextAccessor; _monitoringQueue = standardComponents.MonitoringQueue; + _nodeConnectionTracker = nodeConnectionTracker; } public async Task Read(CancellationToken cancellationToken = default) @@ -49,9 +48,13 @@ public async Task Read(CancellationToken cancellationToken = using var timeout = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); timeout.CancelAfter(ReadTimeout); - var queues = await ReadQueueStats(timeout.Token); - var tcp = await ReadTcpStatsSafe(timeout.Token, cancellationToken); - return QueueDashboardPage.Success(queues, tcp.Rows, tcp.Message); + var queuesTask = ReadQueueStats(timeout.Token); + var replicationConnectionsTask = ReadReplicationStats(timeout.Token); + await Task.WhenAll(queuesTask, replicationConnectionsTask); + return QueueDashboardPage.Success( + await queuesTask, + await replicationConnectionsTask, + _nodeConnectionTracker.Snapshot()); } catch (TimeoutException) { @@ -102,67 +105,17 @@ private async Task> ReadQueueStats(Cancellation return queues; } - private async Task ReadTcpStats(CancellationToken cancellationToken) - { - if (!await HasAccess(TcpStatisticsOperation, cancellationToken)) - { - return new TcpConnectionResult(Array.Empty(), "TCP statistics access was denied."); - } - - var envelope = new TaskCompletionEnvelope(); - _monitoringQueue.Publish(new MonitoringMessage.GetFreshTcpConnectionStats(envelope)); - var completed = await envelope.Task.WaitAsync(ReadTimeout, cancellationToken); - return BuildTcpRows(completed.ConnectionStats ?? []); - } - - private async Task ReadTcpStatsSafe( - CancellationToken timeoutToken, + private async Task> ReadReplicationStats( CancellationToken cancellationToken) { - try - { - return await ReadTcpStats(timeoutToken); - } - catch (TimeoutException) - { - return new TcpConnectionResult(Array.Empty(), "Timed out reading TCP statistics."); - } - catch (OperationCanceledException) - { - if (cancellationToken.IsCancellationRequested) - { - throw; - } - - return new TcpConnectionResult(Array.Empty(), "Timed out reading TCP statistics."); - } - catch (Exception ex) - { - return new TcpConnectionResult( - Array.Empty(), - $"Unable to read TCP statistics: {UiMessages.Friendly(ex)}"); - } - } - - private TcpConnectionResult BuildTcpRows(IReadOnlyList connections) - { - lock (_tcpGate) - { - var now = DateTime.UtcNow; - var elapsedSeconds = _lastTcpRefresh.HasValue - ? Math.Max(1, (now - _lastTcpRefresh.Value).TotalSeconds) - : 1; - - var rows = connections - .Select(x => TcpConnectionRow.From(x, _previousTcpConnections.GetValueOrDefault(x.ConnectionId), elapsedSeconds)) - .OrderBy(x => x.ClientConnectionName, StringComparer.OrdinalIgnoreCase) - .ThenBy(x => x.ConnectionId) - .ToArray(); + var envelope = new TaskCompletionEnvelope(); + _monitoringQueue.Publish(new ReplicationMessage.GetReplicationStats(envelope)); + var completed = await envelope.Task.WaitAsync(ReadTimeout, cancellationToken); - _previousTcpConnections = rows.ToDictionary(x => x.ConnectionId); - _lastTcpRefresh = now; - return new TcpConnectionResult(rows, ""); - } + return completed.ReplicationStats + .Select(ReplicationConnectionRow.From) + .OrderBy(x => x.Endpoint, StringComparer.OrdinalIgnoreCase) + .ToArray(); } } @@ -196,8 +149,8 @@ public static bool TryReadDictionary(object value, out IReadOnlyDictionary Blocks, IReadOnlyList Queues, - IReadOnlyList TcpConnections, - string TcpMessage, + IReadOnlyList ReplicationConnections, + IReadOnlyList NodeConnections, string Message) { private static readonly JsonSerializerOptions PayloadJsonOptions = new(JsonSerializerDefaults.Web); @@ -214,23 +167,28 @@ public sealed record QueueDashboardPage( public string ClientPayloadJson => JsonSerializer.Serialize( new QueueDashboardPayload( Queues.Select(QueuePayload.From).ToArray(), - TcpConnections.Select(TcpConnectionPayload.From).ToArray(), - Message, - TcpMessage), + ReplicationConnections, + NodeConnections, + Message), PayloadJsonOptions); public static QueueDashboardPage Success( IReadOnlyList queues, - IReadOnlyList tcpConnections, - string tcpMessage) => - new(BuildBlocks(queues), queues, tcpConnections, tcpMessage, ""); + IReadOnlyList replicationConnections = null, + IReadOnlyList nodeConnections = null) => + new( + BuildBlocks(queues), + queues, + replicationConnections ?? Array.Empty(), + nodeConnections ?? Array.Empty(), + ""); public static QueueDashboardPage Unavailable(string message) => new( Array.Empty(), Array.Empty(), - Array.Empty(), - message, + Array.Empty(), + Array.Empty(), message); private static IReadOnlyList BuildBlocks(IReadOnlyList queues) @@ -266,15 +224,33 @@ public sealed record QueueDashboardBlock( public bool HasChildren => Children.Count > 0; } -public sealed record TcpConnectionResult( - IReadOnlyList Rows, - string Message); - public sealed record QueueDashboardPayload( IReadOnlyList Queues, - IReadOnlyList TcpConnections, - string Message, - string TcpMessage); + IReadOnlyList ReplicationConnections, + IReadOnlyList NodeConnections, + string Message); + +public sealed record ReplicationConnectionRow( + string SubscriptionId, + string ConnectionId, + string Endpoint, + long TotalBytesSent, + long TotalBytesReceived, + int PendingSendBytes, + int PendingReceivedBytes, + int SendQueueSize) +{ + public static ReplicationConnectionRow From(ReplicationMessage.ReplicationStats stats) => + new( + stats.SubscriptionId.ToString("D"), + stats.ConnectionId.ToString("D"), + stats.SubscriptionEndpoint ?? "", + stats.TotalBytesSent, + stats.TotalBytesReceived, + stats.PendingSendBytes, + stats.PendingReceivedBytes, + stats.SendQueueSize); +} public sealed record QueuePayload( string Kind, @@ -304,95 +280,6 @@ public static QueuePayload From(QueueDashboardRow row) => row.LastProcessedMessage); } -public sealed record TcpConnectionPayload( - Guid ConnectionId, - string ClientConnectionName, - string RemoteEndPoint, - string LocalEndPoint, - long TotalBytesSent, - long TotalBytesReceived, - int PendingSendBytes, - int PendingReceivedBytes, - double SentRate, - double ReceivedRate, - bool IsExternalConnection, - bool IsSslConnection) -{ - public static TcpConnectionPayload From(TcpConnectionRow row) => - new( - row.ConnectionId, - row.ClientConnectionName, - row.RemoteEndPoint, - row.LocalEndPoint, - row.TotalBytesSent, - row.TotalBytesReceived, - row.PendingSendBytes, - row.PendingReceivedBytes, - row.SentRate, - row.ReceivedRate, - row.IsExternalConnection, - row.IsSslConnection); -} - -public sealed record TcpConnectionRow( - Guid ConnectionId, - string ClientConnectionName, - string RemoteEndPoint, - string LocalEndPoint, - long TotalBytesSent, - long TotalBytesReceived, - int PendingSendBytes, - int PendingReceivedBytes, - double SentRate, - double ReceivedRate, - bool IsExternalConnection, - bool IsSslConnection) -{ - public string IdLabel => ConnectionId == Guid.Empty ? "" : ConnectionId.ToString("D"); - public string ClientLabel => DisplayMessage(ClientConnectionName); - public string TypeLabel => $"{(IsExternalConnection ? "External" : "Internal")} {(IsSslConnection ? "TLS" : "TCP")}"; - public string RemoteEndPointLabel => DisplayMessage(RemoteEndPoint); - public string SentRateLabel => FormatByteRate(SentRate); - public string ReceivedRateLabel => FormatByteRate(ReceivedRate); - public string TotalBytesSentLabel => TotalBytesSent.ToString("N0", CultureInfo.InvariantCulture); - public string TotalBytesReceivedLabel => TotalBytesReceived.ToString("N0", CultureInfo.InvariantCulture); - public string PendingSendBytesLabel => PendingSendBytes.ToString("N0", CultureInfo.InvariantCulture); - public string PendingReceivedBytesLabel => PendingReceivedBytes.ToString("N0", CultureInfo.InvariantCulture); - - public static TcpConnectionRow From( - MonitoringMessage.TcpConnectionStats stats, - TcpConnectionRow previous, - double elapsedSeconds) - { - var sentRate = previous is null - ? 0 - : Math.Max(0, (stats.TotalBytesSent - previous.TotalBytesSent) / elapsedSeconds); - var receivedRate = previous is null - ? 0 - : Math.Max(0, (stats.TotalBytesReceived - previous.TotalBytesReceived) / elapsedSeconds); - - return new TcpConnectionRow( - stats.ConnectionId, - stats.ClientConnectionName ?? "", - stats.RemoteEndPoint ?? "", - stats.LocalEndPoint ?? "", - stats.TotalBytesSent, - stats.TotalBytesReceived, - stats.PendingSendBytes, - stats.PendingReceivedBytes, - sentRate, - receivedRate, - stats.IsExternalConnection, - stats.IsSslConnection); - } - - private static string DisplayMessage(string value) => - string.IsNullOrWhiteSpace(value) ? "" : value; - - private static string FormatByteRate(double value) => - $"{Math.Round(value).ToString("N0", CultureInfo.InvariantCulture)} B/s"; -} - public enum QueueDashboardRowKind { Queue, diff --git a/src/EventStore.ClusterNode/Program.cs b/src/EventStore.ClusterNode/Program.cs index 270308e3c5..0759fc776c 100644 --- a/src/EventStore.ClusterNode/Program.cs +++ b/src/EventStore.ClusterNode/Program.cs @@ -273,6 +273,8 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig x.SuppressStatusMessages = true; }); + var nodeConnectionTracker = new NodeConnectionTracker(); + builder.Services.AddSingleton(nodeConnectionTracker); builder.WebHost.ConfigureKestrel(server => { server.Limits.Http2.KeepAlivePingDelay = @@ -281,12 +283,12 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig TimeSpan.FromMilliseconds(options.Grpc.KeepAliveTimeout); server.Listen(options.Interface.NodeIp, options.Interface.NodePort, listenOptions => - ConfigureHttpOptions(listenOptions, hostedService, + ConfigureHttpOptions(listenOptions, hostedService, nodeConnectionTracker, useHttps: !hostedService.Node.DisableHttps)); if (hostedService.Node.EnableUnixSocket) { - TryListenOnUnixSocket(hostedService, server); + TryListenOnUnixSocket(hostedService, server, nodeConnectionTracker); } }); @@ -325,6 +327,19 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig builder.Services.AddSingleton(hostedService); var app = builder.Build(); + app.Use((context, next) => + { + var isGrpc = context.Request.ContentType?.StartsWith( + "application/grpc", + StringComparison.OrdinalIgnoreCase) == true; + nodeConnectionTracker.ObserveRequest( + context.Connection.Id, + context.Request.Protocol, + isGrpc, + context.Request.Headers["connection-name"].FirstOrDefault(), + context.Request.Headers.UserAgent.ToString()); + return next(context); + }); app.UseMiddleware(); hostedService.Node.Startup.Configure(app); if (oauthEnabled) @@ -376,9 +391,14 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig } } - private static void ConfigureHttpOptions(ListenOptions listenOptions, ClusterVNodeHostedService hostedService, + private static void ConfigureHttpOptions( + ListenOptions listenOptions, + ClusterVNodeHostedService hostedService, + NodeConnectionTracker connectionTracker, bool useHttps) { + listenOptions.Use(next => context => connectionTracker.Track(context, next, useHttps)); + if (useHttps) { listenOptions.UseHttps(CreateServerOptionsSelectionCallback(hostedService), null); @@ -390,7 +410,10 @@ private static void ConfigureHttpOptions(ListenOptions listenOptions, ClusterVNo } } - private static void TryListenOnUnixSocket(ClusterVNodeHostedService hostedService, KestrelServerOptions server) + private static void TryListenOnUnixSocket( + ClusterVNodeHostedService hostedService, + KestrelServerOptions server, + NodeConnectionTracker connectionTracker) { if (!RuntimeInformation.IsLinux && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17063)) { @@ -421,7 +444,7 @@ private static void TryListenOnUnixSocket(ClusterVNodeHostedService hostedServic server.ListenUnixSocket(unixSocket, listenOptions => { listenOptions.Use(next => new UnixSocketConnectionMiddleware(next).OnConnectAsync); - ConfigureHttpOptions(listenOptions, hostedService, useHttps: false); + ConfigureHttpOptions(listenOptions, hostedService, connectionTracker, useHttps: false); }); Log.Information("Listening on UNIX domain socket: {unixSocket}", unixSocket); } diff --git a/src/EventStore.ClusterNode/metricsconfig.json b/src/EventStore.ClusterNode/metricsconfig.json index cb1edb93ff..f249180912 100644 --- a/src/EventStore.ClusterNode/metricsconfig.json +++ b/src/EventStore.ClusterNode/metricsconfig.json @@ -236,10 +236,6 @@ "Regex": "CoreMessage-System-.*", "Label": "System" }, - { - "Regex": "CoreMessage-Tcp-.*", - "Label": "Tcp" - }, { "Regex": "ProjectionMessage-.*", "Label": "Projections" diff --git a/src/EventStore.ClusterNode/ui-assets/js/queue-dashboard.js b/src/EventStore.ClusterNode/ui-assets/js/queue-dashboard.js index d4602e7bec..4e059393a4 100644 --- a/src/EventStore.ClusterNode/ui-assets/js/queue-dashboard.js +++ b/src/EventStore.ClusterNode/ui-assets/js/queue-dashboard.js @@ -2,7 +2,7 @@ "use strict"; var pollIntervalMs = 1000; - var tcpPageSize = 5; + var networkPageSize = 5; var dashboards = new WeakMap(); function start() { @@ -20,8 +20,10 @@ expanded: parseExpanded(root), blocks: [], queues: [], - tcpConnections: [], - tcpPage: 0, + networkConnections: [], + networkSamples: new Map(), + networkPage: 0, + replicationConnections: [], timer: null, inFlight: false }; @@ -33,10 +35,10 @@ applyPayload(state, initialPayload); root.addEventListener("click", function (event) { - var tcpPager = event.target.closest("[data-tcp-page]"); - if (tcpPager && root.contains(tcpPager)) { + var networkPager = event.target.closest("[data-network-page]"); + if (networkPager && root.contains(networkPager)) { event.preventDefault(); - changeTcpPage(state, tcpPager.getAttribute("data-tcp-page")); + changeNetworkPage(state, networkPager.getAttribute("data-network-page")); return; } @@ -124,21 +126,14 @@ var parsed = parseQueues(payload); state.queues = parsed.queues; state.blocks = parsed.blocks; - state.tcpConnections = parseTcpConnections(payload); + state.networkConnections = parseNetworkConnections(payload, state); + state.replicationConnections = parseReplicationConnections(payload); setStatus( state.root, payload.message ? "Live stats unavailable" : "Live stats", payload.message || "Updated " + formatTime(new Date())); + setNetworkStatus(state.root, payload.message, state.networkConnections.length); render(state); - - if (state.root.querySelector("[data-tcp-table-body]")) { - var tcpMessage = payload.tcpMessage || payload.message || ""; - setTcpStatus( - state.root, - tcpMessage ? "TCP unavailable" : "TCP live", - tcpMessage || state.tcpConnections.length + " connection" + (state.tcpConnections.length === 1 ? "" : "s")); - renderTcpTable(state.root, state.tcpConnections, state); - } } async function refresh(state) { @@ -159,11 +154,11 @@ } catch (error) { state.queues = []; state.blocks = []; - state.tcpConnections = []; + state.networkConnections = []; + state.replicationConnections = []; setStatus(state.root, "Live stats unavailable", friendlyMessage(error)); - setTcpStatus(state.root, "TCP unavailable", friendlyMessage(error)); + setNetworkStatus(state.root, friendlyMessage(error), 0); render(state); - renderTcpTable(state.root, state.tcpConnections, state); } finally { state.inFlight = false; } @@ -208,32 +203,72 @@ }; } - function parseTcpConnections(payload) { - var rows = payload && Array.isArray(payload.tcpConnections) ? payload.tcpConnections : []; - - return rows.map(function (row) { - var id = readFieldString(row, ["connectionId", "ConnectionId"], ""); - var totalBytesSent = readFieldNumber(row, ["totalBytesSent", "TotalBytesSent"]); - var totalBytesReceived = readFieldNumber(row, ["totalBytesReceived", "TotalBytesReceived"]); + function parseNetworkConnections(payload, state) { + var rows = payload && Array.isArray(payload.nodeConnections) ? payload.nodeConnections : []; + var now = Date.now(); + var nextSamples = new Map(); + var connections = rows.map(function (row) { + var id = readString(row.connectionId, ""); + var totalBytesSent = readNumber(row.totalBytesSent); + var totalBytesReceived = readNumber(row.totalBytesReceived); + var previous = state.networkSamples.get(id); + var elapsedSeconds = previous ? Math.max(0.001, (now - previous.observedAt) / 1000) : 0; + var sentRate = previous ? Math.max(0, totalBytesSent - previous.totalBytesSent) / elapsedSeconds : 0; + var receivedRate = previous + ? Math.max(0, totalBytesReceived - previous.totalBytesReceived) / elapsedSeconds + : 0; + + nextSamples.set(id, { + observedAt: now, + totalBytesSent: totalBytesSent, + totalBytesReceived: totalBytesReceived + }); return { id: id, - clientConnectionName: readFieldString(row, ["clientConnectionName", "ClientConnectionName"], ""), - remoteEndPoint: readFieldString(row, ["remoteEndPoint", "RemoteEndPoint"], ""), - localEndPoint: readFieldString(row, ["localEndPoint", "LocalEndPoint"], ""), + clientName: readString(row.clientName, ""), + application: readString(row.application, "Awaiting request"), + protocol: readString(row.protocol, ""), + remoteEndPoint: readString(row.remoteEndPoint, ""), + localEndPoint: readString(row.localEndPoint, ""), + connectedAt: readString(row.connectedAt, ""), + isTls: Boolean(row.isTls), totalBytesSent: totalBytesSent, totalBytesReceived: totalBytesReceived, - pendingSendBytes: readFieldNumber(row, ["pendingSendBytes", "PendingSendBytes"]), - pendingReceivedBytes: readFieldNumber(row, ["pendingReceivedBytes", "PendingReceivedBytes"]), - sentRate: readFieldNumber(row, ["sentRate", "SentRate"]), - receivedRate: readFieldNumber(row, ["receivedRate", "ReceivedRate"]), - isExternalConnection: readFieldBoolean(row, ["isExternalConnection", "IsExternalConnection"]), - isSslConnection: readFieldBoolean(row, ["isSslConnection", "IsSslConnection"]) + pendingSendBytes: readNumber(row.pendingSendBytes), + pendingReceivedBytes: readNumber(row.pendingReceivedBytes), + sentRate: sentRate, + receivedRate: receivedRate }; }).sort(function (left, right) { - return left.clientConnectionName.localeCompare(right.clientConnectionName, undefined, { sensitivity: "base" }) || + return left.clientName.localeCompare(right.clientName, undefined, { sensitivity: "base" }) || left.id.localeCompare(right.id, undefined, { sensitivity: "base" }); }); + + state.networkSamples = nextSamples; + return connections; + } + + function parseReplicationConnections(payload) { + var rows = payload && Array.isArray(payload.replicationConnections) + ? payload.replicationConnections + : []; + + return rows.map(function (row) { + return { + subscriptionId: readString(row.subscriptionId, ""), + connectionId: readString(row.connectionId, ""), + endpoint: readString(row.endpoint, ""), + totalBytesSent: readNumber(row.totalBytesSent), + totalBytesReceived: readNumber(row.totalBytesReceived), + pendingSendBytes: readNumber(row.pendingSendBytes), + pendingReceivedBytes: readNumber(row.pendingReceivedBytes), + sendQueueSize: readNumber(row.sendQueueSize) + }; + }).sort(function (left, right) { + return left.endpoint.localeCompare(right.endpoint, undefined, { sensitivity: "base" }) || + left.connectionId.localeCompare(right.connectionId, undefined, { sensitivity: "base" }); + }); } function buildBlocks(queues) { @@ -311,6 +346,8 @@ renderSpotlightTable(state.root, state.queues); renderQueueTable(state); renderDashboardSnapshot(state.root, state.blocks); + renderNetworkTable(state); + renderReplicationTable(state.root, state.replicationConnections); } function updateMetrics(root, queues) { @@ -451,32 +488,33 @@ node.textContent = lines.join("\n"); } - function renderTcpTable(root, connections, state) { - var tbody = root.querySelector("[data-tcp-table-body]"); + function renderNetworkTable(state) { + var tbody = state.root.querySelector("[data-network-table-body]"); if (!tbody) return; replaceChildren(tbody); - if (connections.length === 0) { + if (state.networkConnections.length === 0) { var empty = element("tr"); - var cell = element("td", "px-5 py-4 text-es-muted"); - cell.colSpan = 10; - cell.textContent = "No TCP connections are currently reported."; - empty.appendChild(cell); + var emptyCell = element("td", "px-5 py-4 text-es-muted"); + emptyCell.colSpan = 10; + emptyCell.textContent = "No active shared-endpoint connections."; + empty.appendChild(emptyCell); tbody.appendChild(empty); - updateTcpPagination(root, state, 0); + updateNetworkPagination(state, 0); return; } - var pageCount = Math.ceil(connections.length / tcpPageSize); - state.tcpPage = Math.min(state.tcpPage, Math.max(0, pageCount - 1)); - var pageStart = state.tcpPage * tcpPageSize; - connections.slice(pageStart, pageStart + tcpPageSize).forEach(function (connection) { + var pageCount = Math.ceil(state.networkConnections.length / networkPageSize); + state.networkPage = Math.min(state.networkPage, pageCount - 1); + var offset = state.networkPage * networkPageSize; + state.networkConnections.slice(offset, offset + networkPageSize).forEach(function (connection) { var row = element("tr", "bg-white/70 text-es-ink"); - appendText(row, "td", connection.id || "", "max-w-[14rem] truncate px-5 py-4 font-mono text-xs text-es-muted"); - appendText(row, "td", displayMessage(connection.clientConnectionName), "px-5 py-4 font-bold text-es-ink"); - appendText(row, "td", tcpTypeLabel(connection), "px-5 py-4 text-es-muted"); - appendText(row, "td", displayMessage(connection.remoteEndPoint), "px-5 py-4 font-mono text-xs text-es-muted"); + appendText(row, "td", displayMessage(connection.id), "max-w-[14rem] truncate px-5 py-4 font-mono text-xs text-es-muted"); + appendText(row, "td", displayMessage(connection.clientName), "max-w-[16rem] truncate px-5 py-4 font-bold text-es-ink"); + appendText(row, "td", networkTypeLabel(connection), "px-5 py-4 text-es-muted"); + var endpointCell = appendText(row, "td", displayMessage(connection.remoteEndPoint), "px-5 py-4 font-mono text-xs text-es-muted"); + endpointCell.title = networkConnectionDetails(connection); appendText(row, "td", formatByteRate(connection.sentRate), "px-5 py-4 text-right font-mono text-es-ink"); appendText(row, "td", formatInteger(connection.totalBytesSent), "px-5 py-4 text-right font-mono text-es-ink"); appendText(row, "td", formatInteger(connection.pendingSendBytes), "px-5 py-4 text-right font-mono text-es-ink"); @@ -485,17 +523,80 @@ appendText(row, "td", formatInteger(connection.pendingReceivedBytes), "px-5 py-4 text-right font-mono text-es-ink"); tbody.appendChild(row); }); - updateTcpPagination(root, state, pageCount); + + updateNetworkPagination(state, pageCount); + } + + function renderReplicationTable(root, connections) { + var tbody = root.querySelector("[data-replication-table-body]"); + if (!tbody) + return; + + replaceChildren(tbody); + if (connections.length === 0) { + var empty = element("tr"); + var emptyCell = element("td", "px-5 py-4 text-es-muted"); + emptyCell.colSpan = 6; + emptyCell.textContent = "No active gRPC replication connections."; + empty.appendChild(emptyCell); + tbody.appendChild(empty); + return; + } + + connections.forEach(function (connection) { + var row = element("tr", "bg-white/70 text-es-ink"); + appendText(row, "td", connection.endpoint, "px-5 py-4 font-bold text-es-ink"); + var connectionCell = appendText(row, "td", displayMessage(connection.connectionId), "px-5 py-4 font-mono text-xs text-es-muted"); + connectionCell.title = "Subscription " + displayMessage(connection.subscriptionId); + appendText(row, "td", formatInteger(connection.totalBytesSent), "px-5 py-4 text-right font-mono text-es-ink"); + appendText(row, "td", formatInteger(connection.totalBytesReceived), "px-5 py-4 text-right font-mono text-es-ink"); + appendText(row, "td", formatInteger(connection.pendingSendBytes + connection.pendingReceivedBytes), "px-5 py-4 text-right font-mono text-es-ink"); + appendText(row, "td", formatInteger(connection.sendQueueSize), "px-5 py-4 text-right font-mono text-es-ink"); + tbody.appendChild(row); + }); } - function changeTcpPage(state, direction) { - var pageCount = Math.ceil(state.tcpConnections.length / tcpPageSize); + function changeNetworkPage(state, direction) { + var pageCount = Math.ceil(state.networkConnections.length / networkPageSize); if (direction === "previous") - state.tcpPage = Math.max(0, state.tcpPage - 1); + state.networkPage = Math.max(0, state.networkPage - 1); else if (direction === "next") - state.tcpPage = Math.min(Math.max(0, pageCount - 1), state.tcpPage + 1); + state.networkPage = Math.min(Math.max(0, pageCount - 1), state.networkPage + 1); + + renderNetworkTable(state); + } + + function updateNetworkPagination(state, pageCount) { + var status = state.root.querySelector("[data-network-page-status]"); + var previous = state.root.querySelector('[data-network-page="previous"]'); + var next = state.root.querySelector('[data-network-page="next"]'); + var hasRows = state.networkConnections.length > 0; + + if (status) + status.textContent = hasRows ? "Page " + (state.networkPage + 1) + " of " + pageCount : "No pages"; + if (previous) + previous.disabled = !hasRows || state.networkPage === 0; + if (next) + next.disabled = !hasRows || state.networkPage >= pageCount - 1; + } + + function setNetworkStatus(root, message, connectionCount) { + var status = root.querySelector("[data-network-status]"); + if (!status) + return; + + status.textContent = message + ? "Network unavailable · " + message + : "Network live · " + connectionCount + " connection" + (connectionCount === 1 ? "" : "s"); + } + + function networkTypeLabel(connection) { + return connection.application + " · " + connection.protocol + " · " + (connection.isTls ? "TLS" : "Cleartext"); + } - renderTcpTable(state.root, state.tcpConnections, state); + function networkConnectionDetails(connection) { + var connected = connection.connectedAt ? new Date(connection.connectedAt).toLocaleString() : "unknown"; + return "Local " + connection.localEndPoint + ", connected " + connected; } function queueTableRow(queue, block, state) { @@ -554,37 +655,6 @@ updatedNode.textContent = updated; } - function setTcpStatus(root, status, detail) { - var statusNode = root.querySelector("[data-tcp-status]"); - if (!statusNode) - return; - - statusNode.textContent = detail ? status + " · " + detail : status; - } - - function updateTcpPagination(root, state, pageCount) { - var status = root.querySelector("[data-tcp-page-status]"); - var previous = root.querySelector('[data-tcp-page="previous"]'); - var next = root.querySelector('[data-tcp-page="next"]'); - var hasRows = state.tcpConnections.length > 0; - - if (status) - status.textContent = hasRows - ? "Page " + (state.tcpPage + 1) + " of " + pageCount - : "No pages"; - - if (previous) - previous.disabled = !hasRows || state.tcpPage === 0; - - if (next) - next.disabled = !hasRows || state.tcpPage >= pageCount - 1; - } - - function tcpTypeLabel(connection) { - return (connection.isExternalConnection ? "External" : "Internal") + " " + - (connection.isSslConnection ? "TLS" : "TCP"); - } - function currentLastMessage(queue) { return queue.kind === "group" ? "n/a" @@ -662,36 +732,6 @@ return Number.isFinite(number) ? number : 0; } - function readFieldString(source, keys, fallback) { - var value = readField(source, keys); - if (value === null || value === undefined) - return fallback; - - var text = String(value); - return text.trim() ? text : fallback; - } - - function readFieldNumber(source, keys) { - return readNumber(readField(source, keys)); - } - - function readFieldBoolean(source, keys) { - var value = readField(source, keys); - return value === true || String(value).toLowerCase() === "true"; - } - - function readField(source, keys) { - if (!source || typeof source !== "object") - return undefined; - - for (var i = 0; i < keys.length; i++) { - if (Object.prototype.hasOwnProperty.call(source, keys[i])) - return source[keys[i]]; - } - - return undefined; - } - function sum(rows, key) { return rows.reduce(function (total, row) { return total + row[key]; diff --git a/src/EventStore.Core.Tests/Authorization/LegacyPolicyVerification.cs b/src/EventStore.Core.Tests/Authorization/LegacyPolicyVerification.cs index 5eb5403acc..76ae0b674c 100644 --- a/src/EventStore.Core.Tests/Authorization/LegacyPolicyVerification.cs +++ b/src/EventStore.Core.Tests/Authorization/LegacyPolicyVerification.cs @@ -497,7 +497,6 @@ public static IEnumerable PolicyTests() yield return CreateOperation(Operations.Node.Options); yield return CreateOperation(Operations.Node.Statistics.Read); yield return CreateOperation(Operations.Node.Statistics.Replication); - yield return CreateOperation(Operations.Node.Statistics.Tcp); yield return CreateOperation(Operations.Node.Statistics.Custom); } diff --git a/src/EventStore.Core.Tests/ClientAPI/DataStructures/concurrent_queue_wrapper_should.cs b/src/EventStore.Core.Tests/ClientAPI/DataStructures/concurrent_queue_wrapper_should.cs deleted file mode 100644 index 5acd1434dc..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/DataStructures/concurrent_queue_wrapper_should.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Threading; -using EventStore.ClientAPI.Common.Utils.Threading; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.DataStructures; - -[TestFixture] -public class concurrent_queue_wrapper_should -{ - [Test] - public void return_correct_counts_and_values_1() - { - var queue = new ConcurrentQueueWrapper(); - Assert.AreEqual(0, queue.Count); - queue.Enqueue(1234); - Assert.AreEqual(1, queue.Count); - queue.Enqueue(12345); - Assert.AreEqual(2, queue.Count); - - int val; - Assert.AreEqual(true, queue.TryDequeue(out val)); - Assert.AreEqual(1234, val); - Assert.AreEqual(1, queue.Count); - - Assert.AreEqual(true, queue.TryDequeue(out val)); - Assert.AreEqual(12345, val); - Assert.AreEqual(0, queue.Count); - - Assert.AreEqual(false, queue.TryDequeue(out _)); - Assert.AreEqual(false, queue.TryDequeue(out _)); - } - - [Test] - public void return_correct_counts_and_values_2() - { - var queue = new ConcurrentQueueWrapper(); - for (int i = 1; i <= 1000; i++) - { - queue.Enqueue(i); - Assert.AreEqual(i, queue.Count); - } - - int x; - for (int i = 1; i <= 1000; i++) - { - Assert.AreEqual(true, queue.TryDequeue(out x)); - Assert.AreEqual(i, x); - Assert.AreEqual(1000 - i, queue.Count); - } - } -} - -[TestFixture] -public class concurrent_queue_wrapper_with_parallel_dequeues_should -{ - private ConcurrentQueueWrapper _concurrentQueue; - private const int TOTAL_ENQUEUED_ITEMS = 1000000; - private const int NUM_THREADS = 4; - private volatile bool _seenNegativeCount; - private volatile int _totalDequeued; - - public concurrent_queue_wrapper_with_parallel_dequeues_should() - { - _concurrentQueue = new ConcurrentQueueWrapper(); - _seenNegativeCount = false; - _totalDequeued = 0; - } - - private void DequeueThread() - { - while (_totalDequeued < TOTAL_ENQUEUED_ITEMS) - { - var dequeued = _concurrentQueue.TryDequeue(out _); - if (_concurrentQueue.Count < 0) - { - _seenNegativeCount = true; - } - - if (dequeued) - { - Interlocked.Increment(ref _totalDequeued); - } - } - } - - [Test] - public void always_return_non_negative_count() - { - var threads = new Thread[NUM_THREADS]; - for (int i = 0; i < NUM_THREADS; i++) - { - threads[i] = new Thread(DequeueThread); - threads[i].Start(); - } - - int x; - for (int i = 0; i < TOTAL_ENQUEUED_ITEMS; i++) - { - _concurrentQueue.Enqueue(1); - var dequeued = _concurrentQueue.TryDequeue(out x); - if (dequeued) - { - Interlocked.Increment(ref _totalDequeued); - } - } - - for (int i = 0; i < NUM_THREADS; i++) - { - threads[i].Join(TimeSpan.FromSeconds(5)); - } - - Assert.AreEqual(false, _seenNegativeCount); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/MiniNodeWithExistingRecords.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/MiniNodeWithExistingRecords.cs deleted file mode 100644 index 26d2a38dd5..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/MiniNodeWithExistingRecords.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Common.Utils; -using EventStore.Core.Bus; -using EventStore.Core.Data; -using EventStore.Core.Helpers; -using EventStore.Core.Index; -using EventStore.Core.LogAbstraction; -using EventStore.Core.Messaging; -using EventStore.Core.Services.Storage.ReaderIndex; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using EventStore.Core.Tests.TransactionLog; -using EventStore.Core.TransactionLog.Checkpoint; -using EventStore.Core.TransactionLog.Chunks; -using EventStore.Core.TransactionLog.FileNamingStrategy; -using EventStore.Core.TransactionLog.LogRecords; -using EventStore.Core.Util; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -public abstract class MiniNodeWithExistingRecords : SpecificationWithDirectoryPerTestFixture -{ - private readonly TcpType _tcpType = TcpType.Ssl; - protected MiniNode Node; - - protected readonly int MaxEntriesInMemTable = 20; - protected readonly long MetastreamMaxCount = 1; - protected readonly bool PerformAdditionalCommitChecks = true; - protected readonly byte IndexBitnessVersion = Opts.IndexBitnessVersionDefault; - protected LogFormatAbstractor _logFormatFactory; - protected TableIndex TableIndex; - protected IReadIndex ReadIndex; - - protected TFChunkDb Db; - protected TFChunkWriter Writer; - protected ICheckpoint WriterCheckpoint; - protected ICheckpoint ChaserCheckpoint; - protected IODispatcher IODispatcher; - protected SynchronousScheduler Bus; - - protected IEventStoreConnection _store; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, _tcpType); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - string dbPath = Path.Combine(PathName, string.Format("mini-node-db-{0}", Guid.NewGuid())); - - _logFormatFactory = LogFormatHelper.LogFormatFactory.Create(new() - { - IndexDirectory = GetFilePathFor("index"), - }); - - Bus = new(); - IODispatcher = new IODispatcher(Bus, Bus); - - if (!Directory.Exists(dbPath)) - { - Directory.CreateDirectory(dbPath); - } - - var writerCheckFilename = Path.Combine(dbPath, Checkpoint.Writer + ".chk"); - var chaserCheckFilename = Path.Combine(dbPath, Checkpoint.Chaser + ".chk"); - - WriterCheckpoint = new MemoryMappedFileCheckpoint(writerCheckFilename, Checkpoint.Writer); - ChaserCheckpoint = new MemoryMappedFileCheckpoint(chaserCheckFilename, Checkpoint.Chaser); - - Db = new TFChunkDb(TFChunkHelper.CreateDbConfig(dbPath, WriterCheckpoint, ChaserCheckpoint, TFConsts.ChunkSize)); - await Db.Open(); - - // create DB - Writer = new TFChunkWriter(Db); - Writer.Open(); - - var pm = _logFormatFactory.CreatePartitionManager( - reader: new TFChunkReader(Db, WriterCheckpoint), - writer: Writer); - await pm.Initialize(CancellationToken.None); - - await WriteTestScenario(CancellationToken.None); - - await Writer.DisposeAsync(); - Writer = null; - WriterCheckpoint.Flush(); - ChaserCheckpoint.Write(WriterCheckpoint.Read()); - ChaserCheckpoint.Flush(); - await Db.DisposeAsync(); - - // start node with our created DB - Node = new MiniNode(PathName, dbPath: dbPath); - await Node.Start(); - - try - { - await Given().WithTimeout(); - } - catch (Exception ex) - { - throw new Exception("Given Failed", ex); - } - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - _store?.Dispose(); - _logFormatFactory?.Dispose(); - - await Node.Shutdown(); - await base.TestFixtureTearDown(); - } - - public abstract ValueTask WriteTestScenario(CancellationToken token); - public abstract Task Given(); - - protected async ValueTask WriteSingleEvent(string eventStreamName, - long eventNumber, - string data, - DateTime? timestamp = null, - Guid eventId = default(Guid), - string eventType = "some-type", - CancellationToken token = default) - { - - long pos = Writer.Position; - _logFormatFactory.StreamNameIndex.GetOrReserve( - _logFormatFactory.RecordFactory, - eventStreamName, - pos, - out var eventStreamId, - out var streamRecord); - - if (streamRecord != null) - { - (_, pos) = await Writer.Write(streamRecord, token); - } - - _logFormatFactory.EventTypeIndex.GetOrReserveEventType( - _logFormatFactory.RecordFactory, - eventType, - pos, - out var eventTypeId, - out var eventTypeRecord); - - if (eventTypeRecord != null) - { - (_, pos) = await Writer.Write(eventTypeRecord, token); - } - - var prepare = LogRecord.SingleWrite( - _logFormatFactory.RecordFactory, - pos, - eventId == default(Guid) ? Guid.NewGuid() : eventId, - Guid.NewGuid(), - eventStreamId, - eventNumber - 1, - eventTypeId, - Helper.UTF8NoBom.GetBytes(data), - null, - timestamp); - (var written, pos) = await Writer.Write(prepare, token); - Assert.IsTrue(written); - var commit = LogRecord.Commit(pos, prepare.CorrelationId, prepare.LogPosition, - eventNumber); - Assert.IsTrue(await Writer.Write(commit, token) is (true, _)); - Assert.AreEqual(eventStreamId, prepare.EventStreamId); - - var eventRecord = new EventRecord(eventNumber, prepare, eventStreamName, eventType); - return eventRecord; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/append_to_stream_with_event_numbers_greater_than_2_billion.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/append_to_stream_with_event_numbers_greater_than_2_billion.cs deleted file mode 100644 index da9c333c08..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/append_to_stream_with_event_numbers_greater_than_2_billion.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class AppendToStreamWithEventNumbersGreaterThan2Billion - : MiniNodeWithExistingRecords -{ - private const string StreamName = "append_to_stream_with_event_numbers_greater_than_2_billion"; - private const long intMaxValue = int.MaxValue; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - await WriteSingleEvent(StreamName, intMaxValue + 1, new string('.', 3000), token: token); - await WriteSingleEvent(StreamName, intMaxValue + 2, new string('.', 3000), token: token); - await WriteSingleEvent(StreamName, intMaxValue + 3, new string('.', 3000), token: token); - await WriteSingleEvent(StreamName, intMaxValue + 4, new string('.', 3000), token: token); - await WriteSingleEvent(StreamName, intMaxValue + 5, new string('.', 3000), token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - await _store.SetStreamMetadataAsync(StreamName, EventStore.ClientAPI.ExpectedVersion.Any, - StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - } - - [Test] - public async Task should_be_able_to_append_to_stream() - { - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - var writeResult = await _store.AppendToStreamAsync(StreamName, intMaxValue + 5, evnt); - Assert.AreEqual(intMaxValue + 6, writeResult.NextExpectedVersion); - - var readResult = await _store - .ReadStreamEventsForwardAsync(StreamName, intMaxValue + 6, 1, false, DefaultData.AdminCredentials); - Assert.AreEqual(SliceReadStatus.Success, readResult.Status); - Assert.AreEqual(evnt.EventId, readResult.Events[0].Event.EventId); - } - - [Test] - public async Task should_throw_wrong_expected_version_when_version_incorrect() - { - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - await AssertEx.ThrowsAsync(() => - _store.AppendToStreamAsync(StreamName, intMaxValue + 15, evnt)); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/catchup_subscription_to_all_with_event_numbers_greater_than_2_billion.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/catchup_subscription_to_all_with_event_numbers_greater_than_2_billion.cs deleted file mode 100644 index 5874a892a6..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/catchup_subscription_to_all_with_event_numbers_greater_than_2_billion.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Data; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class CatchupSubscriptionToAllWithEventNumbersGreaterThan2Billion - : MiniNodeWithExistingRecords -{ - private const long intMaxValue = (long)int.MaxValue; - - private string _streamId = "subscriptions-catchup-all"; - - private EventRecord _r1, _r2; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - _r1 = await WriteSingleEvent(_streamId, intMaxValue + 1, new string('.', 3000), token: token); - _r2 = await WriteSingleEvent(_streamId, intMaxValue + 2, new string('.', 3000), token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - await _store.SetStreamMetadataAsync(_streamId, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - } - - [Test] - public async Task should_be_able_to_subscribe_to_all_with_catchup_subscription() - { - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - List receivedEvents = new List(); - - var countdown = new CountdownEvent(3); - - _store.SubscribeToAllFrom(Position.Start, CatchUpSubscriptionSettings.Default, (s, e) => - { - if (e.Event.EventStreamId == _streamId) - { - receivedEvents.Add(e); - countdown.Signal(); - } - - return Task.CompletedTask; - }, userCredentials: DefaultData.AdminCredentials); - - await _store.AppendToStreamAsync(_streamId, intMaxValue + 2, evnt); - - Assert.That(countdown.Wait(TimeSpan.FromSeconds(10)), "Timed out waiting for events to appear"); - - Assert.AreEqual(_r1.EventId, receivedEvents[0].Event.EventId); - Assert.AreEqual(_r2.EventId, receivedEvents[1].Event.EventId); - Assert.AreEqual(evnt.EventId, receivedEvents[2].Event.EventId); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/persistent_subscription_with_event_numbers_greater_than_2_billion.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/persistent_subscription_with_event_numbers_greater_than_2_billion.cs deleted file mode 100644 index 6eeacf86b2..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/persistent_subscription_with_event_numbers_greater_than_2_billion.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Data; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class PersistentSubscriptionWithEventNumbersGreaterThan2Billion - : MiniNodeWithExistingRecords -{ - private const long intMaxValue = (long)int.MaxValue; - - private string _streamId = "persistent-subscription-stream"; - - private EventRecord _r1, _r2; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - _r1 = await WriteSingleEvent(_streamId, intMaxValue + 1, new string('.', 3000), token: token); - _r2 = await WriteSingleEvent(_streamId, intMaxValue + 2, new string('.', 3000), token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - await _store.SetStreamMetadataAsync(_streamId, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - } - - [Test] - public async Task should_be_able_to_create_the_persistent_subscription() - { - var groupId = "group-" + Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings.Create().StartFrom(intMaxValue); - await _store.CreatePersistentSubscriptionAsync(_streamId, groupId, settings, DefaultData.AdminCredentials); - } - - [Test] - public async Task should_be_able_to_update_the_persistent_subscription() - { - var groupId = "group-" + Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings.Create(); - await _store.CreatePersistentSubscriptionAsync(_streamId, groupId, settings, DefaultData.AdminCredentials); - - settings = PersistentSubscriptionSettings.Create().StartFrom(intMaxValue); - await _store.UpdatePersistentSubscriptionAsync(_streamId, groupId, settings, DefaultData.AdminCredentials); - } - - [Test] - public async Task should_be_able_to_connect_to_persistent_subscription() - { - var groupId = "group-" + Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings.Create().StartFrom(intMaxValue); - await _store.CreatePersistentSubscriptionAsync(_streamId, groupId, settings, DefaultData.AdminCredentials); - - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - List receivedEvents = new List(); - var countdown = new CountdownEvent(3); - await _store.ConnectToPersistentSubscriptionAsync(_streamId, groupId, (s, e) => - { - receivedEvents.Add(e); - countdown.Signal(); - return Task.CompletedTask; - }, userCredentials: DefaultData.AdminCredentials); - - await _store.AppendToStreamAsync(_streamId, intMaxValue + 2, evnt); - - Assert.That(countdown.Wait(TimeSpan.FromSeconds(5)), "Timed out waiting for events to appear"); - - Assert.AreEqual(_r1.EventId, receivedEvents[0].Event.EventId); - Assert.AreEqual(_r2.EventId, receivedEvents[1].Event.EventId); - Assert.AreEqual(evnt.EventId, receivedEvents[2].Event.EventId); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_event_numbers_greater_than_2_billion.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_event_numbers_greater_than_2_billion.cs deleted file mode 100644 index 2d9f3e5525..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_event_numbers_greater_than_2_billion.cs +++ /dev/null @@ -1,124 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Data; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class ReadStreamWithEventNumbersGreaterThan2Billion - : MiniNodeWithExistingRecords -{ - private const string StreamName = "read_stream_with_event_numbers_greater_than_2_billion"; - private const long intMaxValue = (long)int.MaxValue; - - private EventRecord _r1, _r2, _r3, _r4, _r5; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - _r1 = await WriteSingleEvent(StreamName, intMaxValue + 1, new string('.', 3000), token: token); - _r2 = await WriteSingleEvent(StreamName, intMaxValue + 2, new string('.', 3000), token: token); - _r3 = await WriteSingleEvent(StreamName, intMaxValue + 3, new string('.', 3000), token: token); - _r4 = await WriteSingleEvent(StreamName, intMaxValue + 4, new string('.', 3000), token: token); - _r5 = await WriteSingleEvent(StreamName, intMaxValue + 5, new string('.', 3000), token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - await _store.SetStreamMetadataAsync(StreamName, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - } - - [Test] - public async Task read_forward_from_zero() - { - var result = await _store.ReadStreamEventsForwardAsync(StreamName, 0, 100, false); - Assert.AreEqual(0, result.Events.Length); - Assert.AreEqual(intMaxValue + 1, result.NextEventNumber); - } - - [Test] - public async Task should_be_able_to_read_stream_forward() - { - var result = await _store.ReadStreamEventsForwardAsync(StreamName, intMaxValue, 100, false); - Assert.AreEqual(5, result.Events.Count()); - Assert.AreEqual(_r1.EventId, result.Events[0].Event.EventId); - Assert.AreEqual(_r2.EventId, result.Events[1].Event.EventId); - Assert.AreEqual(_r3.EventId, result.Events[2].Event.EventId); - Assert.AreEqual(_r4.EventId, result.Events[3].Event.EventId); - Assert.AreEqual(_r5.EventId, result.Events[4].Event.EventId); - } - - [Test] - public async Task should_be_able_to_read_stream_backward() - { - var result = await _store.ReadStreamEventsBackwardAsync(StreamName, intMaxValue + 6, 100, false); - Assert.AreEqual(5, result.Events.Count()); - Assert.AreEqual(_r5.EventId, result.Events[0].Event.EventId); - Assert.AreEqual(_r4.EventId, result.Events[1].Event.EventId); - Assert.AreEqual(_r3.EventId, result.Events[2].Event.EventId); - Assert.AreEqual(_r2.EventId, result.Events[3].Event.EventId); - Assert.AreEqual(_r1.EventId, result.Events[4].Event.EventId); - } - - [Test] - public async Task should_be_able_to_read_each_event() - { - var record = await _store.ReadEventAsync(StreamName, intMaxValue + 1, false); - Assert.AreEqual(EventReadStatus.Success, record.Status); - Assert.AreEqual(_r1.EventId, record.Event.Value.Event.EventId); - - record = await _store.ReadEventAsync(StreamName, intMaxValue + 2, false); - Assert.AreEqual(EventReadStatus.Success, record.Status); - Assert.AreEqual(_r2.EventId, record.Event.Value.Event.EventId); - - record = await _store.ReadEventAsync(StreamName, intMaxValue + 3, false); - Assert.AreEqual(EventReadStatus.Success, record.Status); - Assert.AreEqual(_r3.EventId, record.Event.Value.Event.EventId); - - record = await _store.ReadEventAsync(StreamName, intMaxValue + 4, false); - Assert.AreEqual(EventReadStatus.Success, record.Status); - Assert.AreEqual(_r4.EventId, record.Event.Value.Event.EventId); - - record = await _store.ReadEventAsync(StreamName, intMaxValue + 5, false); - Assert.AreEqual(EventReadStatus.Success, record.Status); - Assert.AreEqual(_r5.EventId, record.Event.Value.Event.EventId); - } - - [Test] - public async Task should_be_able_to_read_all_forward() - { - var result = await _store.ReadAllEventsForwardAsync(Position.Start, 100, false, DefaultData.AdminCredentials) -; - Assert.IsTrue(result.Events.Count() > 5); - - var records = result.Events.Where(x => x.OriginalStreamId == StreamName).ToList(); - Assert.AreEqual(_r1.EventId, records[0].Event.EventId); - Assert.AreEqual(_r2.EventId, records[1].Event.EventId); - Assert.AreEqual(_r3.EventId, records[2].Event.EventId); - Assert.AreEqual(_r4.EventId, records[3].Event.EventId); - Assert.AreEqual(_r5.EventId, records[4].Event.EventId); - } - - [Test] - public async Task should_be_able_to_read_all_backward() - { - var result = await _store.ReadAllEventsBackwardAsync(Position.End, 100, false, DefaultData.AdminCredentials) -; - Assert.IsTrue(result.Events.Count() > 5); - - var records = result.Events.Where(x => x.OriginalStreamId == StreamName).ToList(); - Assert.AreEqual(_r5.EventId, records[0].Event.EventId); - Assert.AreEqual(_r4.EventId, records[1].Event.EventId); - Assert.AreEqual(_r3.EventId, records[2].Event.EventId); - Assert.AreEqual(_r2.EventId, records[3].Event.EventId); - Assert.AreEqual(_r1.EventId, records[4].Event.EventId); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs deleted file mode 100644 index ddc69ce017..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/read_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Data; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class - ReadStreamWithLinkToEventWithEventNumberGreaterThanIntMaxvalue - : MiniNodeWithExistingRecords -{ - private const string StreamName = "read_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue"; - private const long intMaxValue = (long)int.MaxValue; - - private string _linkedStreamName = "linked-" + StreamName; - private EventRecord _event1, _event2; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - _event1 = await WriteSingleEvent(StreamName, intMaxValue + 1, new string('.', 3000), token: token); - _event2 = await WriteSingleEvent(StreamName, intMaxValue + 2, new string('.', 3000), token: token); - await WriteSingleEvent(_linkedStreamName, 0, string.Format("{0}@{1}", intMaxValue + 1, StreamName), - eventType: SystemEventTypes.LinkTo, token: token); - await WriteSingleEvent(_linkedStreamName, 1, string.Format("{0}@{1}", intMaxValue + 2, StreamName), - eventType: SystemEventTypes.LinkTo, token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - } - - [Test] - public async Task should_be_able_to_read_link_stream_forward_and_resolve_link_tos() - { - var readResult = await _store - .ReadStreamEventsForwardAsync(_linkedStreamName, 0, 100, true, DefaultData.AdminCredentials); - Assert.AreEqual(SliceReadStatus.Success, readResult.Status); - Assert.AreEqual(2, readResult.Events.Length); - Assert.AreEqual(_event1.EventId, readResult.Events[0].Event.EventId); - Assert.AreEqual(_event2.EventId, readResult.Events[1].Event.EventId); - Assert.AreEqual(intMaxValue + 1, readResult.Events[0].Event.EventNumber); - Assert.AreEqual(intMaxValue + 2, readResult.Events[1].Event.EventNumber); - } - - [Test] - public async Task should_be_able_to_read_link_stream_backward_and_resolve_link_tos() - { - var readResult = await _store - .ReadStreamEventsBackwardAsync(_linkedStreamName, 10, 100, true, DefaultData.AdminCredentials); - Assert.AreEqual(SliceReadStatus.Success, readResult.Status); - Assert.AreEqual(2, readResult.Events.Length); - Assert.AreEqual(_event2.EventId, readResult.Events[0].Event.EventId); - Assert.AreEqual(_event1.EventId, readResult.Events[1].Event.EventId); - Assert.AreEqual(intMaxValue + 2, readResult.Events[0].Event.EventNumber); - Assert.AreEqual(intMaxValue + 1, readResult.Events[1].Event.EventNumber); - } - - [Test] - public async Task should_be_able_to_read_all_stream_forward_and_resolve_link_tos() - { - var readResult = await _store.ReadAllEventsForwardAsync(Position.Start, 100, true, DefaultData.AdminCredentials) - ; - var linkedEvents = readResult.Events.Where(x => x.OriginalStreamId == _linkedStreamName).ToList(); - Assert.AreEqual(2, linkedEvents.Count()); - Assert.AreEqual(_event1.EventId, linkedEvents[0].Event.EventId); - Assert.AreEqual(_event2.EventId, linkedEvents[1].Event.EventId); - Assert.AreEqual(intMaxValue + 1, linkedEvents[0].Event.EventNumber); - Assert.AreEqual(intMaxValue + 2, linkedEvents[1].Event.EventNumber); - } - - [Test] - public async Task should_be_able_to_read_all_stream_backward_and_resolve_link_tos() - { - var readResult = await _store.ReadAllEventsBackwardAsync(Position.End, 100, true, DefaultData.AdminCredentials) - ; - var linkedEvents = readResult.Events.Where(x => x.OriginalStreamId == _linkedStreamName).ToList(); - Assert.AreEqual(2, linkedEvents.Count()); - Assert.AreEqual(_event2.EventId, linkedEvents[0].Event.EventId); - Assert.AreEqual(_event1.EventId, linkedEvents[1].Event.EventId); - Assert.AreEqual(intMaxValue + 2, linkedEvents[0].Event.EventNumber); - Assert.AreEqual(intMaxValue + 1, linkedEvents[1].Event.EventNumber); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscribe_to_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscribe_to_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs deleted file mode 100644 index 9914e058dc..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscribe_to_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common.Utils; -using EventStore.Core.Services; -using NUnit.Framework; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class - SubscribeToStreamWithLinkToEventWithEventNumberGreaterThanIntMaxvalue : - MiniNodeWithExistingRecords -{ - private const string StreamName = - "subscribe_to_stream_with_link_to_event_with_event_number_greater_than_int_maxvalue"; - - private const long intMaxValue = (long)int.MaxValue; - - private string _linkedStreamName = "linked-" + StreamName; - private Guid _event1Id; - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _receivedEvent; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - var event1 = await WriteSingleEvent(StreamName, intMaxValue + 1, new string('.', 3000), token: token); - await WriteSingleEvent(StreamName, intMaxValue + 2, new string('.', 3000), token: token); - _event1Id = event1.EventId; - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - - await _store.SubscribeToStreamAsync(_linkedStreamName, true, HandleEvent); - await _store.AppendToStreamAsync(_linkedStreamName, ExpectedVersion.NoStream, - new EventData(Guid.NewGuid(), - SystemEventTypes.LinkTo, false, Helper.UTF8NoBom.GetBytes( - $"{intMaxValue + 1}@{StreamName}" - ), null)); - } - - private Task HandleEvent(EventStoreSubscription sub, ResolvedEvent resolvedEvent) - { - _receivedEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void should_receive_and_resolve_the_linked_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(intMaxValue + 1, _receivedEvent.Event.EventNumber); - Assert.AreEqual(_event1Id, _receivedEvent.Event.EventId); - Assert.AreEqual(intMaxValue + 1, _receivedEvent.Event.EventNumber); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscriptions_on_stream_with_event_numbers_greater_than_2_billion.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscriptions_on_stream_with_event_numbers_greater_than_2_billion.cs deleted file mode 100644 index be53c8081a..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/subscriptions_on_stream_with_event_numbers_greater_than_2_billion.cs +++ /dev/null @@ -1,115 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Data; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class SubscriptionsOnStreamWithEventNumbersGreaterThan2Billion - : MiniNodeWithExistingRecords -{ - private const long intMaxValue = (long)int.MaxValue; - - private string _volatileStreamOne = "subscriptions-volatile-1"; - private string _volatileStreamTwo = "subscriptions-volatile-2"; - private string _catchupStreamOne = "subscriptions-catchup-1"; - - private EventRecord _c1, _c2; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - await WriteSingleEvent(_volatileStreamOne, intMaxValue + 1, new string('.', 3000), token: token); - await WriteSingleEvent(_volatileStreamOne, intMaxValue + 2, new string('.', 3000), token: token); - - await WriteSingleEvent(_volatileStreamTwo, intMaxValue + 1, new string('.', 3000), token: token); - await WriteSingleEvent(_volatileStreamTwo, intMaxValue + 2, new string('.', 3000), token: token); - - _c1 = await WriteSingleEvent(_catchupStreamOne, intMaxValue + 1, new string('.', 3000), token: token); - _c2 = await WriteSingleEvent(_catchupStreamOne, intMaxValue + 2, new string('.', 3000), token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - await _store.SetStreamMetadataAsync(_volatileStreamOne, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - await _store.SetStreamMetadataAsync(_volatileStreamTwo, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - await _store.SetStreamMetadataAsync(_catchupStreamOne, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - } - - [Test] - public async Task should_be_able_to_subscribe_to_stream_with_volatile_subscription() - { - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - EventStore.ClientAPI.ResolvedEvent receivedEvent = new EventStore.ClientAPI.ResolvedEvent(); - var mre = new ManualResetEvent(false); - await _store.SubscribeToStreamAsync(_volatileStreamOne, true, (s, e) => - { - receivedEvent = e; - mre.Set(); - return Task.CompletedTask; - }); - - await _store.AppendToStreamAsync(_volatileStreamOne, intMaxValue + 2, evnt); - Assert.That(mre.WaitOne(TimeSpan.FromSeconds(5)), "Timed out waiting for events to appear"); - - Assert.AreEqual(evnt.EventId, receivedEvent.Event.EventId); - } - - [Test] - public async Task should_be_able_to_subscribe_to_all_with_volatile_subscription() - { - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - EventStore.ClientAPI.ResolvedEvent receivedEvent = new EventStore.ClientAPI.ResolvedEvent(); - var mre = new ManualResetEvent(false); - await _store.SubscribeToAllAsync(true, (s, e) => - { - if (SystemStreams.IsSystemStream(e.OriginalStreamId)) - { - return Task.CompletedTask; - } - - receivedEvent = e; - mre.Set(); - return Task.CompletedTask; - }, userCredentials: DefaultData.AdminCredentials); - - await _store.AppendToStreamAsync(_volatileStreamTwo, intMaxValue + 2, evnt); - Assert.That(mre.WaitOne(TimeSpan.FromSeconds(5)), "Timed out waiting for events to appear"); - - Assert.AreEqual(evnt.EventId, receivedEvent.Event.EventId); - } - - [Test] - public async Task should_be_able_to_subscribe_to_stream_with_catchup_subscription() - { - var evnt = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - List receivedEvents = new List(); - - var countdown = new CountdownEvent(3); - _store.SubscribeToStreamFrom(_catchupStreamOne, 0, CatchUpSubscriptionSettings.Default, (s, e) => - { - receivedEvents.Add(e); - countdown.Signal(); - return Task.CompletedTask; - }); - - await _store.AppendToStreamAsync(_catchupStreamOne, intMaxValue + 2, evnt); - - Assert.That(countdown.Wait(TimeSpan.FromSeconds(5)), "Timed out waiting for events to appear"); - - Assert.AreEqual(3, receivedEvents.Count); - Assert.AreEqual(_c1.EventId, receivedEvents[0].Event.EventId); - Assert.AreEqual(_c2.EventId, receivedEvents[1].Event.EventId); - Assert.AreEqual(evnt.EventId, receivedEvents[2].Event.EventId); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/transactions_on_stream_with_event_numbers_greater_than_2_billion.cs b/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/transactions_on_stream_with_event_numbers_greater_than_2_billion.cs deleted file mode 100644 index f7c5c029ff..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/ExpectedVersion64Bit/transactions_on_stream_with_event_numbers_greater_than_2_billion.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Data; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.ExpectedVersion64Bit; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("ClientAPI"), Category("LongRunning")] -public class TransactionsOnStreamWithEventNumbersGreaterThan2Billion - : MiniNodeWithExistingRecords -{ - private const string StreamName = "transactions_on_stream_with_event_numbers_greater_than_2_billion"; - private const long intMaxValue = (long)int.MaxValue; - - private EventRecord _r1, _r2, _r3, _r4, _r5; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - _r1 = await WriteSingleEvent(StreamName, intMaxValue + 1, new string('.', 3000), token: token); - _r2 = await WriteSingleEvent(StreamName, intMaxValue + 2, new string('.', 3000), token: token); - _r3 = await WriteSingleEvent(StreamName, intMaxValue + 3, new string('.', 3000), token: token); - _r4 = await WriteSingleEvent(StreamName, intMaxValue + 4, new string('.', 3000), token: token); - _r5 = await WriteSingleEvent(StreamName, intMaxValue + 5, new string('.', 3000), token: token); - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - await _store.SetStreamMetadataAsync(StreamName, EventStore.ClientAPI.ExpectedVersion.Any, - EventStore.ClientAPI.StreamMetadata.Create(truncateBefore: intMaxValue + 1)); - } - - [Test] - public async Task should_be_able_to_append_to_stream_in_a_transaction() - { - var evnt1 = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - var evnt2 = new EventData(Guid.NewGuid(), "EventType", false, new byte[10], new byte[15]); - - var transaction = await _store.StartTransactionAsync(StreamName, intMaxValue + 5, DefaultData.AdminCredentials) -; - await transaction.WriteAsync(evnt1); - await transaction.WriteAsync(evnt2); - await transaction.CommitAsync(); - - var records = await _store.ReadStreamEventsForwardAsync(StreamName, intMaxValue, 10, false); - Assert.AreEqual(7, records.Events.Length); - Assert.AreEqual(_r1.EventId, records.Events[0].Event.EventId); - Assert.AreEqual(_r2.EventId, records.Events[1].Event.EventId); - Assert.AreEqual(_r3.EventId, records.Events[2].Event.EventId); - Assert.AreEqual(_r4.EventId, records.Events[3].Event.EventId); - Assert.AreEqual(_r5.EventId, records.Events[4].Event.EventId); - Assert.AreEqual(evnt1.EventId, records.Events[5].Event.EventId); - Assert.AreEqual(evnt2.EventId, records.Events[6].Event.EventId); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs deleted file mode 100644 index 15a424be6c..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventDataComparer.cs +++ /dev/null @@ -1,46 +0,0 @@ -using EventStore.ClientAPI; -using EventStore.Common.Utils; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -internal static class EventDataComparer -{ - public static bool Equal(EventData expected, RecordedEvent actual) - { - if (expected.EventId != actual.EventId) - { - return false; - } - - if (expected.Type != actual.EventType) - { - return false; - } - - var expectedDataString = Helper.UTF8NoBom.GetString(expected.Data ?? new byte[0]); - var expectedMetadataString = Helper.UTF8NoBom.GetString(expected.Metadata ?? new byte[0]); - - var actualDataString = Helper.UTF8NoBom.GetString(actual.Data ?? new byte[0]); - var actualMetadataDataString = Helper.UTF8NoBom.GetString(actual.Metadata ?? new byte[0]); - - return expectedDataString == actualDataString && expectedMetadataString == actualMetadataDataString; - } - - public static bool Equal(EventData[] expected, RecordedEvent[] actual) - { - if (expected.Length != actual.Length) - { - return false; - } - - for (var i = 0; i < expected.Length; i++) - { - if (!Equal(expected[i], actual[i])) - { - return false; - } - } - - return true; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs deleted file mode 100644 index da4bb62ce2..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/EventsStream.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -internal class EventsStream -{ - private const int SliceSize = 10; - - public static async Task Count(IEventStoreConnection store, string stream) - { - var result = 0; - while (true) - { - var slice = await store.ReadStreamEventsForwardAsync(stream, result, SliceSize, false); - result += slice.Events.Length; - if (slice.IsEndOfStream) - { - break; - } - } - - return result; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs deleted file mode 100644 index b860eb63ac..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TcpType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public enum TcpType -{ - Normal, - Ssl -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs deleted file mode 100644 index 674773889c..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnection.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Net; -using System.Threading; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Internal; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public static class TestConnection -{ - private static int _nextConnId = -1; - - public static IEventStoreConnection Create(IPEndPoint endPoint, TcpType tcpType = TcpType.Ssl, - UserCredentials userCredentials = null) - { - return EventStoreConnection.Create(Settings(tcpType, userCredentials), - endPoint.ToESTcpUri(), - $"ESC-{Interlocked.Increment(ref _nextConnId)}"); - } - - public static IEventStoreConnection CreateMiniNodeClient(IPEndPoint endPoint, TcpType tcpType = TcpType.Ssl, - UserCredentials userCredentials = null) - { - return EventStoreConnection.Create(Settings( - tcpType, - userCredentials, - limitAttemptsForOperationTo: 10, - reconnectionDelay: TimeSpan.FromMilliseconds(100)), - endPoint.ToESTcpUri(), - $"ESC-{Interlocked.Increment(ref _nextConnId)}"); - } - - public static IEventStoreConnection To(MiniNode miniNode, TcpType tcpType, - UserCredentials userCredentials = null) - { - return EventStoreConnection.Create(Settings(tcpType, userCredentials), - miniNode.TcpEndPoint.ToESTcpUri(), - $"ESC-{Interlocked.Increment(ref _nextConnId)}"); - } - - private static ConnectionSettingsBuilder Settings( - TcpType tcpType, - UserCredentials userCredentials, - int limitAttemptsForOperationTo = 1, - TimeSpan? reconnectionDelay = null) - { - var settings = ConnectionSettings.Create() - .SetDefaultUserCredentials(userCredentials) - .UseCustomLogger(ClientApiLoggerBridge.Default) - .EnableVerboseLogging() - .LimitReconnectionsTo(10) - .LimitAttemptsForOperationTo(limitAttemptsForOperationTo) - .SetTimeoutCheckPeriodTo(TimeSpan.FromMilliseconds(100)) - .SetReconnectionDelayTo(reconnectionDelay ?? TimeSpan.Zero) - .FailOnNoServerResponse() - //.SetOperationTimeoutTo(TimeSpan.FromDays(1)) - ; - if (tcpType == TcpType.Ssl) - { - settings.DisableServerCertificateValidation(); - } - else - { - settings.DisableTls(); - } - - return settings; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs deleted file mode 100644 index 9ea1365f78..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestConnectionLifecycle.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public static class TestConnectionLifecycle -{ - public static async Task ReconnectUntilReady( - Func createConnection, - Func readinessProbe, - TimeSpan timeout) - { - var deadline = DateTime.UtcNow + timeout; - - while (true) - { - IEventStoreConnection connection = null; - - try - { - connection = createConnection(); - await connection.ConnectAsync(); - await readinessProbe(connection); - return connection; - } - catch (Exception ex) - { - if (connection != null) - { - TryCloseConnection(connection); - } - - if (IsTransientConnectionFailure(ex) && DateTime.UtcNow < deadline) - { - await Task.Delay(250); - continue; - } - - throw; - } - } - } - - public static async Task CloseConnectionAndWait(IEventStoreConnection connection, TimeSpan timeout) - { - var closed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - connection.Closed += (_, _) => closed.TrySetResult(); - connection.Close(); - await closed.Task.WithTimeout(timeout); - } - - public static bool IsTransientConnectionFailure(Exception ex) => - ex.GetType().Name is "ConnectionClosedException" - or "RetriesLimitReachedException" - or "NotAuthenticatedException" - or "AccessDeniedException"; - - public static void TryCloseConnection(IEventStoreConnection connection) - { - try - { - connection.Close(); - } - catch - { - } - } - - public static void DisposeIfNeeded(object candidate) - { - if (candidate is IDisposable disposable) - { - disposable.Dispose(); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs deleted file mode 100644 index 8fb92bedf3..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/TestEvent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Text; -using EventStore.ClientAPI; -using EventStore.Common.Utils; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -public class TestEvent -{ - public static EventData NewTestEvent(string data = null, string metadata = null, string eventName = "TestEvent") - { - return NewTestEvent(Guid.NewGuid(), data, metadata, eventName); - } - - public static EventData NewTestEvent(Guid eventId, string data = null, string metadata = null, string eventName = "TestEvent") - { - var encodedData = Helper.UTF8NoBom.GetBytes(data ?? eventId.ToString()); - var encodedMetadata = Helper.UTF8NoBom.GetBytes(metadata ?? "metadata"); - - return new EventData(eventId, eventName, false, encodedData, encodedMetadata); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs b/src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs deleted file mode 100644 index 236e47ccb6..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Helpers/Writer.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Helpers; - -internal class StreamWriter -{ - private readonly IEventStoreConnection _store; - private readonly string _stream; - private readonly long _version; - - public StreamWriter(IEventStoreConnection store, string stream, long version) - { - _store = store; - _stream = stream; - _version = version; - } - - public async Task Append(params EventData[] events) - { - for (var i = 0; i < events.Length; i++) - { - var expVer = _version == ExpectedVersion.Any ? ExpectedVersion.Any : _version + i; - var nextExpVer = (await _store.AppendToStreamAsync(_stream, expVer, new[] { events[i] })).NextExpectedVersion; - if (_version != ExpectedVersion.Any) - { - Assert.AreEqual(expVer + 1, nextExpVer); - } - } - - return new TailWriter(_store, _stream); - } -} - -internal class TailWriter -{ - private readonly IEventStoreConnection _store; - private readonly string _stream; - - public TailWriter(IEventStoreConnection store, string stream) - { - _store = store; - _stream = stream; - } - - public async Task Then(EventData @event, long expectedVersion) - { - await _store.AppendToStreamAsync(_stream, expectedVersion, new[] { @event }); - return this; - } -} - -internal class TransactionalWriter -{ - private readonly IEventStoreConnection _store; - private readonly string _stream; - - public TransactionalWriter(IEventStoreConnection store, string stream) - { - _store = store; - _stream = stream; - } - - public async Task StartTransaction(long expectedVersion) - { - return new OngoingTransaction(await _store.StartTransactionAsync(_stream, expectedVersion)); - } - - public OngoingTransaction ContinueTransaction(long transactionId) - { - return new OngoingTransaction(_store.ContinueTransaction(transactionId)); - } -} - -//TODO GFY this should be removed and merged with the public idea of a transaction. -internal class OngoingTransaction -{ - private readonly EventStoreTransaction _transaction; - - public long TransactionId => _transaction.TransactionId; - - public OngoingTransaction(EventStoreTransaction transaction) - { - _transaction = transaction; - } - - public async Task Write(params EventData[] events) - { - await _transaction.WriteAsync(events); - return this; - } - - public Task Commit() - { - return _transaction.CommitAsync(); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/AuthenticationTestBase.cs b/src/EventStore.Core.Tests/ClientAPI/Security/AuthenticationTestBase.cs deleted file mode 100644 index 63d7ca9947..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/AuthenticationTestBase.cs +++ /dev/null @@ -1,268 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Services; -using EventStore.Core.Services.UserManagement; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -public abstract class AuthenticationTestBase : SpecificationWithDirectoryPerTestFixture -{ - private readonly UserCredentials _userCredentials; - private MiniNode _node; - protected IEventStoreConnection Connection; - - protected AuthenticationTestBase(UserCredentials userCredentials = null) - { - _userCredentials = userCredentials; - } - - - public virtual IEventStoreConnection SetupConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint, TcpType.Ssl, _userCredentials); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName, enableTrustedAuth: true); - await _node.Start(); - - var userCreateEvent1 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - _node.Node.MainQueue.Publish( - new UserManagementMessage.Create( - new CallbackEnvelope( - m => - { - Assert.IsTrue(m is UserManagementMessage.UpdateResult); - var msg = (UserManagementMessage.UpdateResult)m; - Assert.IsTrue(msg.Success); - - userCreateEvent1.SetResult(true); - }), - SystemAccounts.System, - "user1", - "Test User 1", - new string[0], - "pa$$1")); - - var userCreateEvent2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - _node.Node.MainQueue.Publish( - new UserManagementMessage.Create( - new CallbackEnvelope( - m => - { - Assert.IsTrue(m is UserManagementMessage.UpdateResult); - var msg = (UserManagementMessage.UpdateResult)m; - Assert.IsTrue(msg.Success); - - userCreateEvent2.SetResult(true); - }), - SystemAccounts.System, - "user2", - "Test User 2", - new string[0], - "pa$$2")); - - var adminCreateEvent2 = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - _node.Node.MainQueue.Publish( - new UserManagementMessage.Create( - new CallbackEnvelope( - m => - { - Assert.IsTrue(m is UserManagementMessage.UpdateResult); - var msg = (UserManagementMessage.UpdateResult)m; - Assert.IsTrue(msg.Success); - - adminCreateEvent2.SetResult(true); - }), - SystemAccounts.System, - "adm", - "Administrator User", - new[] { SystemRoles.Admins }, - "admpa$$")); - - Assert.IsTrue(await userCreateEvent1.Task.WithTimeout(10000), "User 1 creation failed"); - Assert.IsTrue(await userCreateEvent2.Task.WithTimeout(10000), "User 2 creation failed"); - Assert.IsTrue(await adminCreateEvent2.Task.WithTimeout(10000), "Administrator User creation failed"); - - Connection = SetupConnection(_node); - await Connection.ConnectAsync(); - - await Connection.SetStreamMetadataAsync("noacl-stream", ExpectedVersion.NoStream, StreamMetadata.Build()); - await Connection.SetStreamMetadataAsync( - "read-stream", - ExpectedVersion.NoStream, - StreamMetadata.Build().SetReadRole("user1")); - await Connection.SetStreamMetadataAsync( - "write-stream", - ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRole("user1")); - await Connection.SetStreamMetadataAsync( - "metaread-stream", - ExpectedVersion.NoStream, - StreamMetadata.Build().SetMetadataReadRole("user1")); - await Connection.SetStreamMetadataAsync( - "metawrite-stream", - ExpectedVersion.NoStream, - StreamMetadata.Build().SetMetadataWriteRole("user1")); - - await Connection.SetStreamMetadataAsync( - "$all", - ExpectedVersion.Any, - StreamMetadata.Build().SetReadRole("user1"), - new UserCredentials("adm", "admpa$$")); - - await Connection.SetStreamMetadataAsync( - "$system-acl", - ExpectedVersion.NoStream, - StreamMetadata.Build() - .SetReadRole("user1") - .SetWriteRole("user1") - .SetMetadataReadRole("user1") - .SetMetadataWriteRole("user1"), - new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync( - "$system-adm", - ExpectedVersion.NoStream, - StreamMetadata.Build() - .SetReadRole(SystemRoles.Admins) - .SetWriteRole(SystemRoles.Admins) - .SetMetadataReadRole(SystemRoles.Admins) - .SetMetadataWriteRole(SystemRoles.Admins), - new UserCredentials("adm", "admpa$$")); - - await Connection.SetStreamMetadataAsync( - "normal-all", - ExpectedVersion.NoStream, - StreamMetadata.Build() - .SetReadRole(SystemRoles.All) - .SetWriteRole(SystemRoles.All) - .SetMetadataReadRole(SystemRoles.All) - .SetMetadataWriteRole(SystemRoles.All)); - await Connection.SetStreamMetadataAsync( - "$system-all", - ExpectedVersion.NoStream, - StreamMetadata.Build() - .SetReadRole(SystemRoles.All) - .SetWriteRole(SystemRoles.All) - .SetMetadataReadRole(SystemRoles.All) - .SetMetadataWriteRole(SystemRoles.All), - new UserCredentials("adm", "admpa$$")); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - Connection.Close(); - await base.TestFixtureTearDown(); - } - - protected Task ReadEvent(string streamId, string login, string password) - { - return Connection.ReadEventAsync(streamId, -1, false, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task ReadStreamForward(string streamId, string login, string password) - { - return Connection.ReadStreamEventsForwardAsync(streamId, 0, 1, false, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task ReadStreamBackward(string streamId, string login, string password) - { - return Connection.ReadStreamEventsBackwardAsync(streamId, 0, 1, false, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task WriteStream(string streamId, string login, string password) - { - return Connection.AppendToStreamAsync(streamId, ExpectedVersion.Any, CreateEvents(), - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task TransStart(string streamId, string login, string password) - { - return Connection.StartTransactionAsync(streamId, ExpectedVersion.Any, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task ReadAllForward(string login, string password) - { - return Connection.ReadAllEventsForwardAsync(Position.Start, 1, false, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task ReadAllBackward(string login, string password) - { - return Connection.ReadAllEventsBackwardAsync(Position.End, 1, false, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task ReadMeta(string streamId, string login, string password) - { - return Connection.GetStreamMetadataAsRawBytesAsync(streamId, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task WriteMeta(string streamId, string login, string password, string metawriteRole) - { - return Connection.SetStreamMetadataAsync(streamId, ExpectedVersion.Any, - metawriteRole == null - ? StreamMetadata.Build() - : StreamMetadata.Build().SetReadRole(metawriteRole) - .SetWriteRole(metawriteRole) - .SetMetadataReadRole(metawriteRole) - .SetMetadataWriteRole(metawriteRole), - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected async Task SubscribeToStream(string streamId, string login, string password) - { - using (await Connection.SubscribeToStreamAsync(streamId, false, (x, y) => Task.CompletedTask, - (x, y, z) => { }, - login == null && password == null ? null : new UserCredentials(login, password))) - { - } - } - - protected async Task SubscribeToAll(string login, string password) - { - using (await Connection.SubscribeToAllAsync(false, (x, y) => Task.CompletedTask, (x, y, z) => { }, - login == null && password == null ? null : new UserCredentials(login, password))) - { - } - } - - protected async Task CreateStreamWithMeta(StreamMetadata metadata, string streamPrefix = null) - { - var stream = (streamPrefix ?? string.Empty) + Guid.NewGuid().ToString(); - await Connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - metadata, new UserCredentials("adm", "admpa$$")); - return stream; - } - - protected Task DeleteStream(string streamId, string login, string password) - { - return Connection.DeleteStreamAsync(streamId, ExpectedVersion.Any, true, - login == null && password == null ? null : new UserCredentials(login, password)); - } - - protected Task ExpectNoException(Func action) => action(); - - protected EventData[] CreateEvents() - { - return new[] { new EventData(Guid.NewGuid(), "some-type", false, new byte[] { 1, 2, 3 }, null) }; - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/all_stream_with_no_acl_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/all_stream_with_no_acl_security.cs deleted file mode 100644 index 82d23ba1ce..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/all_stream_with_no_acl_security.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class all_stream_with_no_acl_security : AuthenticationTestBase -{ - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - await Connection.SetStreamMetadataAsync("$all", ExpectedVersion.Any, StreamMetadata.Build(), - new UserCredentials("adm", "admpa$$")); - } - - [Test] - public async Task write_to_all_is_never_allowed() - { - await AssertEx.ThrowsAsync(() => WriteStream("$all", null, null)); - await AssertEx.ThrowsAsync(() => WriteStream("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => WriteStream("$all", "adm", "admpa$$")); - } - - [Test] - public async Task delete_of_all_is_never_allowed() - { - await AssertEx.ThrowsAsync(() => DeleteStream("$all", null, null)); - await AssertEx.ThrowsAsync(() => DeleteStream("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => DeleteStream("$all", "adm", "admpa$$")); - } - - - [Test] - public async Task reading_and_subscribing_is_not_allowed_when_no_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => ReadEvent("$all", null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamForward("$all", null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("$all", null, null)); - await AssertEx.ThrowsAsync(() => ReadMeta("$all", null, null)); - await AssertEx.ThrowsAsync(() => SubscribeToStream("$all", null, null)); - } - - [Test] - public async Task reading_and_subscribing_is_not_allowed_for_usual_user() - { - await AssertEx.ThrowsAsync(() => ReadEvent("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadMeta("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => SubscribeToStream("$all", "user1", "pa$$1")); - } - - [Test] - public async Task reading_and_subscribing_is_allowed_for_admin_user() - { - await ReadEvent("$all", "adm", "admpa$$"); - await ReadStreamForward("$all", "adm", "admpa$$"); - await ReadStreamBackward("$all", "adm", "admpa$$"); - await ReadMeta("$all", "adm", "admpa$$"); - await SubscribeToStream("$all", "adm", "admpa$$"); - } - - - [Test] - public async Task meta_write_is_not_allowed_when_no_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => WriteMeta("$all", null, null, null)); - } - - [Test] - public async Task meta_write_is_not_allowed_for_usual_user() - { - await AssertEx.ThrowsAsync(() => WriteMeta("$all", "user1", "pa$$1", null)); - } - - [Test] - public async Task meta_write_is_allowed_for_admin_user() - { - await WriteMeta("$all", "adm", "admpa$$", null); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/authorized_default_credentials_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/authorized_default_credentials_security.cs deleted file mode 100644 index b2ee2aec05..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/authorized_default_credentials_security.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class authorized_default_credentials_security : AuthenticationTestBase -{ - public authorized_default_credentials_security() : base(new UserCredentials("user1", "pa$$1")) - { - } - - [Test] - public async Task all_operations_succeeds_when_passing_no_explicit_credentials() - { - await ReadAllForward(null, null); - await ReadAllBackward(null, null); - - await ReadEvent("read-stream", null, null); - await ReadStreamForward("read-stream", null, null); - await ReadStreamBackward("read-stream", null, null); - - await WriteStream("write-stream", null, null); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await ExpectNoException(async () => - { - var trans = await TransStart("write-stream", null, null); - await trans.WriteAsync(); - await trans.CommitAsync(); - }); - } - - await ReadMeta("metaread-stream", null, null); - await WriteMeta("metawrite-stream", null, null, "user1"); - - await SubscribeToStream("read-stream", null, null); - await SubscribeToAll(null, null); - } - - [Test] - public async Task all_operations_are_not_authenticated_when_overriden_with_not_existing_credentials() - { - await AssertEx.ThrowsAsync(() => ReadAllForward("badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadAllBackward("badlogin", "badpass")); - - await AssertEx.ThrowsAsync(() => ReadEvent("read-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("read-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("read-stream", "badlogin", "badpass")); - - await AssertEx.ThrowsAsync(() => WriteStream("write-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => TransStart("write-stream", "badlogin", "badpass")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("write-stream", null, null)).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta("metaread-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => WriteMeta("metawrite-stream", "badlogin", "badpass", "user1")); - - await AssertEx.ThrowsAsync(() => SubscribeToStream("read-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => SubscribeToAll("badlogin", "badpass")); - } - - [Test] - public async Task all_operations_are_not_authorized_when_overriden_with_not_authorized_credentials() - { - await AssertEx.ThrowsAsync(() => ReadAllForward("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadAllBackward("user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => ReadEvent("read-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("read-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("read-stream", "user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => WriteStream("write-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => TransStart("write-stream", "user2", "pa$$2")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("write-stream", null, null)).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta("metaread-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => WriteMeta("metawrite-stream", "user2", "pa$$2", "user1")); - - await AssertEx.ThrowsAsync(() => SubscribeToStream("read-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => SubscribeToAll("user2", "pa$$2")); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/delete_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/delete_stream_security.cs deleted file mode 100644 index 6283707220..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/delete_stream_security.cs +++ /dev/null @@ -1,220 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class delete_stream_security : AuthenticationTestBase -{ - [Test] - public async Task delete_of_all_is_never_allowed() - { - await AssertEx.ThrowsAsync(() => DeleteStream("$all", null, null)); - await AssertEx.ThrowsAsync(() => DeleteStream("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => DeleteStream("$all", "adm", "admpa$$")); - } - - [Test] - public async Task deleting_normal_no_acl_stream_with_no_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build()); - await DeleteStream(streamId, null, null); - } - - [Test] - public async Task deleting_normal_no_acl_stream_with_existing_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build()); - await DeleteStream(streamId, "user1", "pa$$1"); - } - - [Test] - public async Task deleting_normal_no_acl_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build()); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - - [Test] - public async Task deleting_normal_user_stream_with_no_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole("user1")); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, null, null)); - } - - [Test] - public async Task deleting_normal_user_stream_with_not_authorized_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole("user1")); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, "user2", "pa$$2")); - } - - [Test] - public async Task deleting_normal_user_stream_with_authorized_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole("user1")); - await DeleteStream(streamId, "user1", "pa$$1"); - } - - [Test] - public async Task deleting_normal_user_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole("user1")); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - - [Test] - public async Task deleting_normal_admin_stream_with_no_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole(SystemRoles.Admins)); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, null, null)); - } - - [Test] - public async Task deleting_normal_admin_stream_with_existing_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole(SystemRoles.Admins)); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, "user1", "pa$$1")); - } - - [Test] - public async Task deleting_normal_admin_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole(SystemRoles.Admins)); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - - [Test] - public async Task deleting_normal_all_stream_with_no_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole(SystemRoles.All)); - await DeleteStream(streamId, null, null); - } - - [Test] - public async Task deleting_normal_all_stream_with_existing_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole(SystemRoles.All)); - await DeleteStream(streamId, "user1", "pa$$1"); - } - - [Test] - public async Task deleting_normal_all_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(StreamMetadata.Build().SetDeleteRole(SystemRoles.All)); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - // $-stream - - [Test] - public async Task deleting_system_no_acl_stream_with_no_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", metadata: StreamMetadata.Build()); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, null, null)); - } - - [Test] - public async Task deleting_system_no_acl_stream_with_existing_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", metadata: StreamMetadata.Build()); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, "user1", "pa$$1")); - } - - [Test] - public async Task deleting_system_no_acl_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", metadata: StreamMetadata.Build()); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - - [Test] - public async Task deleting_system_user_stream_with_no_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole("user1")); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, null, null)); - } - - [Test] - public async Task deleting_system_user_stream_with_not_authorized_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole("user1")); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, "user2", "pa$$2")); - } - - [Test] - public async Task deleting_system_user_stream_with_authorized_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole("user1")); - await DeleteStream(streamId, "user1", "pa$$1"); - } - - [Test] - public async Task deleting_system_user_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole("user1")); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - - [Test] - public async Task deleting_system_admin_stream_with_no_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole(SystemRoles.Admins)); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, null, null)); - } - - [Test] - public async Task deleting_system_admin_stream_with_existing_user_is_not_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole(SystemRoles.Admins)); - await AssertEx.ThrowsAsync(() => DeleteStream(streamId, "user1", "pa$$1")); - } - - [Test] - public async Task deleting_system_admin_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole(SystemRoles.Admins)); - await DeleteStream(streamId, "adm", "admpa$$"); - } - - - [Test] - public async Task deleting_system_all_stream_with_no_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole(SystemRoles.All)); - await DeleteStream(streamId, null, null); - } - - [Test] - public async Task deleting_system_all_stream_with_existing_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole(SystemRoles.All)); - await DeleteStream(streamId, "user1", "pa$$1"); - } - - [Test] - public async Task deleting_system_all_stream_with_admin_user_is_allowed() - { - var streamId = await CreateStreamWithMeta(streamPrefix: "$", - metadata: StreamMetadata.Build().SetDeleteRole(SystemRoles.All)); - await DeleteStream(streamId, "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/multiple_role_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/multiple_role_security.cs deleted file mode 100644 index 7cb6b9fe42..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/multiple_role_security.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class multiple_role_security : AuthenticationTestBase -{ - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - var settings = new SystemSettings( - new StreamAcl(new[] { "user1", "user2" }, new[] { "$admins", "user1" }, new[] { "user1", SystemRoles.All }, - null, null), - null); - await Connection.SetSystemSettingsAsync(settings, new UserCredentials("adm", "admpa$$")); - } - - [Test] - public async Task multiple_roles_are_handled_correctly() - { - await AssertEx.ThrowsAsync(() => ReadEvent("usr-stream", null, null)); - await ReadEvent("usr-stream", "user1", "pa$$1"); - await ReadEvent("usr-stream", "user2", "pa$$2"); - await ReadEvent("usr-stream", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("usr-stream", null, null)); - await WriteStream("usr-stream", "user1", "pa$$1"); - await AssertEx.ThrowsAsync(() => WriteStream("usr-stream", "user2", "pa$$2")); - await WriteStream("usr-stream", "adm", "admpa$$"); - - await DeleteStream("usr-stream1", null, null); - await DeleteStream("usr-stream2", "user1", "pa$$1"); - await DeleteStream("usr-stream3", "user2", "pa$$2"); - await DeleteStream("usr-stream4", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security.cs deleted file mode 100644 index 8de06e3df1..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class overriden_system_stream_security : AuthenticationTestBase -{ - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - var settings = new SystemSettings(userStreamAcl: null, - systemStreamAcl: new StreamAcl("user1", "user1", "user1", "user1", "user1")); - await Connection.SetSystemSettingsAsync(settings, new UserCredentials("adm", "admpa$$")); - } - - [Test] - public async Task operations_on_system_stream_succeed_for_authorized_user() - { - const string stream = "$sys-authorized-user"; - await ReadEvent(stream, "user1", "pa$$1"); - await ReadStreamForward(stream, "user1", "pa$$1"); - await ReadStreamBackward(stream, "user1", "pa$$1"); - - await WriteStream(stream, "user1", "pa$$1"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, "user1", "pa$$1"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, "user1", "pa$$1"); - await WriteMeta(stream, "user1", "pa$$1", null); - - await SubscribeToStream(stream, "user1", "pa$$1"); - - await DeleteStream(stream, "user1", "pa$$1"); - } - - [Test] - public async Task operations_on_system_stream_fail_for_not_authorized_user() - { - const string stream = "$sys-not-authorized-user"; - await AssertEx.ThrowsAsync(() => ReadEvent(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamForward(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward(stream, "user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => WriteStream(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => TransStart(stream, "user2", "pa$$2")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => WriteMeta(stream, "user2", "pa$$2", null)); - - await AssertEx.ThrowsAsync(() => SubscribeToStream(stream, "user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => DeleteStream(stream, "user2", "pa$$2")); - } - - [Test] - public async Task operations_on_system_stream_fail_for_anonymous_user() - { - const string stream = "$sys-anonymous-user"; - await AssertEx.ThrowsAsync(() => ReadEvent(stream, null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamForward(stream, null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamBackward(stream, null, null)); - - await AssertEx.ThrowsAsync(() => WriteStream(stream, null, null)); - await AssertEx.ThrowsAsync(() => TransStart(stream, null, null)); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta(stream, null, null)); - await AssertEx.ThrowsAsync(() => WriteMeta(stream, null, null, null)); - - await AssertEx.ThrowsAsync(() => SubscribeToStream(stream, null, null)); - - await AssertEx.ThrowsAsync(() => DeleteStream(stream, null, null)); - } - - [Test] - public async Task operations_on_system_stream_succeed_for_admin() - { - const string stream = "$sys-admin"; - await ReadEvent(stream, "adm", "admpa$$"); - await ReadStreamForward(stream, "adm", "admpa$$"); - await ReadStreamBackward(stream, "adm", "admpa$$"); - - await WriteStream(stream, "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, "adm", "admpa$$"); - await WriteMeta(stream, "adm", "admpa$$", null); - - await SubscribeToStream(stream, "adm", "admpa$$"); - - await DeleteStream(stream, "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security_for_all.cs b/src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security_for_all.cs deleted file mode 100644 index d3e17bf9b3..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/overriden_system_stream_security_for_all.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class overriden_system_stream_security_for_all : AuthenticationTestBase -{ - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - var settings = new SystemSettings( - userStreamAcl: null, - systemStreamAcl: new StreamAcl(SystemRoles.All, SystemRoles.All, SystemRoles.All, SystemRoles.All, - SystemRoles.All)); - await Connection.SetSystemSettingsAsync(settings, new UserCredentials("adm", "admpa$$")); - } - - [Test] - public async Task operations_on_system_stream_succeeds_for_user() - { - const string stream = "$sys-authorized-user"; - await ReadEvent(stream, "user1", "pa$$1"); - await ReadStreamForward(stream, "user1", "pa$$1"); - await ReadStreamBackward(stream, "user1", "pa$$1"); - - await WriteStream(stream, "user1", "pa$$1"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, "user1", "pa$$1"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, "user1", "pa$$1"); - await WriteMeta(stream, "user1", "pa$$1", null); - - await SubscribeToStream(stream, "user1", "pa$$1"); - - await DeleteStream(stream, "user1", "pa$$1"); - } - - [Test] - public async Task operations_on_system_stream_fail_for_anonymous_user() - { - const string stream = "$sys-anonymous-user"; - await ReadEvent(stream, null, null); - await ReadStreamForward(stream, null, null); - await ReadStreamBackward(stream, null, null); - - await WriteStream(stream, null, null); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, null, null); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, null, null); - await WriteMeta(stream, null, null, null); - - await SubscribeToStream(stream, null, null); - - await DeleteStream(stream, null, null); - } - - [Test] - public async Task operations_on_system_stream_succeed_for_admin() - { - const string stream = "$sys-admin"; - await ReadEvent(stream, "adm", "admpa$$"); - await ReadStreamForward(stream, "adm", "admpa$$"); - await ReadStreamBackward(stream, "adm", "admpa$$"); - - await WriteStream(stream, "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, "adm", "admpa$$"); - await WriteMeta(stream, "adm", "admpa$$", null); - - await SubscribeToStream(stream, "adm", "admpa$$"); - - await DeleteStream(stream, "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/overriden_user_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/overriden_user_stream_security.cs deleted file mode 100644 index 26c9867709..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/overriden_user_stream_security.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class overriden_user_stream_security : AuthenticationTestBase -{ - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - var settings = new SystemSettings(userStreamAcl: new StreamAcl("user1", "user1", "user1", "user1", "user1"), - systemStreamAcl: null); - await Connection.SetSystemSettingsAsync(settings, new UserCredentials("adm", "admpa$$")); - } - - [Test] - public async Task operations_on_user_stream_succeeds_for_authorized_user() - { - const string stream = "user-authorized-user"; - await ReadEvent(stream, "user1", "pa$$1"); - await ReadStreamForward(stream, "user1", "pa$$1"); - await ReadStreamBackward(stream, "user1", "pa$$1"); - - await WriteStream(stream, "user1", "pa$$1"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, "user1", "pa$$1"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, "user1", "pa$$1"); - await WriteMeta(stream, "user1", "pa$$1", null); - - await SubscribeToStream(stream, "user1", "pa$$1"); - - await DeleteStream(stream, "user1", "pa$$1"); - } - - [Test] - public async Task operations_on_user_stream_fail_for_not_authorized_user() - { - const string stream = "user-not-authorized"; - await AssertEx.ThrowsAsync(() => ReadEvent(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamForward(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward(stream, "user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => WriteStream(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => TransStart(stream, "user2", "pa$$2")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta(stream, "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => WriteMeta(stream, "user2", "pa$$2", null)); - - await AssertEx.ThrowsAsync(() => SubscribeToStream(stream, "user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => DeleteStream(stream, "user2", "pa$$2")); - } - - [Test] - public async Task operations_on_user_stream_fail_for_anonymous_user() - { - const string stream = "user-anonymous-user"; - await AssertEx.ThrowsAsync(() => ReadEvent(stream, null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamForward(stream, null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamBackward(stream, null, null)); - - await AssertEx.ThrowsAsync(() => WriteStream(stream, null, null)); - await AssertEx.ThrowsAsync(() => TransStart(stream, null, null)); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta(stream, null, null)); - await AssertEx.ThrowsAsync(() => WriteMeta(stream, null, null, null)); - - await AssertEx.ThrowsAsync(() => SubscribeToStream(stream, null, null)); - - await AssertEx.ThrowsAsync(() => DeleteStream(stream, null, null)); - } - - [Test] - public async Task operations_on_user_stream_succeed_for_admin() - { - const string stream = "user-admin"; - await ReadEvent(stream, "adm", "admpa$$"); - await ReadStreamForward(stream, "adm", "admpa$$"); - await ReadStreamBackward(stream, "adm", "admpa$$"); - - await WriteStream(stream, "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart(stream, "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart(stream, "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta(stream, "adm", "admpa$$"); - await WriteMeta(stream, "adm", "admpa$$", null); - - await SubscribeToStream(stream, "adm", "admpa$$"); - - await DeleteStream(stream, "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/read_all_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/read_all_security.cs deleted file mode 100644 index cf18b122fc..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/read_all_security.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_security : AuthenticationTestBase -{ - [Test] - public async Task reading_all_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => ReadAllForward("badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadAllBackward("badlogin", "badpass")); - } - - [Test] - public async Task reading_all_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => ReadAllForward(null, null)); - await AssertEx.ThrowsAsync(() => ReadAllBackward(null, null)); - } - - [Test] - public async Task reading_all_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => ReadAllForward("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadAllBackward("user2", "pa$$2")); - } - - [Test] - public async Task reading_all_with_authorized_user_credentials_succeeds() - { - await ReadAllForward("user1", "pa$$1"); - await ReadAllBackward("user1", "pa$$1"); - } - - [Test] - public async Task reading_all_with_admin_credentials_succeeds() - { - await ReadAllForward("adm", "admpa$$"); - await ReadAllBackward("adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/read_stream_meta_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/read_stream_meta_security.cs deleted file mode 100644 index 6f6ebdd066..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/read_stream_meta_security.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_stream_meta_security : AuthenticationTestBase -{ - [Test] - public async Task reading_stream_meta_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => ReadMeta("metaread-stream", "badlogin", "badpass")); - } - - [Test] - public async Task reading_stream_meta_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => ReadMeta("metaread-stream", null, null)); - } - - [Test] - public async Task reading_stream_meta_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => ReadMeta("metaread-stream", "user2", "pa$$2")); - } - - [Test] - public async Task reading_stream_meta_with_authorized_user_credentials_succeeds() - { - await ReadMeta("metaread-stream", "user1", "pa$$1"); - } - - [Test] - public async Task reading_stream_meta_with_admin_user_credentials_succeeds() - { - await ReadMeta("metaread-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task reading_no_acl_stream_meta_succeeds_when_no_credentials_are_passed() - { - await ReadMeta("noacl-stream", null, null); - } - - [Test] - public async Task reading_no_acl_stream_meta_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => ReadMeta("noacl-stream", "badlogin", "badpass")); - } - - [Test] - public async Task reading_no_acl_stream_meta_succeeds_when_any_existing_user_credentials_are_passed() - { - await ReadMeta("noacl-stream", "user1", "pa$$1"); - await ReadMeta("noacl-stream", "user2", "pa$$2"); - } - - [Test] - public async Task reading_no_acl_stream_meta_succeeds_when_admin_user_credentials_are_passed() - { - await ReadMeta("noacl-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task reading_all_access_normal_stream_meta_succeeds_when_no_credentials_are_passed() - { - await ReadMeta("normal-all", null, null); - } - - [Test] - public async Task - reading_all_access_normal_stream_meta_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => ReadMeta("normal-all", "badlogin", "badpass")); - } - - [Test] - public async Task reading_all_access_normal_stream_meta_succeeds_when_any_existing_user_credentials_are_passed() - { - await ReadMeta("normal-all", "user1", "pa$$1"); - await ReadMeta("normal-all", "user2", "pa$$2"); - } - - [Test] - public async Task reading_all_access_normal_stream_meta_succeeds_when_admin_user_credentials_are_passed() - { - await ReadMeta("normal-all", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/read_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/read_stream_security.cs deleted file mode 100644 index 277c586fbb..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/read_stream_security.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_stream_security : AuthenticationTestBase -{ - [Test] - public async Task reading_stream_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => ReadEvent("read-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("read-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("read-stream", "badlogin", "badpass")); - } - - [Test] - public async Task reading_stream_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => ReadEvent("read-stream", null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamForward("read-stream", null, null)); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("read-stream", null, null)); - } - - [Test] - public async Task reading_stream_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => ReadEvent("read-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("read-stream", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("read-stream", "user2", "pa$$2")); - } - - [Test] - public async Task reading_stream_with_authorized_user_credentials_succeeds() - { - await ReadEvent("read-stream", "user1", "pa$$1"); - await ReadStreamForward("read-stream", "user1", "pa$$1"); - await ReadStreamBackward("read-stream", "user1", "pa$$1"); - } - - [Test] - public async Task reading_stream_with_admin_user_credentials_succeeds() - { - await ReadEvent("read-stream", "adm", "admpa$$"); - await ReadStreamForward("read-stream", "adm", "admpa$$"); - await ReadStreamBackward("read-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task reading_no_acl_stream_succeeds_when_no_credentials_are_passed() - { - await ReadEvent("noacl-stream", null, null); - await ReadStreamForward("noacl-stream", null, null); - await ReadStreamBackward("noacl-stream", null, null); - } - - [Test] - public async Task reading_no_acl_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => ReadEvent("noacl-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("noacl-stream", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("noacl-stream", "badlogin", "badpass")); - } - - [Test] - public async Task reading_no_acl_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await ReadEvent("noacl-stream", "user1", "pa$$1"); - await ReadStreamForward("noacl-stream", "user1", "pa$$1"); - await ReadStreamBackward("noacl-stream", "user1", "pa$$1"); - await ReadEvent("noacl-stream", "user2", "pa$$2"); - await ReadStreamForward("noacl-stream", "user2", "pa$$2"); - await ReadStreamBackward("noacl-stream", "user2", "pa$$2"); - } - - [Test] - public async Task reading_no_acl_stream_succeeds_when_admin_user_credentials_are_passed() - { - await ReadEvent("noacl-stream", "adm", "admpa$$"); - await ReadStreamForward("noacl-stream", "adm", "admpa$$"); - await ReadStreamBackward("noacl-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task reading_all_access_normal_stream_succeeds_when_no_credentials_are_passed() - { - await ReadEvent("normal-all", null, null); - await ReadStreamForward("normal-all", null, null); - await ReadStreamBackward("normal-all", null, null); - } - - [Test] - public async Task reading_all_access_normal_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => ReadEvent("normal-all", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("normal-all", "badlogin", "badpass")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("normal-all", "badlogin", "badpass")); - } - - [Test] - public async Task reading_all_access_normal_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await ReadEvent("normal-all", "user1", "pa$$1"); - await ReadStreamForward("normal-all", "user1", "pa$$1"); - await ReadStreamBackward("normal-all", "user1", "pa$$1"); - await ReadEvent("normal-all", "user2", "pa$$2"); - await ReadStreamForward("normal-all", "user2", "pa$$2"); - await ReadStreamBackward("normal-all", "user2", "pa$$2"); - } - - [Test] - public async Task reading_all_access_normal_stream_succeeds_when_admin_user_credentials_are_passed() - { - await ReadEvent("normal-all", "adm", "admpa$$"); - await ReadStreamForward("normal-all", "adm", "admpa$$"); - await ReadStreamBackward("normal-all", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/stream_security_inheritance.cs b/src/EventStore.Core.Tests/ClientAPI/Security/stream_security_inheritance.cs deleted file mode 100644 index d1a395ef9f..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/stream_security_inheritance.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class stream_security_inheritance : AuthenticationTestBase -{ - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - var settings = new SystemSettings(userStreamAcl: new StreamAcl(null, "user1", null, null, null), - systemStreamAcl: new StreamAcl(null, "user1", null, null, null)); - await Connection.SetSystemSettingsAsync(settings, new UserCredentials("adm", "admpa$$")); - - await Connection.SetStreamMetadataAsync("user-no-acl", ExpectedVersion.NoStream, - StreamMetadata.Build(), new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync("user-w-diff", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRole("user2"), new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync("user-w-multiple", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRoles(new[] { "user1", "user2" }), - new UserCredentials("adm", "admpa$$")) - ; - await Connection.SetStreamMetadataAsync("user-w-restricted", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRoles(new string[0]), new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync("user-w-all", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRole(SystemRoles.All), new UserCredentials("adm", "admpa$$")); - - await Connection.SetStreamMetadataAsync("user-r-restricted", ExpectedVersion.NoStream, - StreamMetadata.Build().SetReadRole("user1"), new UserCredentials("adm", "admpa$$")); - - await Connection.SetStreamMetadataAsync("$sys-no-acl", ExpectedVersion.NoStream, - StreamMetadata.Build(), new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync("$sys-w-diff", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRole("user2"), new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync("$sys-w-multiple", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRoles(new[] { "user1", "user2" }), - new UserCredentials("adm", "admpa$$")) - ; - await Connection.SetStreamMetadataAsync("$sys-w-restricted", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRoles(new string[0]), new UserCredentials("adm", "admpa$$")); - await Connection.SetStreamMetadataAsync("$sys-w-all", ExpectedVersion.NoStream, - StreamMetadata.Build().SetWriteRole(SystemRoles.All), new UserCredentials("adm", "admpa$$")); - } - - [Test] - public async Task acl_inheritance_is_working_properly_on_user_streams() - { - await AssertEx.ThrowsAsync(() => WriteStream("user-no-acl", null, null)); - await WriteStream("user-no-acl", "user1", "pa$$1"); - await AssertEx.ThrowsAsync(() => WriteStream("user-no-acl", "user2", "pa$$2")); - await WriteStream("user-no-acl", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("user-w-diff", null, null)); - await AssertEx.ThrowsAsync(() => WriteStream("user-w-diff", "user1", "pa$$1")); - await WriteStream("user-w-diff", "user2", "pa$$2"); - await WriteStream("user-w-diff", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("user-w-multiple", null, null)); - await WriteStream("user-w-multiple", "user1", "pa$$1"); - await WriteStream("user-w-multiple", "user2", "pa$$2"); - await WriteStream("user-w-multiple", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("user-w-restricted", null, null)); - await AssertEx.ThrowsAsync(() => WriteStream("user-w-restricted", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => WriteStream("user-w-restricted", "user2", "pa$$2")); - await WriteStream("user-w-restricted", "adm", "admpa$$"); - - await WriteStream("user-w-all", null, null); - await WriteStream("user-w-all", "user1", "pa$$1"); - await WriteStream("user-w-all", "user2", "pa$$2"); - await WriteStream("user-w-all", "adm", "admpa$$"); - - - await ReadEvent("user-no-acl", null, null); - await ReadEvent("user-no-acl", "user1", "pa$$1"); - await ReadEvent("user-no-acl", "user2", "pa$$2"); - await ReadEvent("user-no-acl", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => ReadEvent("user-r-restricted", null, null)); - await ReadEvent("user-r-restricted", "user1", "pa$$1"); - await AssertEx.ThrowsAsync(() => ReadEvent("user-r-restricted", "user2", "pa$$2")); - await ReadEvent("user-r-restricted", "adm", "admpa$$"); - } - - [Test] - public async Task acl_inheritance_is_working_properly_on_system_streams() - { - await AssertEx.ThrowsAsync(() => WriteStream("$sys-no-acl", null, null)); - await WriteStream("$sys-no-acl", "user1", "pa$$1"); - await AssertEx.ThrowsAsync(() => WriteStream("$sys-no-acl", "user2", "pa$$2")); - await WriteStream("$sys-no-acl", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("$sys-w-diff", null, null)); - await AssertEx.ThrowsAsync(() => WriteStream("$sys-w-diff", "user1", "pa$$1")); - await WriteStream("$sys-w-diff", "user2", "pa$$2"); - await WriteStream("$sys-w-diff", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("$sys-w-multiple", null, null)); - await WriteStream("$sys-w-multiple", "user1", "pa$$1"); - await WriteStream("$sys-w-multiple", "user2", "pa$$2"); - await WriteStream("$sys-w-multiple", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => WriteStream("$sys-w-restricted", null, null)); - await AssertEx.ThrowsAsync(() => WriteStream("$sys-w-restricted", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => WriteStream("$sys-w-restricted", "user2", "pa$$2")); - await WriteStream("$sys-w-restricted", "adm", "admpa$$"); - - await WriteStream("$sys-w-all", null, null); - await WriteStream("$sys-w-all", "user1", "pa$$1"); - await WriteStream("$sys-w-all", "user2", "pa$$2"); - await WriteStream("$sys-w-all", "adm", "admpa$$"); - - await AssertEx.ThrowsAsync(() => ReadEvent("$sys-no-acl", null, null)); - await AssertEx.ThrowsAsync(() => ReadEvent("$sys-no-acl", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadEvent("$sys-no-acl", "user2", "pa$$2")); - await ReadEvent("$sys-no-acl", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_all_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_all_security.cs deleted file mode 100644 index 00e4fd49f5..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_all_security.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_to_all_security : AuthenticationTestBase -{ - [Test] - public async Task subscribing_to_all_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => SubscribeToAll("badlogin", "badpass")); - } - - [Test] - public async Task subscribing_to_all_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => SubscribeToAll(null, null)); - } - - [Test] - public async Task subscribing_to_all_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => SubscribeToAll("user2", "pa$$2")); - } - - [Test] - public async Task subscribing_to_all_with_authorized_user_credentials_succeeds() - { - await SubscribeToAll("user1", "pa$$1"); - } - - [Test] - public async Task subscribing_to_all_with_admin_user_credentials_succeeds() - { - await SubscribeToAll("adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_stream_security.cs deleted file mode 100644 index bc113bea71..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/subscribe_to_stream_security.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_to_stream_security : AuthenticationTestBase -{ - [Test] - public async Task subscribing_to_stream_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => SubscribeToStream("read-stream", "badlogin", "badpass")); - } - - [Test] - public async Task subscribing_to_stream_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => SubscribeToStream("read-stream", null, null)); - } - - [Test] - public async Task subscribing_to_stream_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => SubscribeToStream("read-stream", "user2", "pa$$2")); - } - - [Test] - public async Task reading_stream_with_authorized_user_credentials_succeeds() - { - await SubscribeToStream("read-stream", "user1", "pa$$1"); - } - - [Test] - public async Task reading_stream_with_admin_user_credentials_succeeds() - { - await SubscribeToStream("read-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task subscribing_to_no_acl_stream_succeeds_when_no_credentials_are_passed() - { - await SubscribeToStream("noacl-stream", null, null); - } - - [Test] - public async Task subscribing_to_no_acl_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => SubscribeToStream("noacl-stream", "badlogin", "badpass")); - } - - [Test] - public async Task subscribing_to_no_acl_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await SubscribeToStream("noacl-stream", "user1", "pa$$1"); - await SubscribeToStream("noacl-stream", "user2", "pa$$2"); - } - - [Test] - public async Task subscribing_to_no_acl_stream_succeeds_when_admin_user_credentials_are_passed() - { - await SubscribeToStream("noacl-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task subscribing_to_all_access_normal_stream_succeeds_when_no_credentials_are_passed() - { - await SubscribeToStream("normal-all", null, null); - } - - [Test] - public async Task - subscribing_to_all_access_normal_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => SubscribeToStream("normal-all", "badlogin", "badpass")); - } - - [Test] - public async Task subscribing_to_all_access_normal_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await SubscribeToStream("normal-all", "user1", "pa$$1"); - await SubscribeToStream("normal-all", "user2", "pa$$2"); - } - - [Test] - public async Task subscribing_to_all_access_normal_streamm_succeeds_when_admin_user_credentials_are_passed() - { - await SubscribeToStream("normal-all", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/system_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/system_stream_security.cs deleted file mode 100644 index b80f76090c..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/system_stream_security.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class system_stream_security : AuthenticationTestBase -{ - [Test] - public async Task operations_on_system_stream_with_no_acl_set_fail_for_non_admin() - { - await AssertEx.ThrowsAsync(() => ReadEvent("$system-no-acl", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("$system-no-acl", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("$system-no-acl", "user1", "pa$$1")); - - await AssertEx.ThrowsAsync(() => WriteStream("$system-no-acl", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => TransStart("$system-no-acl", "user1", "pa$$1")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-no-acl", "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta("$system-no-acl", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => WriteMeta("$system-no-acl", "user1", "pa$$1", null)); - - await AssertEx.ThrowsAsync(() => SubscribeToStream("$system-no-acl", "user1", "pa$$1")); - } - - [Test] - public async Task operations_on_system_stream_with_no_acl_set_succeed_for_admin() - { - await ReadEvent("$system-no-acl", "adm", "admpa$$"); - await ReadStreamForward("$system-no-acl", "adm", "admpa$$"); - await ReadStreamBackward("$system-no-acl", "adm", "admpa$$"); - - await WriteStream("$system-no-acl", "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-no-acl", "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-no-acl", "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-no-acl", "adm", "admpa$$"); - await WriteMeta("$system-no-acl", "adm", "admpa$$", null); - - await SubscribeToStream("$system-no-acl", "adm", "admpa$$"); - } - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_usual_user_fail_for_not_authorized_user() - { - await AssertEx.ThrowsAsync(() => ReadEvent("$system-acl", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("$system-acl", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("$system-acl", "user2", "pa$$2")); - - await AssertEx.ThrowsAsync(() => WriteStream("$system-acl", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => TransStart("$system-acl", "user2", "pa$$2")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-acl", "user1", "pa$$1")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta("$system-acl", "user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => WriteMeta("$system-acl", "user2", "pa$$2", "user1")); - - await AssertEx.ThrowsAsync(() => SubscribeToStream("$system-acl", "user2", "pa$$2")); - } - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_usual_user_succeed_for_that_user() - { - await ReadEvent("$system-acl", "user1", "pa$$1"); - await ReadStreamForward("$system-acl", "user1", "pa$$1"); - await ReadStreamBackward("$system-acl", "user1", "pa$$1"); - - await WriteStream("$system-acl", "user1", "pa$$1"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-acl", "user1", "pa$$1"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-acl", "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-acl", "user1", "pa$$1"); - await WriteMeta("$system-acl", "user1", "pa$$1", "user1"); - - await SubscribeToStream("$system-acl", "user1", "pa$$1"); - } - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_usual_user_succeed_for_admin() - { - await ReadEvent("$system-acl", "adm", "admpa$$"); - await ReadStreamForward("$system-acl", "adm", "admpa$$"); - await ReadStreamBackward("$system-acl", "adm", "admpa$$"); - - await WriteStream("$system-acl", "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-acl", "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-acl", "user1", "pa$$1")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-acl", "adm", "admpa$$"); - await WriteMeta("$system-acl", "adm", "admpa$$", "user1"); - - await SubscribeToStream("$system-acl", "adm", "admpa$$"); - } - - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_admins_fail_for_usual_user() - { - await AssertEx.ThrowsAsync(() => ReadEvent("$system-adm", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadStreamForward("$system-adm", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => ReadStreamBackward("$system-adm", "user1", "pa$$1")); - - await AssertEx.ThrowsAsync(() => WriteStream("$system-adm", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => TransStart("$system-adm", "user1", "pa$$1")); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-adm", "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => trans.WriteAsync()); - await AssertEx.ThrowsAsync(() => trans.CommitAsync()); - } - - await AssertEx.ThrowsAsync(() => ReadMeta("$system-adm", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => WriteMeta("$system-adm", "user1", "pa$$1", SystemRoles.Admins)); - - await AssertEx.ThrowsAsync(() => SubscribeToStream("$system-adm", "user1", "pa$$1")); - } - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_admins_succeed_for_admin() - { - await ReadEvent("$system-adm", "adm", "admpa$$"); - await ReadStreamForward("$system-adm", "adm", "admpa$$"); - await ReadStreamBackward("$system-adm", "adm", "admpa$$"); - - await WriteStream("$system-adm", "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-adm", "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-adm", "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-adm", "adm", "admpa$$"); - await WriteMeta("$system-adm", "adm", "admpa$$", SystemRoles.Admins); - - await SubscribeToStream("$system-adm", "adm", "admpa$$"); - } - - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_all_succeed_for_not_authenticated_user() - { - await ReadEvent("$system-all", null, null); - await ReadStreamForward("$system-all", null, null); - await ReadStreamBackward("$system-all", null, null); - - await WriteStream("$system-all", null, null); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-all", null, null); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-all", null, null)).TransactionId; - var trans = Connection.ContinueTransaction(transId); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-all", null, null); - await WriteMeta("$system-all", null, null, SystemRoles.All); - - await SubscribeToStream("$system-all", null, null); - } - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_all_succeed_for_usual_user() - { - await ReadEvent("$system-all", "user1", "pa$$1"); - await ReadStreamForward("$system-all", "user1", "pa$$1"); - await ReadStreamBackward("$system-all", "user1", "pa$$1"); - - await WriteStream("$system-all", "user1", "pa$$1"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-all", "user1", "pa$$1"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-all", "user1", "pa$$1")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-all", "user1", "pa$$1"); - await WriteMeta("$system-all", "user1", "pa$$1", SystemRoles.All); - - await SubscribeToStream("$system-all", "user1", "pa$$1"); - } - - [Test] - public async Task operations_on_system_stream_with_acl_set_to_all_succeed_for_admin() - { - await ReadEvent("$system-all", "adm", "admpa$$"); - await ReadStreamForward("$system-all", "adm", "admpa$$"); - await ReadStreamBackward("$system-all", "adm", "admpa$$"); - - await WriteStream("$system-all", "adm", "admpa$$"); - - if (LogFormatHelper.SupportsExplicitTransactions) - { - await TransStart("$system-all", "adm", "admpa$$"); - } - - if (LogFormatHelper.SupportsExplicitTransactions) - { - var transId = (await TransStart("$system-all", "adm", "admpa$$")).TransactionId; - var trans = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await trans.WriteAsync(); - await trans.CommitAsync(); - } - - await ReadMeta("$system-all", "adm", "admpa$$"); - await WriteMeta("$system-all", "adm", "admpa$$", SystemRoles.All); - - await SubscribeToStream("$system-all", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/transactional_write_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/transactional_write_stream_security.cs deleted file mode 100644 index 93cea90bdd..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/transactional_write_stream_security.cs +++ /dev/null @@ -1,181 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class transactional_write_stream_security : AuthenticationTestBase -{ - [Test] - public async Task starting_transaction_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => TransStart("write-stream", "badlogin", "badpass")); - } - - [Test] - public async Task starting_transaction_to_stream_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => TransStart("write-stream", null, null)); - } - - [Test] - public async Task starting_transaction_to_stream_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => TransStart("write-stream", "user2", "pa$$2")); - } - - [Test] - public async Task starting_transaction_to_stream_with_authorized_user_credentials_succeeds() - { - await TransStart("write-stream", "user1", "pa$$1"); - } - - [Test] - public async Task starting_transaction_to_stream_with_admin_user_credentials_succeeds() - { - await TransStart("write-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task committing_transaction_with_not_existing_credentials_is_not_authenticated() - { - var t1 = await TransStart("write-stream", "user1", "pa$$1"); - await t1.WriteAsync(CreateEvents()); - var t2 = Connection.ContinueTransaction(t1.TransactionId, new UserCredentials("badlogin", "badpass")); - - await AssertEx.ThrowsAsync(() => t2.CommitAsync()); - } - - [Test] - public async Task committing_transaction_to_stream_with_no_credentials_is_denied() - { - var t1 = await TransStart("write-stream", "user1", "pa$$1"); - await t1.WriteAsync(); - var t2 = Connection.ContinueTransaction(t1.TransactionId); - await AssertEx.ThrowsAsync(() => t2.CommitAsync()); - } - - [Test] - public async Task committing_transaction_to_stream_with_not_authorized_user_credentials_is_denied() - { - var t1 = await TransStart("write-stream", "user1", "pa$$1"); - await t1.WriteAsync(); - var t2 = Connection.ContinueTransaction(t1.TransactionId, new UserCredentials("user2", "pa$$2")); - await AssertEx.ThrowsAsync(() => t2.CommitAsync()); - } - - [Test] - public async Task committing_transaction_to_stream_with_authorized_user_credentials_succeeds() - { - var transId = (await TransStart("write-stream", "user1", "pa$$1")).TransactionId; - var t2 = Connection.ContinueTransaction(transId, new UserCredentials("user1", "pa$$1")); - await t2.WriteAsync(); - await t2.CommitAsync(); - } - - [Test] - public async Task committing_transaction_to_stream_with_admin_user_credentials_succeeds() - { - var transId = (await TransStart("write-stream", "user1", "pa$$1")).TransactionId; - var t2 = Connection.ContinueTransaction(transId, new UserCredentials("adm", "admpa$$")); - await t2.WriteAsync(); - await t2.CommitAsync(); - } - - - [Test] - public void transaction_to_no_acl_stream_succeeds_when_no_credentials_are_passed() - { - ExpectNoException(async () => - { - var t = await TransStart("noacl-stream", null, null); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - } - - [Test] - public async Task transaction_to_no_acl_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => TransStart("noacl-stream", "badlogin", "badpass")); - } - - [Test] - public void transaction_to_no_acl_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - ExpectNoException(async () => - { - var t = await TransStart("noacl-stream", "user1", "pa$$1"); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - ExpectNoException(async () => - { - var t = await TransStart("noacl-stream", "user2", "pa$$2"); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - } - - [Test] - public void transaction_to_no_acl_stream_succeeds_when_admin_user_credentials_are_passed() - { - ExpectNoException(async () => - { - var t = await TransStart("noacl-stream", "adm", "admpa$$"); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - } - - - [Test] - public void transaction_to_all_access_normal_stream_succeeds_when_no_credentials_are_passed() - { - ExpectNoException(async () => - { - var t = await TransStart("normal-all", null, null); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - } - - [Test] - public async Task - transaction_to_all_access_normal_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => TransStart("normal-all", "badlogin", "badpass")); - } - - [Test] - public void transaction_to_all_access_normal_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - ExpectNoException(async () => - { - var t = await TransStart("normal-all", "user1", "pa$$1"); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - ExpectNoException(async () => - { - var t = await TransStart("normal-all", "user2", "pa$$2"); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - } - - [Test] - public void transaction_to_all_access_normal_stream_succeeds_when_admin_user_credentials_are_passed() - { - ExpectNoException(async () => - { - var t = await TransStart("normal-all", "adm", "admpa$$"); - await t.WriteAsync(CreateEvents()); - await t.CommitAsync(); - }); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/write_stream_meta_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/write_stream_meta_security.cs deleted file mode 100644 index 68b1475159..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/write_stream_meta_security.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Services; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class write_stream_meta_security : AuthenticationTestBase -{ - [Test] - public async Task writing_meta_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => WriteMeta("metawrite-stream", "badlogin", "badpass", "user1")); - } - - [Test] - public async Task writing_meta_to_stream_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => WriteMeta("metawrite-stream", null, null, "user1")); - } - - [Test] - public async Task writing_meta_to_stream_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => WriteMeta("metawrite-stream", "user2", "pa$$2", "user1")); - } - - [Test] - public async Task writing_meta_to_stream_with_authorized_user_credentials_succeeds() - { - await WriteMeta("metawrite-stream", "user1", "pa$$1", "user1"); - } - - [Test] - public async Task writing_meta_to_stream_with_admin_user_credentials_succeeds() - { - await WriteMeta("metawrite-stream", "adm", "admpa$$", "user1"); - } - - - [Test] - public async Task writing_meta_to_no_acl_stream_succeeds_when_no_credentials_are_passed() - { - await WriteMeta("noacl-stream", null, null, null); - } - - [Test] - public async Task writing_meta_to_no_acl_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => WriteMeta("noacl-stream", "badlogin", "badpass", null)); - } - - [Test] - public async Task writing_meta_to_no_acl_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await WriteMeta("noacl-stream", "user1", "pa$$1", null); - await WriteMeta("noacl-stream", "user2", "pa$$2", null); - } - - [Test] - public async Task writing_meta_to_no_acl_stream_succeeds_when_admin_user_credentials_are_passed() - { - await WriteMeta("noacl-stream", "adm", "admpa$$", null); - } - - - [Test] - public async Task writing_meta_to_all_access_normal_stream_succeeds_when_no_credentials_are_passed() - { - await WriteMeta("normal-all", null, null, SystemRoles.All); - } - - [Test] - public async Task - writing_meta_to_all_access_normal_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => WriteMeta("normal-all", "badlogin", "badpass", SystemRoles.All)); - } - - [Test] - public async Task writing_meta_to_all_access_normal_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await WriteMeta("normal-all", "user1", "pa$$1", SystemRoles.All); - await WriteMeta("normal-all", "user2", "pa$$2", SystemRoles.All); - } - - [Test] - public async Task writing_meta_to_all_access_normal_stream_succeeds_when_admin_user_credentials_are_passed() - { - await WriteMeta("normal-all", "adm", "admpa$$", SystemRoles.All); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/Security/write_stream_security.cs b/src/EventStore.Core.Tests/ClientAPI/Security/write_stream_security.cs deleted file mode 100644 index 8581de126b..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/Security/write_stream_security.cs +++ /dev/null @@ -1,101 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.Security; - -[Category("ClientAPI"), Category("LongRunning"), Category("Network")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class write_stream_security : AuthenticationTestBase -{ - [Test] - public async Task writing_to_all_is_never_allowed() - { - await AssertEx.ThrowsAsync(() => WriteStream("$all", null, null)); - await AssertEx.ThrowsAsync(() => WriteStream("$all", "user1", "pa$$1")); - await AssertEx.ThrowsAsync(() => WriteStream("$all", "adm", "admpa$$")); - } - - [Test] - public async Task writing_with_not_existing_credentials_is_not_authenticated() - { - await AssertEx.ThrowsAsync(() => WriteStream("write-stream", "badlogin", "badpass")); - } - - [Test] - public async Task writing_to_stream_with_no_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => WriteStream("write-stream", null, null)); - } - - [Test] - public async Task writing_to_stream_with_not_authorized_user_credentials_is_denied() - { - await AssertEx.ThrowsAsync(() => WriteStream("write-stream", "user2", "pa$$2")); - } - - [Test] - public async Task writing_to_stream_with_authorized_user_credentials_succeeds() - { - await WriteStream("write-stream", "user1", "pa$$1"); - } - - [Test] - public async Task writing_to_stream_with_admin_user_credentials_succeeds() - { - await WriteStream("write-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task writing_to_no_acl_stream_succeeds_when_no_credentials_are_passed() - { - await WriteStream("noacl-stream", null, null); - } - - [Test] - public async Task writing_to_no_acl_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => WriteStream("noacl-stream", "badlogin", "badpass")); - } - - [Test] - public async Task writing_to_no_acl_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await WriteStream("noacl-stream", "user1", "pa$$1"); - await WriteStream("noacl-stream", "user2", "pa$$2"); - } - - [Test] - public async Task writing_to_no_acl_stream_succeeds_when_any_admin_user_credentials_are_passed() - { - await WriteStream("noacl-stream", "adm", "admpa$$"); - } - - - [Test] - public async Task writing_to_all_access_normal_stream_succeeds_when_no_credentials_are_passed() - { - await WriteStream("normal-all", null, null); - } - - [Test] - public async Task - writing_to_all_access_normal_stream_is_not_authenticated_when_not_existing_credentials_are_passed() - { - await AssertEx.ThrowsAsync(() => WriteStream("normal-all", "badlogin", "badpass")); - } - - [Test] - public async Task writing_to_all_access_normal_stream_succeeds_when_any_existing_user_credentials_are_passed() - { - await WriteStream("normal-all", "user1", "pa$$1"); - await WriteStream("normal-all", "user2", "pa$$2"); - } - - [Test] - public async Task writing_to_all_access_normal_stream_succeeds_when_any_admin_user_credentials_are_passed() - { - await WriteStream("normal-all", "adm", "admpa$$"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToDeletedEvents.cs b/src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToDeletedEvents.cs deleted file mode 100644 index 9b77af2ca7..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToDeletedEvents.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Messages; - -namespace EventStore.Core.Tests.ClientAPI; - -public abstract class SpecificationWithLinkToToDeletedEvents - : SpecificationWithMiniNode -{ - protected string LinkedStreamName; - protected string DeletedStreamName; - - protected override async Task Given() - { - var creds = DefaultData.AdminCredentials; - LinkedStreamName = Guid.NewGuid().ToString(); - DeletedStreamName = Guid.NewGuid().ToString(); - await _conn.AppendToStreamAsync(DeletedStreamName, ExpectedVersion.Any, creds, - new EventData(Guid.NewGuid(), "testing", true, Encoding.UTF8.GetBytes("{'foo' : 4}"), new byte[0])); - await _conn.AppendToStreamAsync(LinkedStreamName, ExpectedVersion.Any, creds, - new EventData(Guid.NewGuid(), SystemEventTypes.LinkTo, false, - Encoding.UTF8.GetBytes("0@" + DeletedStreamName), new byte[0])); - await _conn.DeleteStreamAsync(DeletedStreamName, ExpectedVersion.Any); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToMaxCountDeletedEvents.cs b/src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToMaxCountDeletedEvents.cs deleted file mode 100644 index 07b510971a..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithLinkToToMaxCountDeletedEvents.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common; -using EventStore.ClientAPI.SystemData; - -namespace EventStore.Core.Tests.ClientAPI; - -public abstract class SpecificationWithLinkToToMaxCountDeletedEvents - : SpecificationWithMiniNode -{ - protected string LinkedStreamName; - protected string DeletedStreamName; - - protected override async Task Given() - { - var creds = DefaultData.AdminCredentials; - DeletedStreamName = Guid.NewGuid().ToString(); - LinkedStreamName = Guid.NewGuid().ToString(); - await _conn.AppendToStreamAsync(DeletedStreamName, ExpectedVersion.Any, creds, - new EventData(Guid.NewGuid(), "testing1", true, Encoding.UTF8.GetBytes("{'foo' : 4}"), new byte[0])); - await _conn.SetStreamMetadataAsync(DeletedStreamName, ExpectedVersion.Any, - new StreamMetadata(2, null, null, null, null)); - await _conn.AppendToStreamAsync(DeletedStreamName, ExpectedVersion.Any, creds, - new EventData(Guid.NewGuid(), "testing2", true, Encoding.UTF8.GetBytes("{'foo' : 4}"), new byte[0])); - await _conn.AppendToStreamAsync(DeletedStreamName, ExpectedVersion.Any, creds, - new EventData(Guid.NewGuid(), "testing3", true, Encoding.UTF8.GetBytes("{'foo' : 4}"), new byte[0])); - await _conn.AppendToStreamAsync(LinkedStreamName, ExpectedVersion.Any, creds, - new EventData(Guid.NewGuid(), SystemEventTypes.LinkTo, false, - Encoding.UTF8.GetBytes("0@" + DeletedStreamName), new byte[0])); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs b/src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs deleted file mode 100644 index ddd01c25d0..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/SpecificationWithMiniNode.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -public abstract class SpecificationWithMiniNode : SpecificationWithDirectoryPerTestFixture -{ - private readonly int _chunkSize; - protected MiniNode _node; - protected IEventStoreConnection _conn; - protected virtual TimeSpan Timeout { get; } = TimeSpan.FromMinutes(1); - protected virtual TimeSpan StartupTimeout => TimeSpan.FromMinutes(5); - - protected virtual Task Given() => Task.CompletedTask; - - protected abstract Task When(); - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.CreateMiniNodeClient(node.TcpEndPoint, TcpType.Ssl); - } - - protected Task CloseConnectionAndWait(IEventStoreConnection connection) => - TestConnectionLifecycle.CloseConnectionAndWait(connection, Timeout); - - protected SpecificationWithMiniNode() : this(chunkSize: 1024 * 1024) { } - - protected SpecificationWithMiniNode(int chunkSize) - { - _chunkSize = chunkSize; - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - - MiniNodeLogging.Setup(); - - try - { - await base.TestFixtureSetUp(); - } - catch (Exception ex) - { - throw new Exception("TestFixtureSetUp Failed", ex); - } - - try - { - _node = new MiniNode(PathName, chunkSize: _chunkSize); - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(StartupTimeout); - _conn = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - connection => connection.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - } - catch (Exception ex) - { - MiniNodeLogging.WriteLogs(); - throw new Exception("MiniNodeSetUp Failed", ex); - } - - try - { - await Given().WithTimeout(Timeout); - } - catch (Exception ex) - { - MiniNodeLogging.WriteLogs(); - throw new Exception("Given Failed", ex); - } - - try - { - await When().WithTimeout(Timeout); - } - catch (Exception ex) - { - MiniNodeLogging.WriteLogs(); - throw new Exception("When Failed", ex); - } - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - if (_conn != null) - { - await TestConnectionLifecycle.CloseConnectionAndWait(_conn, Timeout); - } - - await _node.Shutdown(); - await base.TestFixtureTearDown(); - - MiniNodeLogging.Clear(); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/SystemData/clientapi_tcp_package.cs b/src/EventStore.Core.Tests/ClientAPI/SystemData/clientapi_tcp_package.cs deleted file mode 100644 index a9a6a61bf1..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/SystemData/clientapi_tcp_package.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using EventStore.ClientAPI.SystemData; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI.SystemData; - -[TestFixture, Category("ClientAPI")] -public class clientapi_tcp_package -{ - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_login_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), null, "pa$$", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_password_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), "login", null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_token_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_login_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), "login", null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_password_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), null, "pa$$", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_token_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), "token", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void not_authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, corrId, null, null, new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.None, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void not_authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, corrId, null, null, new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.None, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "login", "pa$$", - new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("login", pkg.Tokens["uid"]); - Assert.AreEqual("pa$$", pkg.Tokens["pwd"]); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "login", "pa$$", - new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("login", pkg.Tokens["uid"]); - Assert.AreEqual("pa$$", pkg.Tokens["pwd"]); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void token_authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "token", - new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("token", pkg.Tokens["jwt"]); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void token_authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "token", - new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("token", pkg.Tokens["jwt"]); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void should_throw_argument_exception_when_login_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), new string('*', TcpPackage.MaxLoginLength + 1), "pa$$", new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_password_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), "login", new string('*', TcpPackage.MaxPasswordLength + 1), new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_token_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), new string('*', TcpPackage.MaxTokenLength + 1), new byte[] { 1, 2, 3 })); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/UserManagement/TestWithNode.cs b/src/EventStore.Core.Tests/ClientAPI/UserManagement/TestWithNode.cs deleted file mode 100644 index 3717bc41e6..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/UserManagement/TestWithNode.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using EventStore.Client.Users; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common.Log; -using EventStore.ClientAPI.SystemData; -using EventStore.ClientAPI.UserManagement; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using Grpc.Core; -using Grpc.Net.Client; -using NUnit.Framework; -using UsersClient = EventStore.Client.Users.Users.UsersClient; - -namespace EventStore.Core.Tests.ClientAPI.UserManagement; - -[Category("LongRunning"), Category("ClientAPI")] -public abstract class TestWithNode : SpecificationWithDirectoryPerTestFixture -{ - protected MiniNode _node; - protected UsersManager _manager; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - _manager = new UsersManager(new NoopLogger(), _node.HttpEndPoint, TimeSpan.FromSeconds(5), httpMessageHandler: _node.HttpMessageHandler); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - protected async Task CreateUserWithGrpc( - string loginName, - string fullName, - string[] groups, - string password, - UserCredentials credentials) - { - using var channel = GrpcChannel.ForAddress(new Uri($"https://{_node.HttpEndPoint}"), - new GrpcChannelOptions - { - HttpClient = _node.HttpClient, - DisposeHttpClient = false - }); - var users = new UsersClient(channel); - await users.CreateAsync(new CreateReq - { - Options = new CreateReq.Types.Options - { - LoginName = loginName, - FullName = fullName, - Groups = { groups }, - Password = password - } - }, new CallOptions(new Metadata { - {"authorization", BasicAuthHeader(credentials)} - })); - } - - private static string BasicAuthHeader(UserCredentials credentials) => - "Basic " + Convert.ToBase64String( - Encoding.ASCII.GetBytes($"{credentials.Username}:{credentials.Password}")); -} diff --git a/src/EventStore.Core.Tests/ClientAPI/append_to_stream.cs b/src/EventStore.Core.Tests/ClientAPI/append_to_stream.cs deleted file mode 100644 index 60c1bbdce9..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/append_to_stream.cs +++ /dev/null @@ -1,798 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class append_to_stream : SpecificationWithDirectoryPerTestFixture -{ - private readonly TcpType _tcpType = TcpType.Ssl; - private MiniNode _node; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, _tcpType); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test, Category("Network")] - public async Task should_allow_appending_zero_events_to_stream_with_no_problems() - { - const string stream1 = "should_allow_appending_zero_events_to_stream_with_no_problems1"; - const string stream2 = "should_allow_appending_zero_events_to_stream_with_no_problems2"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - Assert.AreEqual(-1, - (await store.AppendToStreamAsync(stream1, ExpectedVersion.Any)).NextExpectedVersion); - Assert.AreEqual(-1, - (await store.AppendToStreamAsync(stream1, ExpectedVersion.NoStream)).NextExpectedVersion); - Assert.AreEqual(-1, (await store.AppendToStreamAsync(stream1, ExpectedVersion.Any)).NextExpectedVersion); - Assert.AreEqual(-1, - (await store.AppendToStreamAsync(stream1, ExpectedVersion.NoStream)).NextExpectedVersion); - - var read1 = await store.ReadStreamEventsForwardAsync(stream1, 0, 2, resolveLinkTos: false); - Assert.That(read1.Events.Length, Is.EqualTo(0)); - - Assert.AreEqual(-1, (await store.AppendToStreamAsync(stream2, ExpectedVersion.NoStream)).NextExpectedVersion); - Assert.AreEqual(-1, (await store.AppendToStreamAsync(stream2, ExpectedVersion.Any)).NextExpectedVersion); - Assert.AreEqual(-1, (await store.AppendToStreamAsync(stream2, ExpectedVersion.NoStream)).NextExpectedVersion); - Assert.AreEqual(-1, (await store.AppendToStreamAsync(stream2, ExpectedVersion.Any)).NextExpectedVersion); - - var read2 = await store.ReadStreamEventsForwardAsync(stream2, 0, 2, resolveLinkTos: false); - Assert.That(read2.Events.Length, Is.EqualTo(0)); - } - } - - [Test, Category("Network")] - public async Task should_create_stream_with_no_stream_exp_ver_on_first_write_if_does_not_exist() - { - const string stream = "should_create_stream_with_no_stream_exp_ver_on_first_write_if_does_not_exist"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - Assert.AreEqual(0, (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())) - .NextExpectedVersion); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 2, resolveLinkTos: false); - Assert.That(read.Events.Length, Is.EqualTo(1)); - } - } - - [Test] - [Category("Network")] - public async Task should_create_stream_with_any_exp_ver_on_first_write_if_does_not_exist() - { - const string stream = "should_create_stream_with_any_exp_ver_on_first_write_if_does_not_exist"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())).NextExpectedVersion); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 2, resolveLinkTos: false); - Assert.That(read.Events.Length, Is.EqualTo(1)); - } - } - - [Test] - [Category("Network")] - public async Task multiple_idempotent_writes() - { - const string stream = "multiple_idempotent_writes"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new[] { - TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), - TestEvent.NewTestEvent() - }; - Assert.AreEqual(3, - (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, events)).NextExpectedVersion); - Assert.AreEqual(3, - (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, events)).NextExpectedVersion); - } - } - - [Test] - [Category("Network")] - public async Task multiple_idempotent_writes_with_same_id_bug_case() - { - const string stream = "multiple_idempotent_writes_with_same_id_bug_case"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var x = TestEvent.NewTestEvent(); - var events = new[] { x, x, x, x, x, x }; - Assert.AreEqual(5, (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, events)).NextExpectedVersion); - } - } - - [Test] - [Category("Network")] - public async Task - in_case_where_multiple_writes_of_multiple_events_with_the_same_ids_using_expected_version_any_then_next_expected_version_is_unreliable() - { - const string stream = "in_wtf_multiple_case_of_multiple_writes_expected_version_any_per_all_same_id"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var x = TestEvent.NewTestEvent(); - var events = new[] { x, x, x, x, x, x }; - Assert.AreEqual(5, (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, events)).NextExpectedVersion); - var f = await store.AppendToStreamAsync(stream, ExpectedVersion.Any, events); - Assert.AreEqual(0, f.NextExpectedVersion); - } - } - - [Test] - [Category("Network")] - public async Task - in_case_where_multiple_writes_of_multiple_events_with_the_same_ids_using_expected_version_nostream_then_next_expected_version_is_correct() - { - const string stream = - "in_slightly_reasonable_multiple_case_of_multiple_writes_with_expected_version_per_all_same_id"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var x = TestEvent.NewTestEvent(); - var events = new[] { x, x, x, x, x, x }; - Assert.AreEqual(5, - (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - var f = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events); - Assert.AreEqual(5, f.NextExpectedVersion); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_writing_with_correct_exp_ver_to_deleted_stream() - { - const string stream = "should_fail_writing_with_correct_exp_ver_to_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())); - - } - } - - [Test] - [Category("Network")] - public async Task should_return_log_position_when_writing() - { - const string stream = "should_return_log_position_when_writing"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var result = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - Assert.IsTrue(0 < result.LogPosition.PreparePosition); - Assert.IsTrue(0 < result.LogPosition.CommitPosition); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_writing_with_any_exp_ver_to_deleted_stream() - { - const string stream = "should_fail_writing_with_any_exp_ver_to_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - try - { - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - } - catch (Exception exc) - { - Console.WriteLine(exc); - Assert.Fail(); - } - - await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_writing_with_invalid_exp_ver_to_deleted_stream() - { - const string stream = "should_fail_writing_with_invalid_exp_ver_to_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync(() => store.AppendToStreamAsync(stream, 5, TestEvent.NewTestEvent())); - } - } - - [Test] - [Category("Network")] - public async Task should_append_with_correct_exp_ver_to_existing_stream() - { - const string stream = "should_append_with_correct_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - await store.AppendToStreamAsync(stream, 0, new[] { TestEvent.NewTestEvent() }); - } - } - - [Test] - [Category("Network")] - public async Task should_append_with_any_exp_ver_to_existing_stream() - { - const string stream = "should_append_with_any_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - Assert.AreEqual(1, (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())).NextExpectedVersion); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_appending_with_wrong_exp_ver_to_existing_stream() - { - const string stream = "should_fail_appending_with_wrong_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var wev = await AssertEx.ThrowsAsync(() => - store.AppendToStreamAsync(stream, 1, new[] { TestEvent.NewTestEvent() })); - Assert.AreEqual(1, wev.ExpectedVersion); - Assert.AreEqual(ExpectedVersion.NoStream, wev.ActualVersion); - } - } - - [Test] - [Category("Network")] - public async Task should_append_with_stream_exists_exp_ver_to_existing_stream() - { - const string stream = "should_append_with_stream_exists_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - await store.AppendToStreamAsync(stream, ExpectedVersion.StreamExists, TestEvent.NewTestEvent()); - } - } - - [Test] - [Category("Network")] - public async Task should_append_with_stream_exists_exp_ver_to_stream_with_multiple_events() - { - const string stream = "should_append_with_stream_exists_exp_ver_to_stream_with_multiple_events"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - for (var i = 0; i < 5; i++) - { - await store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent()); - } - - await store.AppendToStreamAsync(stream, ExpectedVersion.StreamExists, TestEvent.NewTestEvent()); - } - } - - [Test] - [Category("Network")] - public async Task should_append_with_stream_exists_exp_ver_if_metadata_stream_exists() - { - const string stream = "should_append_with_stream_exists_exp_ver_if_metadata_stream_exists"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await store.SetStreamMetadataAsync(stream, ExpectedVersion.Any, - new StreamMetadata(10, null, null, null, null)); - - await store.AppendToStreamAsync(stream, ExpectedVersion.StreamExists, TestEvent.NewTestEvent()); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_appending_with_stream_exists_exp_ver_and_stream_does_not_exist() - { - const string stream = "should_fail_appending_with_stream_exists_exp_ver_and_stream_does_not_exist"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var wev = await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream, ExpectedVersion.StreamExists, TestEvent.NewTestEvent())); - Assert.AreEqual(ExpectedVersion.StreamExists, wev.ExpectedVersion); - Assert.AreEqual(ExpectedVersion.NoStream, wev.ActualVersion); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_appending_with_stream_exists_exp_ver_to_hard_deleted_stream() - { - const string stream = "should_fail_appending_with_stream_exists_exp_ver_to_hard_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream, ExpectedVersion.StreamExists, TestEvent.NewTestEvent())); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_appending_with_stream_exists_exp_ver_to_soft_deleted_stream() - { - const string stream = "should_fail_appending_with_stream_exists_exp_ver_to_soft_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - await store.DeleteStreamAsync(stream, ExpectedVersion.StreamExists, hardDelete: false); - - await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream, ExpectedVersion.StreamExists, TestEvent.NewTestEvent())); - } - } - - [Test, Category("Network")] - public async Task can_append_multiple_events_at_once() - { - const string stream = "can_append_multiple_events_at_once"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 100).Select(i => TestEvent.NewTestEvent(i.ToString(), i.ToString())); - Assert.AreEqual(99, (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - } - } - - [Test] - public async Task cannot_append_multiple_events_larger_than_chunk_size_at_once() - { - const string stream = "cannot_append_multiple_events_larger_than_chunk_size_at_once"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - // Create events with large payloads (20KB each) to exceed chunk size when combined - const int eventPayloadSize = 20_000; // 20KB per event - const int eventCount = 100; // Total: ~2MB, exceeding default 256MB chunk only if chunk is very small - var largeEventData = new string('X', eventPayloadSize); - var events = Enumerable.Range(0, eventCount).Select(i => TestEvent.NewTestEvent(largeEventData, $"event-{i}")); - - // First, write a small batch successfully (4 events) - const int initialBatchSize = 4; - var firstWriteResult = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events.Take(initialBatchSize)); - Assert.AreEqual(initialBatchSize - 1, firstWriteResult.NextExpectedVersion, "First write should succeed"); - - // Attempt to write a transaction that's too large - should fail - await AssertEx.ThrowsAsync(async () => - await store.AppendToStreamAsync(stream, firstWriteResult.NextExpectedVersion, events)); - - // Verify that subsequent writes still work correctly with the expected version - // This tests that the missing return statement bug is fixed - without the fix, - // the expected version would be corrupted and this write would fail - var subsequentWriteResult = await store.AppendToStreamAsync(stream, firstWriteResult.NextExpectedVersion, events.Take(initialBatchSize)); - Assert.AreEqual(firstWriteResult.NextExpectedVersion + initialBatchSize, subsequentWriteResult.NextExpectedVersion, - "Subsequent write should succeed with correct expected version after failed large transaction"); - } - } - - [Test] - public async Task can_append_to_stream_with_long_name() - { - var stream = "can_append_to_stream_with_long_name" + new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - } - } - - [Test] - public async Task can_append_multiple_events_to_stream_with_long_name_at_once() - { - var stream = "can_append_multiple_events_to_stream_with_long_name_at_once" + new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var events = Enumerable.Range(0, 3).Select(i => TestEvent.NewTestEvent(i.ToString(), i.ToString())); - Assert.AreEqual(2, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - } - } - - [Test] - public async Task can_append_event_with_long_event_type() - { - const string stream = "can_append_event_with_long_event_type"; - var eventType = new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(eventName: eventType))).NextExpectedVersion); - } - } - - [Test] - public async Task can_append_multiple_events_with_long_event_type_at_once() - { - const string stream = "can_append_multiple_events_with_long_event_type_at_once"; - var eventType = new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var events = Enumerable.Range(0, 3).Select(i => - TestEvent.NewTestEvent(i.ToString(), i.ToString(), eventName: eventType)); - Assert.AreEqual(2, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - } - } - - [Test, Category("Network")] - public async Task returns_failure_status_when_conditionally_appending_with_version_mismatch() - { - const string stream = "returns_failure_status_when_conditionally_appending_with_version_mismatch"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var result = await store.ConditionalAppendToStreamAsync(stream, 7, new[] { TestEvent.NewTestEvent() }); - - Assert.AreEqual(ConditionalWriteStatus.VersionMismatch, result.Status); - } - } - - [Test, Category("Network")] - public async Task returns_success_status_when_conditionally_appending_with_matching_version() - { - const string stream = "returns_success_status_when_conditionally_appending_with_matching_version"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var result = await store - .ConditionalAppendToStreamAsync(stream, ExpectedVersion.Any, new[] { TestEvent.NewTestEvent() }); - - Assert.AreEqual(ConditionalWriteStatus.Succeeded, result.Status); - Assert.IsNotNull(result.LogPosition); - Assert.IsNotNull(result.NextExpectedVersion); - } - } - - [Test, Category("Network")] - public async Task returns_failure_status_when_conditionally_appending_to_a_deleted_stream() - { - const string stream = "returns_failure_status_when_conditionally_appending_to_a_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent()); - await store.DeleteStreamAsync(stream, ExpectedVersion.Any, true); - - var result = await store - .ConditionalAppendToStreamAsync(stream, ExpectedVersion.Any, new[] { TestEvent.NewTestEvent() }); - - Assert.AreEqual(ConditionalWriteStatus.StreamDeleted, result.Status); - } - } -} - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class ssl_append_to_stream : SpecificationWithDirectoryPerTestFixture -{ - private readonly TcpType _tcpType = TcpType.Ssl; - protected MiniNode _node; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, _tcpType); - } - - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task should_allow_appending_zero_events_to_stream_with_no_problems() - { - const string stream = "should_allow_appending_zero_events_to_stream_with_no_problems"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(-1, - (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream)).NextExpectedVersion); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 2, resolveLinkTos: false); - Assert.That(read.Events.Length, Is.EqualTo(0)); - } - } - - [Test] - public async Task should_create_stream_with_no_stream_exp_ver_on_first_write_if_does_not_exist() - { - const string stream = "should_create_stream_with_no_stream_exp_ver_on_first_write_if_does_not_exist"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 2, resolveLinkTos: false); - Assert.That(read.Events.Length, Is.EqualTo(1)); - } - } - - [Test] - public async Task should_create_stream_with_any_exp_ver_on_first_write_if_does_not_exist() - { - const string stream = "should_create_stream_with_any_exp_ver_on_first_write_if_does_not_exist"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())).NextExpectedVersion); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 2, resolveLinkTos: false); - Assert.That(read.Events.Length, Is.EqualTo(1)); - } - } - - [Test] - public async Task should_fail_writing_with_correct_exp_ver_to_deleted_stream() - { - const string stream = "should_fail_writing_with_correct_exp_ver_to_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync(() => - store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())); - } - } - - [Test] - public async Task should_fail_writing_with_any_exp_ver_to_deleted_stream() - { - const string stream = "should_fail_writing_with_any_exp_ver_to_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync(() => - store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())); - } - } - - [Test] - public async Task should_fail_writing_with_invalid_exp_ver_to_deleted_stream() - { - const string stream = "should_fail_writing_with_invalid_exp_ver_to_deleted_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync(() => - store.AppendToStreamAsync(stream, 5, new[] { TestEvent.NewTestEvent() })); - } - } - - [Test] - public async Task should_append_with_correct_exp_ver_to_existing_stream() - { - const string stream = "should_append_with_correct_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - Assert.AreEqual(1, (await store.AppendToStreamAsync(stream, 0, TestEvent.NewTestEvent())).NextExpectedVersion); - } - } - - [Test] - public async Task should_append_with_any_exp_ver_to_existing_stream() - { - const string stream = "should_append_with_any_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - Assert.AreEqual(1, (await store.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())).NextExpectedVersion); - } - } - - [Test] - public async Task should_return_log_position_when_writing() - { - const string stream = "should_return_log_position_when_writing"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var result = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - Assert.IsTrue(0 < result.LogPosition.PreparePosition); - Assert.IsTrue(0 < result.LogPosition.CommitPosition); - } - } - - [Test] - public async Task should_fail_appending_with_wrong_exp_ver_to_existing_stream() - { - const string stream = "should_fail_appending_with_wrong_exp_ver_to_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - - var wev = await AssertEx.ThrowsAsync(() => - store.AppendToStreamAsync(stream, 1, TestEvent.NewTestEvent())); - Assert.AreEqual(1, wev.ExpectedVersion); - Assert.AreEqual(0, wev.ActualVersion.GetValueOrDefault(0)); - } - } - - [Test] - public async Task can_append_multiple_events_at_once() - { - const string stream = "can_append_multiple_events_at_once"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 100).Select(i => TestEvent.NewTestEvent(i.ToString(), i.ToString())); - Assert.AreEqual(99, (await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - } - } - - [Test] - public async Task cannot_append_multiple_events_larger_than_chunk_size_at_once() - { - const string stream = "cannot_append_multiple_events_larger_than_chunk_size_at_once"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - // Create events with large payloads (20KB each) to exceed chunk size when combined - const int eventPayloadSize = 20_000; // 20KB per event - const int eventCount = 100; // Total: ~2MB, exceeding default 256MB chunk only if chunk is very small - var largeEventData = new string('X', eventPayloadSize); - var events = Enumerable.Range(0, eventCount).Select(i => TestEvent.NewTestEvent(largeEventData, $"event-{i}")); - - // First, write a small batch successfully (4 events) - const int initialBatchSize = 4; - var firstWriteResult = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events.Take(initialBatchSize)); - Assert.AreEqual(initialBatchSize - 1, firstWriteResult.NextExpectedVersion, "First write should succeed"); - - // Attempt to write a transaction that's too large - should fail - await AssertEx.ThrowsAsync(async () => - await store.AppendToStreamAsync(stream, firstWriteResult.NextExpectedVersion, events)); - - // Verify that subsequent writes still work correctly with the expected version - // This tests that the missing return statement bug is fixed - without the fix, - // the expected version would be corrupted and this write would fail - var subsequentWriteResult = await store.AppendToStreamAsync(stream, firstWriteResult.NextExpectedVersion, events.Take(initialBatchSize)); - Assert.AreEqual(firstWriteResult.NextExpectedVersion + initialBatchSize, subsequentWriteResult.NextExpectedVersion, - "Subsequent write should succeed with correct expected version after failed large transaction"); - } - } - - [Test] - public async Task can_append_to_stream_with_long_name() - { - var stream = "can_append_to_stream_with_long_name" + new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())).NextExpectedVersion); - } - } - - [Test] - public async Task can_append_multiple_events_to_stream_with_long_name_at_once() - { - var stream = "can_append_multiple_events_to_stream_with_long_name_at_once" + new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var events = Enumerable.Range(0, 3).Select(i => TestEvent.NewTestEvent(i.ToString(), i.ToString())); - Assert.AreEqual(2, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - } - } - - [Test] - public async Task can_append_event_with_long_event_type() - { - const string stream = "can_append_event_with_long_event_type"; - var eventType = new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(0, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(eventName: eventType))).NextExpectedVersion); - } - } - - [Test] - public async Task can_append_multiple_events_with_long_event_type_at_once() - { - const string stream = "can_append_multiple_events_with_long_event_type_at_once"; - var eventType = new string('A', 300); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var events = Enumerable.Range(0, 3).Select(i => - TestEvent.NewTestEvent(i.ToString(), i.ToString(), eventName: eventType)); - Assert.AreEqual(2, (await store.AppendToStreamAsync - (stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/append_to_stream_with_hash_collision.cs b/src/EventStore.Core.Tests/ClientAPI/append_to_stream_with_hash_collision.cs deleted file mode 100644 index 9fcd053034..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/append_to_stream_with_hash_collision.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Storage.HashCollisions; - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class append_to_stream_with_hash_collision : SpecificationWithDirectoryPerTestFixture -{ - private const int LongRunningTimeout = 1200000; - private static readonly TimeSpan StartupTimeout = TimeSpan.FromMinutes(10); - private MiniNode _node; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, TcpType.Ssl); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(StartupTimeout); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test, Timeout(LongRunningTimeout)] - public async Task should_throw_wrong_expected_version() - { - const string stream1 = "account--696193173"; - const string stream2 = "LPN-FC002_LPK51001"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - //Write event to stream 1 - Assert.AreEqual(0, (await store.AppendToStreamAsync(stream1, ExpectedVersion.NoStream, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - //Write 100 events to stream 2 which will have the same hash as stream 1. - for (int i = 0; i < 100; i++) - { - Assert.AreEqual(i, (await store.AppendToStreamAsync(stream2, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - } - } - - var tcpPort = _node.TcpEndPoint.Port; - var httpPort = _node.HttpEndPoint.Port; - await _node.Shutdown(keepDb: true); - - //Restart the node to ensure the read index stream info cache is empty - _node = new MiniNode(PathName, - tcpPort, httpPort, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(StartupTimeout); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream1, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream.cs b/src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream.cs deleted file mode 100644 index 28059a09c4..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream.cs +++ /dev/null @@ -1,323 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class appending_to_implicitly_created_stream : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - /* - * sequence - events written so stream - * 0em1 - event number 0 written with exp version -1 (minus 1) - * 1any - event number 1 written with exp version any - * S_0em1_1em1_E - START bucket, two events in bucket, END bucket - */ - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0em1_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0em1_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var tail = await writer.Append(events); - - await tail.Then(events[0], -1); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_4e4_0any_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_sequence_0em1_1e0_2e1_3e2_4e3_4e4_0any_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var tail = await writer.Append(events); - await tail.Then(events.First(), ExpectedVersion.Any); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e5_non_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e5_non_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var tail = await writer.Append(events); - await tail.Then(events.First(), 5); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length + 1)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e6_wev() - { - const string stream = "appending_to_implicitly_created_stream_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e6_wev"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var first6 = await writer.Append(events); - await AssertEx.ThrowsAsync( - () => first6.Then(events.First(), 6)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e4_wev() - { - const string stream = "appending_to_implicitly_created_stream_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e4_wev"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var first6 = await writer.Append(events); - await AssertEx.ThrowsAsync( - () => first6.Then(events.First(), 4)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_0e0_non_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_0em1_0e0_non_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 1).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var tail = await writer.Append(events); - await tail.Then(events.First(), 0); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length + 1)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_0any_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_0em1_0any_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 1).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var tail = await writer.Append(events); - - await tail.Then(events.First(), ExpectedVersion.Any); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_0em1_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_0em1_0em1_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 1).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - var tail = await writer.Append(events); - await tail.Then(events.First(), -1); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_1any_1any_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_0em1_1e0_2e1_1any_1any_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 3).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new StreamWriter(store, stream, -1); - - - var tailWriter = await writer.Append(events); - tailWriter = await tailWriter.Then(events[1], ExpectedVersion.Any); - await tailWriter.Then(events[1], ExpectedVersion.Any); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_S_0em1_1em1_E_S_0em1_E_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_S_0em1_1em1_E_S_0em1_E_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 2).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - await store.AppendToStreamAsync(stream, -1, events); - - await store.AppendToStreamAsync(stream, -1, events.First()); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_S_0em1_1em1_E_S_0any_E_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_S_0em1_1em1_E_S_0any_E_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 2).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - await store.AppendToStreamAsync(stream, -1, events); - - await store.AppendToStreamAsync(stream, ExpectedVersion.Any, new[] { events.First() }); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_S_0em1_1em1_E_S_1e0_E_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_S_0em1_1em1_E_S_1e0_E_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 2).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - await store.AppendToStreamAsync(stream, -1, events); - - await store.AppendToStreamAsync(stream, 0, events[1]); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_S_0em1_1em1_E_S_1any_E_idempotent() - { - const string stream = "appending_to_implicitly_created_stream_sequence_S_0em1_1em1_E_S_1any_E_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 2).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - await store.AppendToStreamAsync(stream, -1, events); - - await store.AppendToStreamAsync(stream, ExpectedVersion.Any, events[1]); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_S_0em1_1em1_E_S_0em1_1em1_2em1_E_idempotancy_fail() - { - const string stream = - "appending_to_implicitly_created_stream_sequence_S_0em1_1em1_E_S_0em1_1em1_2em1_E_idempotancy_fail"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 2).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - await store.AppendToStreamAsync(stream, -1, events); - - await AssertEx.ThrowsAsync( - () => store.AppendToStreamAsync(stream, -1, - events.Concat(new[] { TestEvent.NewTestEvent(Guid.NewGuid()) }))); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream_using_transaction.cs b/src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream_using_transaction.cs deleted file mode 100644 index efeb03aaf2..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/appending_to_implicitly_created_stream_using_transaction.cs +++ /dev/null @@ -1,251 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class appending_to_implicitly_created_stream_using_transaction : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - virtual protected IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - /* - * sequence - events written so stream - * 0em1 - event number 0 written with exp version -1 (minus 1) - * 1any - event number 1 written with exp version any - * S_0em1_1em1_E - START bucket, two events in bucket, END bucket - */ - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0em1_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0em1_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(5, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(0, (await (await (await writer.StartTransaction(-1)).Write(events.First())).Commit()).NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0any_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0any_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(5, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(0, (await (await (await writer.StartTransaction(ExpectedVersion.Any)).Write(events.First())).Commit()).NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e5_non_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e5_non_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(5, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(6, (await (await (await writer.StartTransaction(5)).Write(events.First())).Commit()).NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length + 1)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e6_wev() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e6_wev"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(5, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - await AssertEx.ThrowsAsync(async () => - await (await (await writer.StartTransaction(6)).Write(events.First())).Commit()); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e4_wev() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_1e0_2e1_3e2_4e3_5e4_0e4_wev"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 6).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(5, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - await AssertEx.ThrowsAsync( - async () => await (await (await writer.StartTransaction(4)).Write(events.First())).Commit()); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_0e0_non_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_0e0_non_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 1).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(0, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(1, (await (await (await writer.StartTransaction(0)).Write(events.First())).Commit()).NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length + 1)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_0any_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_0any_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 1).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(0, - (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(0, - (await (await (await writer.StartTransaction(ExpectedVersion.Any)).Write(events.First())).Commit()) - .NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_0em1_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_0em1_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 1).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(0, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(0, (await (await (await writer.StartTransaction(-1)).Write(events.First())).Commit()).NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_0em1_1e0_2e1_1any_1any_idempotent() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_0em1_1e0_2e1_1any_1any_idempotent"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 3).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(2, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - Assert.AreEqual(1, (await (await (await (await writer.StartTransaction(ExpectedVersion.Any)).Write(events[1])).Write(events[1])).Commit()).NextExpectedVersion); - - var total = await EventsStream.Count(store, stream); - Assert.That(total, Is.EqualTo(events.Length)); - } - } - - [Test] - [Category("Network")] - public async Task sequence_S_0em1_1em1_E_S_0em1_1em1_2em1_E_idempotancy_fail() - { - const string stream = - "appending_to_implicitly_created_stream_using_transaction_sequence_S_0em1_1em1_E_S_0em1_1em1_2em1_E_idempotancy_fail"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = Enumerable.Range(0, 2).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var writer = new TransactionalWriter(store, stream); - - Assert.AreEqual(1, (await (await (await writer.StartTransaction(-1)).Write(events)).Commit()).NextExpectedVersion); - await AssertEx.ThrowsAsync(async () => - await (await (await writer.StartTransaction(-1)).Write(events.Concat(new[] { TestEvent.NewTestEvent(Guid.NewGuid()) }).ToArray())).Commit()); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/appending_to_streams_across_restart.cs b/src/EventStore.Core.Tests/ClientAPI/appending_to_streams_across_restart.cs deleted file mode 100644 index 6ffbfdbba7..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/appending_to_streams_across_restart.cs +++ /dev/null @@ -1,187 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class appending_to_streams_across_restart : SpecificationWithDirectory -{ - private MiniNode _node; - private static readonly TimeSpan ReadinessTimeout = TimeSpan.FromSeconds(60); - - virtual protected IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [TearDown] - public override async Task TearDown() - { - await _node?.Shutdown(); - await base.TearDown(); - } - - [Test] - [Category("Network")] - [Timeout(80000)] - public async Task detect_existing_streams_flush() - { - void CreateNode() - { - _node = new MiniNode( - pathname: PathName, - dbPath: Path.Combine(PathName, "mini-node-db"), - streamExistenceFilterSize: 10_000, - streamExistenceFilterCheckpointIntervalMs: 100, - streamExistenceFilterCheckpointDelayMs: 0); - } - - CreateNode(); - await _node.Start(); - await WaitForNodeReadiness(); - - // GIVEN some streams - var normal = "detect_existing_streams_normal"; - var meta = "detect_existing_streams_meta"; - var committed = "detect_existing_streams_commited_transaction"; - var uncommitted = "detect_existing_streams_uncommited_transaction"; - - EventData[] GenEvents() => Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(Guid.NewGuid())).ToArray(); - var eventsByStream = new Dictionary(){ - { normal, GenEvents() }, - { committed, GenEvents() }, - { uncommitted, GenEvents() }, - }; - - var uncommittedTransId = 0L; - - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - // normal - Assert.AreEqual(9, await store - .Apply(x => x.AppendToStreamAsync(normal, ExpectedVersion.NoStream, eventsByStream[normal])) - .Apply(x => x.NextExpectedVersion)); - - // meta - Assert.AreEqual(0, await store - .Apply(x => x.SetStreamMetadataAsync("meta", ExpectedVersion.NoStream, StreamMetadata.Create(maxCount: 5))) - .Apply(x => x.NextExpectedVersion)); - - if (LogFormatHelper.IsV2) - { - // committed - Assert.AreEqual(9, await new TransactionalWriter(store, committed) - .Apply(x => x.StartTransaction(-1)) - .Apply(x => x.Write(eventsByStream[committed])) - .Apply(x => x.Commit()) - .Apply(x => x.NextExpectedVersion)); - - // uncommitted - uncommittedTransId = await new TransactionalWriter(store, uncommitted) - .Apply(x => x.StartTransaction(-1)) - .Apply(x => x.Write(eventsByStream[committed])) - .Apply(x => x.TransactionId); - } - - // append an event to another stream because the last event is always going to get reinitialised - Assert.AreEqual(0, await store - .Apply(x => x.AppendToStreamAsync("another-stream", ExpectedVersion.NoStream, TestEvent.NewTestEvent(Guid.NewGuid()))) - .Apply(x => x.NextExpectedVersion)); - } - - // WHEN flush and restart - await Task.Delay(500); - await _node.Shutdown(keepDb: true); - CreateNode(); - await _node.Start(); - await WaitForNodeReadiness(); - - // THEN the streams all exist - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - // normal - Assert.AreEqual(10, await store - .Apply(x => x.AppendToStreamAsync(normal, 9, TestEvent.NewTestEvent(Guid.NewGuid()))) - .Apply(x => x.NextExpectedVersion)); - - Assert.AreEqual( - eventsByStream[normal].Length + 1, - await EventsStream.Count(store, normal)); - - // meta - Assert.AreEqual(1, await store - .Apply(x => x.SetStreamMetadataAsync("meta", 0, StreamMetadata.Create(maxCount: 6))) - .Apply(x => x.NextExpectedVersion)); - - Assert.AreEqual(0, await EventsStream.Count(store, meta)); - - if (LogFormatHelper.IsV2) - { - // committed - Assert.AreEqual(10, await store - .Apply(x => x.AppendToStreamAsync(committed, 9, TestEvent.NewTestEvent(Guid.NewGuid()))) - .Apply(x => x.NextExpectedVersion)); - - Assert.AreEqual( - eventsByStream[committed].Length + 1, - await EventsStream.Count(store, committed)); - - // uncommitted - Assert.AreEqual(9, await new TransactionalWriter(store, uncommitted) - .Apply(x => x.ContinueTransaction(uncommittedTransId)) - .Apply(x => x.Commit()) - .Apply(x => x.NextExpectedVersion)); - - Assert.AreEqual( - eventsByStream[uncommitted].Length, - await EventsStream.Count(store, uncommitted)); - } - } - } - - private async Task WaitForNodeReadiness() - { - await _node.WaitForTcpEndPoint().WithTimeout(ReadinessTimeout); - using var connection = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - conn => conn.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - ReadinessTimeout); - } -} - -public static class Extensions -{ - public async static Task Apply(this Task x, Func> f) - { - var r = await x; - return await f(r); - } - - public static U Apply(this T x, Func f) - { - return f(x); - } - - public async static Task Apply(this T x, Func> f) - { - return await f(x); - } - - public async static Task Apply(this Task x, Func f) - { - var r = await x; - return f(r); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/catch_up_subscription_handles_errors.cs b/src/EventStore.Core.Tests/ClientAPI/catch_up_subscription_handles_errors.cs deleted file mode 100644 index 93a44ceabc..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/catch_up_subscription_handles_errors.cs +++ /dev/null @@ -1,753 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.ClientOperations; -using EventStore.ClientAPI.Common.Log; -using EventStore.ClientAPI.Internal; -using EventStore.ClientAPI.SystemData; -using NUnit.Framework; -using ClientMessage = EventStore.ClientAPI.Messages.ClientMessage; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; -using SystemSettings = EventStore.ClientAPI.SystemSettings; - -namespace EventStore.Core.Tests.ClientAPI; - -[TestFixture, Category("ClientAPI"), Category("LongRunning")] -public class catch_up_subscription_handles_errors -{ - private const int TimeoutMs = 2000; - private FakeEventStoreConnection _connection; - private IList _raisedEvents; - private bool _liveProcessingStarted; - private bool _isDropped; - private TaskCompletionSource _dropEvent; - private TaskCompletionSource _raisedEventEvent; - private Exception _dropException; - private SubscriptionDropReason _dropReason; - private EventStoreStreamCatchUpSubscription _subscription; - private static readonly string StreamId = "stream1"; - - [SetUp] - public void SetUp() - { - _connection = new FakeEventStoreConnection(); - _raisedEvents = new List(); - _dropEvent = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - _raisedEventEvent = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - _liveProcessingStarted = false; - _isDropped = false; - _dropReason = SubscriptionDropReason.Unknown; - _dropException = null; - - var settings = new CatchUpSubscriptionSettings(1, 1, false, false, String.Empty); - _subscription = new EventStoreStreamCatchUpSubscription(_connection, new NoopLogger(), StreamId, null, null, - (subscription, ev) => - { - _raisedEvents.Add(ev); - if (_raisedEvents.Count >= 2) - { - _raisedEventEvent.TrySetResult(true); - } - return Task.CompletedTask; - }, - subscription => { _liveProcessingStarted = true; }, - (subscription, reason, ex) => - { - _isDropped = true; - _dropReason = reason; - _dropException = ex; - _dropEvent.TrySetResult(true); - }, - settings); - } - - [Test] - public async Task read_events_til_stops_subscription_when_throws_immediately() - { - var expectedException = new ApplicationException("Test"); - - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - Assert.That(stream, Is.EqualTo(StreamId)); - Assert.That(start, Is.EqualTo(0)); - Assert.That(max, Is.EqualTo(1)); - throw expectedException; - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - } - - private async Task AssertStartFailsAndDropsSubscriptionWithException(ApplicationException expectedException) - { - await AssertEx.ThrowsAsync(() => _subscription.StartAsync()); - Assert.That(_isDropped); - Assert.That(_dropReason, Is.EqualTo(SubscriptionDropReason.CatchUpError)); - Assert.That(_dropException, Is.SameAs(expectedException)); - Assert.That(_liveProcessingStarted, Is.False); - } - - [Test] - public async Task read_events_til_stops_subscription_when_throws_asynchronously() - { - var expectedException = new ApplicationException("Test"); - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - Assert.That(stream, Is.EqualTo(StreamId)); - Assert.That(start, Is.EqualTo(0)); - Assert.That(max, Is.EqualTo(1)); - taskCompletionSource.SetException(expectedException); - return taskCompletionSource.Task; - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - } - - [Test] - public async Task read_events_til_stops_subscription_when_second_read_throws_immediately() - { - var expectedException = new ApplicationException("Test"); - - int callCount = 0; - - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - Assert.That(stream, Is.EqualTo(StreamId)); - Assert.That(max, Is.EqualTo(1)); - - if (callCount++ == 0) - { - Assert.That(start, Is.EqualTo(0)); - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateStreamEventsSlice()); - return taskCompletionSource.Task; - } - else - { - Assert.That(start, Is.EqualTo(1)); - throw expectedException; - } - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - Assert.That(_raisedEvents.Count, Is.EqualTo(1)); - } - - [Test] - public async Task read_events_til_stops_subscription_when_second_read_throws_asynchronously() - { - var expectedException = new ApplicationException("Test"); - - int callCount = 0; - - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - Assert.That(stream, Is.EqualTo(StreamId)); - Assert.That(max, Is.EqualTo(1)); - - if (callCount++ == 0) - { - Assert.That(start, Is.EqualTo(0)); - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateStreamEventsSlice()); - return taskCompletionSource.Task; - } - else - { - Assert.That(start, Is.EqualTo(1)); - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetException(expectedException); - return taskCompletionSource.Task; - } - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - Assert.That(_raisedEvents.Count, Is.EqualTo(1)); - } - - [Test] - public async Task start_stops_subscription_if_subscribe_fails_immediately() - { - var expectedException = new ApplicationException("Test"); - - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateStreamEventsSlice(isEnd: true)); - return taskCompletionSource.Task; - }); - - _connection.HandleSubscribeToStreamAsync((stream, raise, drop) => - { - Assert.That(stream, Is.EqualTo(StreamId)); - throw expectedException; - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - Assert.That(_raisedEvents.Count, Is.EqualTo(1)); - } - - [Test] - public async Task start_stops_subscription_if_subscribe_fails_async() - { - var expectedException = new ApplicationException("Test"); - - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateStreamEventsSlice(isEnd: true)); - return taskCompletionSource.Task; - }); - - _connection.HandleSubscribeToStreamAsync((stream, raise, drop) => - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions - .RunContinuationsAsynchronously); - taskCompletionSource.SetException(expectedException); - return taskCompletionSource.Task; - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - Assert.That(_raisedEvents.Count, Is.EqualTo(1)); - } - - [Test] - public async Task start_stops_subscription_if_historical_missed_events_load_fails_immediate() - { - var expectedException = new ApplicationException("Test"); - - int callCount = 0; - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - if (callCount++ == 0) - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateStreamEventsSlice(isEnd: true)); - return taskCompletionSource.Task; - } - else - { - Assert.That(start, Is.EqualTo(1)); - throw expectedException; - } - }); - - _connection.HandleSubscribeToStreamAsync((stream, raise, drop) => - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions - .RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateVolatileSubscription(raise, drop, 1)); - return taskCompletionSource.Task; - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - Assert.That(_raisedEvents.Count, Is.EqualTo(1)); - } - - - [Test] - public async Task start_stops_subscription_if_historical_missed_events_load_fails_async() - { - var expectedException = new ApplicationException("Test"); - - int callCount = 0; - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => - { - if (callCount++ == 0) - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateStreamEventsSlice(isEnd: true)); - return taskCompletionSource.Task; - } - else - { - Assert.That(start, Is.EqualTo(1)); - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - taskCompletionSource.SetException(expectedException); - return taskCompletionSource.Task; - } - }); - - _connection.HandleSubscribeToStreamAsync((stream, raise, drop) => - { - var taskCompletionSource = - new TaskCompletionSource(TaskCreationOptions - .RunContinuationsAsynchronously); - taskCompletionSource.SetResult(CreateVolatileSubscription(raise, drop, 1)); - return taskCompletionSource.Task; - }); - - await AssertStartFailsAndDropsSubscriptionWithException(expectedException); - Assert.That(_raisedEvents.Count, Is.EqualTo(1)); - } - - [Test] - public async Task start_completes_onces_subscription_is_live() - { - var finalEvent = new ManualResetEventSlim(); - int callCount = 0; - _connection.HandleReadStreamEventsForwardAsync((stream, start, max) => Task.Run(() => - { - callCount++; - - if (callCount == 1) - { - return Task.FromResult(CreateStreamEventsSlice(isEnd: true)); - } - else if (callCount == 2) - { - var result = Task.FromResult(CreateStreamEventsSlice(fromEvent: 1, isEnd: true)); - Assert.That(finalEvent.Wait(TimeoutMs)); - return result; - } - else - { - throw new InvalidOperationException("Should not get a third call"); - } - })); - - _connection.HandleSubscribeToStreamAsync((stream, raise, drop) => - Task.FromResult(CreateVolatileSubscription(raise, drop, 1))); - - var task = _subscription.StartAsync(); - - Assert.That(task.Status, Is.Not.EqualTo(TaskStatus.RanToCompletion)); - - finalEvent.Set(); - - await task.WithTimeout(TimeoutMs); - } - - private static VolatileEventStoreSubscription CreateVolatileSubscription( - Func raise, - Action drop, int? lastEventNumber) - { - return new VolatileEventStoreSubscription( - new VolatileSubscriptionOperation(new NoopLogger(), - new TaskCompletionSource(TaskCreationOptions - .RunContinuationsAsynchronously), StreamId, false, null, raise, drop, false, () => null), - StreamId, -1, lastEventNumber); - } - - private static StreamEventsSlice CreateStreamEventsSlice(int fromEvent = 0, int count = 1, bool isEnd = false) - { - var events = Enumerable.Range(0, count) - .Select( - i => - new ClientMessage.ResolvedIndexedEvent( - new ClientMessage.EventRecord(StreamId, i, Guid.NewGuid().ToByteArray(), null, 0, 0, null, - null, null, null), null)) - .ToArray(); - - return new StreamEventsSlice(SliceReadStatus.Success, StreamId, fromEvent, ReadDirection.Forward, events, - fromEvent + count, 100, isEnd); - } -} - -internal class FakeEventStoreConnection : IEventStoreConnection -{ - private Func> _readAllEventsForwardAsync; - private Func> _readStreamEventsForwardAsync; - - private Func, - Action, Task> - _subscribeToStreamAsync; - - private Func, - Action, Task> - _subscribeToAllAsync; - - public void Dispose() - { - throw new NotImplementedException(); - } - - public string ConnectionName { get; } - - public ConnectionSettings Settings - { - get { return null; } - } - - public Task ConnectAsync() - { - throw new NotImplementedException(); - } - - public void Close() - { - throw new NotImplementedException(); - } - - public Task DeleteStreamAsync(string stream, long expectedVersion, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task DeleteStreamAsync(string stream, long expectedVersion, bool hardDelete, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task AppendToStreamAsync(string stream, long expectedVersion, params EventData[] events) - { - throw new NotImplementedException(); - } - - public Task AppendToStreamAsync(string stream, long expectedVersion, - UserCredentials userCredentials, params EventData[] events) - { - throw new NotImplementedException(); - } - - public Task AppendToStreamAsync(string stream, long expectedVersion, IEnumerable events, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task ConditionalAppendToStreamAsync(string stream, long expectedVersion, - IEnumerable events, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task StartTransactionAsync(string stream, long expectedVersion, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public EventStoreTransaction ContinueTransaction(long transactionId, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task ReadEventAsync(string stream, long eventNumber, bool resolveLinkTos, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public void HandleReadStreamEventsForwardAsync(Func> callback) - { - _readStreamEventsForwardAsync = callback; - } - - public Task ReadStreamEventsForwardAsync(string stream, long start, int count, - bool resolveLinkTos, - UserCredentials userCredentials = null) - { - return _readStreamEventsForwardAsync(stream, start, count); - } - - public Task ReadStreamEventsBackwardAsync(string stream, long start, int count, - bool resolveLinkTos, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public void HandleReadAllEventsForwardAsync( - Func> callback) - { - _readAllEventsForwardAsync = callback; - } - - public Task ReadAllEventsForwardAsync(Position position, int maxCount, bool resolveLinkTos, - UserCredentials userCredentials = null) - { - return _readAllEventsForwardAsync(position, maxCount, resolveLinkTos, userCredentials); - } - - public Task FilteredReadAllEventsForwardAsync(Position position, int maxCount, bool resolveLinkTos, Filter filter, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task FilteredReadAllEventsForwardAsync(Position position, int maxCount, bool resolveLinkTos, Filter filter, - int maxSearchWindow, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task ReadAllEventsBackwardAsync(Position position, int maxCount, bool resolveLinkTos, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task FilteredReadAllEventsBackwardAsync(Position position, int maxCount, bool resolveLinkTos, Filter filter, - int maxSearchWindow, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task FilteredReadAllEventsBackwardAsync(Position position, int maxCount, bool resolveLinkTos, Filter filter, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public void HandleSubscribeToStreamAsync( - Func, - Action, Task> - callback) - { - _subscribeToStreamAsync = callback; - } - - public Task SubscribeToStreamAsync(string stream, bool resolveLinkTos, - Func eventAppeared, - Action subscriptionDropped = null, - UserCredentials userCredentials = null) - { - return _subscribeToStreamAsync(stream, eventAppeared, subscriptionDropped); - } - - public EventStoreStreamCatchUpSubscription SubscribeToStreamFrom(string stream, long? lastCheckpoint, - bool resolveLinkTos, - Func eventAppeared, - Action liveProcessingStarted = null, - Action subscriptionDropped = null, - UserCredentials userCredentials = null, int readBatchSize = 500, string subscriptionName = "") - { - throw new NotImplementedException(); - } - - public EventStoreStreamCatchUpSubscription SubscribeToStreamFrom( - string stream, - long? lastCheckpoint, - CatchUpSubscriptionSettings settings, - Func eventAppeared, - Action liveProcessingStarted = null, - Action subscriptionDropped = null, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public void HandleSubscribeToAllAsync( - Func, - Action, Task> - callback) - { - _subscribeToAllAsync = callback; - } - - public Task SubscribeToAllAsync(bool resolveLinkTos, - Func eventAppeared, - Action subscriptionDropped = null, - UserCredentials userCredentials = null) - { - return _subscribeToAllAsync(resolveLinkTos, eventAppeared, subscriptionDropped); - } - - public Task FilteredSubscribeToAllAsync(bool resolveLinkTos, Filter filter, Func eventAppeared, Func checkpointReached, - int checkpointInterval, Action subscriptionDropped = null, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task FilteredSubscribeToAllAsync(bool resolveLinkTos, Filter filter, Func eventAppeared, - Action subscriptionDropped = null, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task SubscribeToAllFilteredAsync(bool resolveLinkTos, Filter filter, - Func eventAppeared, - Func checkpointReached = null, - int? checkpointInterval = null, - Action subscriptionDropped = null, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public EventStorePersistentSubscriptionBase ConnectToPersistentSubscription(string stream, string groupName, - Func eventAppeared, - Action subscriptionDropped = null, - UserCredentials userCredentials = null, int bufferSize = 10, - bool autoAck = true) - { - throw new NotImplementedException(); - } - - public Task ConnectToPersistentSubscriptionAsync(string stream, - string groupName, Func eventAppeared, - Action subscriptionDropped = null, - UserCredentials userCredentials = null, int bufferSize = 10, - bool autoAck = true) - { - throw new NotImplementedException(); - } - - public EventStoreAllCatchUpSubscription SubscribeToAllFrom(Position? lastCheckpoint, bool resolveLinkTos, - Func eventAppeared, - Action liveProcessingStarted = null, - Action subscriptionDropped = null, - UserCredentials userCredentials = null, - int readBatchSize = 500, - string subscriptionName = "") - { - throw new NotImplementedException(); - } - - public EventStoreAllCatchUpSubscription SubscribeToAllFrom( - Position? lastCheckpoint, - CatchUpSubscriptionSettings settings, - Func eventAppeared, - Action liveProcessingStarted = null, - Action subscriptionDropped = null, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public EventStoreAllFilteredCatchUpSubscription FilteredSubscribeToAllFrom(Position? lastCheckpoint, Filter filter, - CatchUpSubscriptionFilteredSettings settings, Func eventAppeared, Func checkpointReached, - int checkpointIntervalMultiplier, Action liveProcessingStarted = null, Action subscriptionDropped = null, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public EventStoreAllFilteredCatchUpSubscription FilteredSubscribeToAllFrom(Position? lastCheckpoint, Filter filter, - CatchUpSubscriptionFilteredSettings settings, Func eventAppeared, Action liveProcessingStarted = null, - Action subscriptionDropped = null, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task UpdatePersistentSubscriptionAsync(string stream, string groupName, - PersistentSubscriptionSettings settings, - UserCredentials credentials) - { - throw new NotImplementedException(); - } - - public Task CreatePersistentSubscriptionAsync(string stream, string groupName, - PersistentSubscriptionSettings settings, - UserCredentials credentials) - { - throw new NotImplementedException(); - } - - public Task DeletePersistentSubscriptionAsync(string stream, string groupName, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task SetStreamMetadataAsync(string stream, long expectedMetastreamVersion, - StreamMetadata metadata, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task SetStreamMetadataAsync(string stream, long expectedMetastreamVersion, byte[] metadata, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task - GetStreamMetadataAsync(string stream, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task GetStreamMetadataAsRawBytesAsync(string stream, - UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public Task SetSystemSettingsAsync(SystemSettings settings, UserCredentials userCredentials = null) - { - throw new NotImplementedException(); - } - - public event EventHandler Connected; - public event EventHandler Disconnected; - public event EventHandler Reconnecting; - public event EventHandler Closed; - public event EventHandler ErrorOccurred; - public event EventHandler AuthenticationFailed; - - protected virtual void OnErrorOccurred(ClientErrorEventArgs e) - { - var handler = ErrorOccurred; - if (handler != null) - { - handler(this, e); - } - } - - protected virtual void OnAuthenticationFailed(ClientAuthenticationFailedEventArgs e) - { - var handler = AuthenticationFailed; - if (handler != null) - { - handler(this, e); - } - } - - protected virtual void OnClosed(ClientClosedEventArgs e) - { - var handler = Closed; - if (handler != null) - { - handler(this, e); - } - } - - protected virtual void OnReconnecting(ClientReconnectingEventArgs e) - { - var handler = Reconnecting; - if (handler != null) - { - handler(this, e); - } - } - - protected virtual void OnDisconnected(ClientConnectionEventArgs e) - { - var handler = Disconnected; - if (handler != null) - { - handler(this, e); - } - } - - public void OnConnected(ClientConnectionEventArgs e) - { - var handler = Connected; - if (handler != null) - { - handler(this, e); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/catchup_filtered_subscription.cs b/src/EventStore.Core.Tests/ClientAPI/catchup_filtered_subscription.cs deleted file mode 100644 index 0d8df30fc9..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/catchup_filtered_subscription.cs +++ /dev/null @@ -1,358 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("LongRunning"), Category("ClientAPI"), NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class catchup_filtered_subscription : SpecificationWithDirectory -{ - private MiniNode _node; - private IEventStoreConnection _conn; - private List _testEvents; - private List _testEventsAfter; - private const int Timeout = 10000; - private static readonly TimeSpan StartupTimeout = TimeSpan.FromMinutes(10); - private static readonly TimeSpan ConnectionCloseTimeout = TimeSpan.FromSeconds(10); - private const int LongRunningTimeout = 600000; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(TimeSpan.FromSeconds(60)); - - _conn = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - conn => conn.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - await _conn.SetStreamMetadataAsync(SystemStreams.AllStream, -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - - _testEvents = Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "AEvent")) - .ToList(); - - _testEvents.AddRange( - Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "BEvent")).ToList()); - - _testEventsAfter = Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "AEvent")) - .ToList(); - - _testEventsAfter.AddRange( - Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "BEvent")).ToList()); - - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.OddEvents()); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test] - public void calls_checkpoint_delegate_during_catchup() - { - var filter = Filter.StreamId.Prefix("stream-a"); - // in v2 there are 20 events, 10 in stream-a and 10 in stream-b. - // in v3 there are additionally two stream records and two event type records - var isV2 = LogFormatHelper.IsV2; - var checkpointReached = new CountdownEvent(isV2 ? 10 : 14); - var eventsSeen = 0; - - var settings = new CatchUpSubscriptionFilteredSettings( - maxLiveQueueSize: 10000, - readBatchSize: 2, - verboseLogging: false, - resolveLinkTos: true, - maxSearchWindow: 2, - subscriptionName: String.Empty - ); - - var subscription = _conn.FilteredSubscribeToAllFrom( - Position.Start, - filter, - settings, - (s, e) => - { - eventsSeen++; - return Task.CompletedTask; - }, - (s, p) => - { - checkpointReached.Signal(); - return Task.CompletedTask; - }, 1); - - try - { - if (!checkpointReached.Wait(Timeout)) - { - Assert.Fail("Checkpoint reached not called enough times within time limit."); - } - - Assert.AreEqual(10, eventsSeen); - } - finally - { - StopSubscription(subscription); - } - } - - [Test] - public void calls_checkpoint_during_live_processing_stage() - { - var filter = Filter.StreamId.Prefix("stream-a"); - var appeared = new CountdownEvent(_testEventsAfter.EvenEvents().Count + 1); // Calls once for switch to live. - var eventsSeen = 0; - var isLive = false; - - var settings = new CatchUpSubscriptionFilteredSettings( - 10000, - 1, - verboseLogging: false, - resolveLinkTos: true, - maxSearchWindow: 1, - subscriptionName: String.Empty - ); - - var subscription = _conn.FilteredSubscribeToAllFrom( - Position.Start, - filter, - settings, - (s, e) => - { - eventsSeen++; - return Task.CompletedTask; - }, - (s, p) => - { - if (isLive) - { - appeared.Signal(); - } - - return Task.CompletedTask; - }, 1, s => - { - isLive = true; - _conn.AppendToStreamAsync("stream-a", ExpectedVersion.Any, _testEventsAfter.EvenEvents()).Wait(); - }); - - try - { - if (!appeared.Wait(Timeout)) - { - Assert.Fail("Checkpoint appeared not called enough times within time limit."); - } - - Assert.AreEqual(20, eventsSeen); - } - finally - { - StopSubscription(subscription); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public void only_return_events_with_a_given_stream_prefix() - { - var filter = Filter.StreamId.Prefix("stream-a"); - var foundEvents = new ConcurrentBag(); - var appeared = new CountdownEvent(20); - var subscription = Subscribe(filter, foundEvents, appeared); - try - { - if (!appeared.Wait(Timeout)) - { - Assert.Fail("Appeared countdown event timed out."); - } - - Assert.True(foundEvents.All(e => e.Event.EventStreamId == "stream-a")); - } - finally - { - StopSubscription(subscription); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public void only_return_events_with_a_given_event_prefix() - { - var filter = Filter.EventType.Prefix("AE"); - var foundEvents = new ConcurrentBag(); - var appeared = new CountdownEvent(20); - var subscription = Subscribe(filter, foundEvents, appeared); - try - { - if (!appeared.Wait(Timeout)) - { - Assert.Fail("Appeared countdown event timed out."); - } - - Assert.True(foundEvents.All(e => e.Event.EventType == "AEvent")); - } - finally - { - StopSubscription(subscription); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public void only_return_events_that_satisfy_a_given_stream_regex() - { - var filter = Filter.StreamId.Regex(new Regex(@"^.*eam-b.*$")); - var foundEvents = new ConcurrentBag(); - var appeared = new CountdownEvent(20); - var subscription = Subscribe(filter, foundEvents, appeared); - try - { - if (!appeared.Wait(Timeout)) - { - Assert.Fail("Appeared countdown event timed out."); - } - - Assert.True(foundEvents.All(e => e.Event.EventStreamId == "stream-b")); - } - finally - { - StopSubscription(subscription); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public void only_return_events_that_satisfy_a_given_event_regex() - { - var filter = Filter.EventType.Regex(new Regex(@"^.*BEv.*$")); - var foundEvents = new ConcurrentBag(); - var appeared = new CountdownEvent(20); - var subscription = Subscribe(filter, foundEvents, appeared); - try - { - if (!appeared.Wait(Timeout)) - { - Assert.Fail("Appeared countdown event timed out."); - } - - Assert.True(foundEvents.All(e => e.Event.EventType == "BEvent")); - } - finally - { - StopSubscription(subscription); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public void only_return_events_that_are_not_system_events() - { - var filter = Filter.ExcludeSystemEvents; - var foundEvents = new ConcurrentBag(); - using var appeared = new ManualResetEventSlim(); - const int requiredEvents = 20; - var foundEventsCount = 0; - var subscription = _conn.FilteredSubscribeToAllFrom( - Position.Start, - filter, - CatchUpSubscriptionFilteredSettings.Default, - (s, e) => - { - foundEvents.Add(e); - if (Interlocked.Increment(ref foundEventsCount) >= requiredEvents) - { - appeared.Set(); - } - - return Task.CompletedTask; - }, - (s, p) => Task.CompletedTask, 5, - s => - { - _conn.AppendToStreamAsync("stream-a", ExpectedVersion.Any, _testEventsAfter.EvenEvents()).Wait(); - _conn.AppendToStreamAsync("stream-b", ExpectedVersion.Any, _testEventsAfter.OddEvents()).Wait(); - }); - try - { - if (!appeared.Wait(Timeout)) - { - Assert.Fail("Appeared countdown event timed out."); - } - - Assert.True(foundEvents.All(e => !e.Event.EventType.StartsWith("$"))); - } - finally - { - StopSubscription(subscription); - } - } - - private EventStoreCatchUpSubscription Subscribe(Filter filter, ConcurrentBag foundEvents, - CountdownEvent appeared) - { - return _conn.FilteredSubscribeToAllFrom( - Position.Start, - filter, - CatchUpSubscriptionFilteredSettings.Default, - (s, e) => - { - foundEvents.Add(e); - appeared.Signal(); - return Task.CompletedTask; - }, - (s, p) => Task.CompletedTask, 5, - s => - { - _conn.AppendToStreamAsync("stream-a", ExpectedVersion.Any, _testEventsAfter.EvenEvents()).Wait(); - _conn.AppendToStreamAsync("stream-b", ExpectedVersion.Any, _testEventsAfter.OddEvents()).Wait(); - }); - } - - [TearDown] - public override async Task TearDown() - { - try - { - await TestConnectionLifecycle.CloseConnectionAndWait(_conn, ConnectionCloseTimeout); - } - catch - { - TestConnectionLifecycle.TryCloseConnection(_conn); - } - - TestConnectionLifecycle.DisposeIfNeeded(_conn); - await _node.Shutdown(); - await base.TearDown(); - } - - private static void StopSubscription(EventStoreCatchUpSubscription subscription) - { - try - { - subscription.Stop(ConnectionCloseTimeout); - } - catch - { - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/catchup_subscription_handles_small_batch_sizes.cs b/src/EventStore.Core.Tests/ClientAPI/catchup_subscription_handles_small_batch_sizes.cs deleted file mode 100644 index f64aebf271..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/catchup_subscription_handles_small_batch_sizes.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Ignore("Very long running")] -[Category("LongRunning"), Category("ClientAPI")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class catchup_subscription_handles_small_batch_sizes : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - private string _streamName = "TestStream"; - private CatchUpSubscriptionSettings _settings; - private IEventStoreConnection _conn; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - _conn = BuildConnection(_node); - await _conn.ConnectAsync(); - //Create 80000 events - for (var i = 0; i < 80; i++) - { - await _conn.AppendToStreamAsync(_streamName, ExpectedVersion.Any, CreateThousandEvents()); - } - - _settings = new CatchUpSubscriptionSettings(100, 1, false, true, String.Empty); - } - - private EventData[] CreateThousandEvents() - { - var events = new List(); - for (var i = 0; i < 1000; i++) - { - events.Add(new EventData(Guid.NewGuid(), "testEvent", true, - Encoding.UTF8.GetBytes("{ \"Foo\":\"Bar\" }"), null)); - } - - return events.ToArray(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - _conn.Dispose(); - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test] - public void CatchupSubscriptionToAllHandlesManyEventsWithSmallBatchSize() - { - var mre = new ManualResetEvent(false); - _conn.SubscribeToAllFrom(null, _settings, (sub, evnt) => - { - if (evnt.OriginalEventNumber % 1000 == 0) - { - Console.WriteLine("Processed {0} events", evnt.OriginalEventNumber); - } - - return Task.CompletedTask; - }, (sub) => { mre.Set(); }, null, new UserCredentials("admin", "changeit")); - - if (!mre.WaitOne(TimeSpan.FromMinutes(10))) - { - Assert.Fail("Timed out waiting for test to complete"); - } - } - - [Test] - public void CatchupSubscriptionToStreamHandlesManyEventsWithSmallBatchSize() - { - var mre = new ManualResetEvent(false); - _conn.SubscribeToStreamFrom(_streamName, null, _settings, (sub, evnt) => - { - if (evnt.OriginalEventNumber % 1000 == 0) - { - Console.WriteLine("Processed {0} events", evnt.OriginalEventNumber); - } - - return Task.CompletedTask; - }, (sub) => { mre.Set(); }, null, new UserCredentials("admin", "changeit")); - - if (!mre.WaitOne(TimeSpan.FromMinutes(10))) - { - Assert.Fail("Timed out waiting for test to complete"); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/connect.cs b/src/EventStore.Core.Tests/ClientAPI/connect.cs deleted file mode 100644 index 588507fc92..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/connect.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using System.Net; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Internal; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string), TcpType.Normal)] -[TestFixture(typeof(LogFormat.V2), typeof(string), TcpType.Ssl)] -public class Connect : SpecificationWithDirectoryPerTestFixture -{ - private readonly TcpType _tcpType; - - public Connect(TcpType tcpType) - { - _tcpType = tcpType; - } - - //TODO GFY THESE NEED TO BE LOOKED AT IN LINUX - [Test, Category("Network")] - public async Task should_not_throw_exception_when_server_is_down() - { - var ip = IPAddress.Loopback; - int port = PortsHelper.GetAvailablePort(ip); - using var connection = TestConnection.Create(new IPEndPoint(ip, port), _tcpType); - await connection.ConnectAsync(); - } - - //TODO GFY THESE NEED TO BE LOOKED AT IN LINUX - [Test, Category("Network")] - public async Task should_throw_exception_when_trying_to_reopen_closed_connection() - { - ClientApiLoggerBridge.Default.Info("Starting '{0}' test...", - "should_throw_exception_when_trying_to_reopen_closed_connection"); - - var closed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var settings = ConnectionSettings.Create() - .EnableVerboseLogging() - .UseCustomLogger(ClientApiLoggerBridge.Default) - .LimitReconnectionsTo(0) - .WithConnectionTimeoutOf(TimeSpan.FromSeconds(10)) - .SetReconnectionDelayTo(TimeSpan.FromMilliseconds(0)) - .FailOnNoServerResponse(); - if (_tcpType == TcpType.Ssl) - { - settings.DisableServerCertificateValidation(); - } - else - { - settings.DisableTls(); - } - - var ip = IPAddress.Loopback; - int port = PortsHelper.GetAvailablePort(ip); - using var connection = EventStoreConnection.Create(settings, new IPEndPoint(ip, port).ToESTcpUri()); - connection.Closed += (s, e) => closed.TrySetResult(true); - - await connection.ConnectAsync(); - - await closed.Task.WithTimeout( - TimeSpan.FromSeconds(120)); // TCP connection timeout might be even 60 seconds - - await AssertEx.ThrowsAsync(() => connection.ConnectAsync().WithTimeout()); - } - - //TODO GFY THIS TEST TIMES OUT IN LINUX. - [Test, Category("Network")] - public async Task should_close_connection_after_configured_amount_of_failed_reconnections() - { - var closed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var settings = - ConnectionSettings.Create() - .EnableVerboseLogging() - .UseCustomLogger(ClientApiLoggerBridge.Default) - .LimitReconnectionsTo(1) - .WithConnectionTimeoutOf(TimeSpan.FromSeconds(10)) - .SetReconnectionDelayTo(TimeSpan.FromMilliseconds(0)) - .FailOnNoServerResponse(); - if (_tcpType == TcpType.Ssl) - { - settings.DisableServerCertificateValidation(); - } - else - { - settings.DisableTls(); - } - - var ip = IPAddress.Loopback; - int port = PortsHelper.GetAvailablePort(ip); - using var connection = EventStoreConnection.Create(settings, new IPEndPoint(ip, port).ToESTcpUri()); - connection.Closed += (s, e) => closed.TrySetResult(true); - connection.Connected += (s, e) => - Console.WriteLine("EventStoreConnection '{0}': connected to [{1}]...", - e.Connection.ConnectionName, e.RemoteEndPoint); - connection.Reconnecting += (s, e) => - Console.WriteLine("EventStoreConnection '{0}': reconnecting...", e.Connection.ConnectionName); - connection.Disconnected += (s, e) => - Console.WriteLine("EventStoreConnection '{0}': disconnected from [{1}]...", - e.Connection.ConnectionName, e.RemoteEndPoint); - connection.ErrorOccurred += (s, e) => Console.WriteLine("EventStoreConnection '{0}': error = {1}", - e.Connection.ConnectionName, e.Exception); - - await connection.ConnectAsync(); - - await closed.Task.WithTimeout( - TimeSpan.FromSeconds(120)); // TCP connection timeout might be even 60 seconds - - await AssertEx.ThrowsAsync(() => connection - .AppendToStreamAsync("stream", ExpectedVersion.NoStream, TestEvent.NewTestEvent()) - .WithTimeout()); - } -} - -[TestFixture, Category("ClientAPI"), Category("LongRunning")] -public class not_connected_tests -{ - private readonly TcpType _tcpType = TcpType.Ssl; - - [Test] - public async Task should_timeout_connection_after_configured_amount_time_on_conenct() - { - var closed = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var settings = - ConnectionSettings.Create() - .EnableVerboseLogging() - .UseCustomLogger(ClientApiLoggerBridge.Default) - .LimitReconnectionsTo(0) - .SetReconnectionDelayTo(TimeSpan.FromMilliseconds(0)) - .FailOnNoServerResponse() - .WithConnectionTimeoutOf(TimeSpan.FromMilliseconds(1000)); - - if (_tcpType == TcpType.Ssl) - { - settings.DisableServerCertificateValidation(); - } - else - { - settings.DisableTls(); - } - - var ip = new IPAddress(new byte[] - {8, 8, 8, 8}); //NOTE: This relies on Google DNS server being configured to swallow nonsense traffic - const int port = 4567; - using (var connection = EventStoreConnection.Create(settings, new IPEndPoint(ip, port).ToESTcpUri())) - { - connection.Closed += (s, e) => closed.TrySetResult(true); - connection.Connected += (s, e) => Console.WriteLine("EventStoreConnection '{0}': connected to [{1}]...", - e.Connection.ConnectionName, e.RemoteEndPoint); - connection.Reconnecting += (s, e) => - Console.WriteLine("EventStoreConnection '{0}': reconnecting...", e.Connection.ConnectionName); - connection.Disconnected += (s, e) => - Console.WriteLine("EventStoreConnection '{0}': disconnected from [{1}]...", - e.Connection.ConnectionName, e.RemoteEndPoint); - connection.ErrorOccurred += (s, e) => Console.WriteLine("EventStoreConnection '{0}': error = {1}", - e.Connection.ConnectionName, e.Exception); - await connection.ConnectAsync(); - - await closed.Task.WithTimeout(TimeSpan.FromSeconds(15)); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription.cs b/src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription.cs deleted file mode 100644 index a5edc279b4..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription.cs +++ /dev/null @@ -1,980 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.ClientOperations; -using EventStore.ClientAPI.Common; -using EventStore.ClientAPI.Common.Utils; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("LongRunning"), Category("ClientAPI")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_non_existing_persistent_subscription_with_permissions - : SpecificationWithMiniNode -{ - private Exception _caught; - - protected override Task When() - { - _caught = Assert.Throws( - () => - { - _conn.ConnectToPersistentSubscription( - "nonexisting2", - "foo", - (sub, e) => - { - Console.Write("appeared"); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - throw new Exception("should have thrown"); - }).InnerException; - return Task.CompletedTask; - } - - [Test] - public void the_completion_fails() - { - Assert.IsNotNull(_caught); - } - - [Test] - public void the_exception_is_an_argument_exception() - { - Assert.IsInstanceOf(_caught); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_existing_persistent_subscription_with_permissions : SpecificationWithMiniNode -{ - private EventStorePersistentSubscriptionBase _sub; - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "agroupname17", _settings, - DefaultData.AdminCredentials); - } - - protected override Task When() - { - _sub = _conn.ConnectToPersistentSubscription(_stream, - "agroupname17", - (sub, e) => - { - Console.Write("appeared"); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - - return Task.CompletedTask; - } - - [Test] - public void the_subscription_succeeds() - { - Assert.IsNotNull(_sub); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_existing_persistent_subscription_without_permissions : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() - { - return _conn.CreatePersistentSubscriptionAsync(_stream, "agroupname55", _settings, - DefaultData.AdminCredentials); - } - - [Test] - public void the_subscription_fails_to_connect() - { - try - { - _conn.ConnectToPersistentSubscription( - _stream, - "agroupname55", - (sub, e) => - { - Console.Write("appeared"); - return Task.CompletedTask; - }, - (sub, reason, ex) => Console.WriteLine("dropped.")); - throw new Exception("should have thrown."); - } - catch (Exception ex) - { - Assert.IsInstanceOf(ex); - Assert.IsInstanceOf(ex.InnerException); - } - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_existing_persistent_subscription_with_max_one_client : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent() - .WithMaxSubscriberCountOf(1); - - private Exception _exception; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await base.Given(); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - (s, e) => - { - s.Acknowledge(e); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - protected override Task When() - { - _exception = Assert.Throws(() => - { - _conn.ConnectToPersistentSubscription( - _stream, - _group, - (s, e) => - { - s.Acknowledge(e); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - throw new Exception("should have thrown."); - }).InnerException; - return Task.CompletedTask; - } - - [Test] - public void the_second_subscription_fails_to_connect() - { - Assert.IsInstanceOf(_exception); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_and_no_stream : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromBeginning(); - - private readonly Guid _id = Guid.NewGuid(); - private readonly TaskCompletionSource _firstEventSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - protected override Task When() - { - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _firstEventSource.TrySetResult(resolvedEvent); - return Task.CompletedTask; - } - - [Test] - public async Task the_subscription_gets_event_zero_as_its_first_event() - { - var firstEvent = await _firstEventSource.Task.WithTimeout(TimeSpan.FromSeconds(10)); - Assert.AreEqual(0, firstEvent.Event.EventNumber); - Assert.AreEqual(_id, firstEvent.Event.EventId); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_two_and_no_stream - : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFrom(2); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private readonly Guid _id = Guid.NewGuid(); - private readonly TaskCompletionSource _firstEventSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - protected override async Task When() - { - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])) -; - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])) -; - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _firstEventSource.TrySetResult(resolvedEvent); - return Task.CompletedTask; - } - - [Test] - public async Task the_subscription_gets_event_two_as_its_first_event() - { - var resolvedEvent = await _firstEventSource.Task.WithTimeout(TimeSpan.FromSeconds(10)); - Assert.AreEqual(2, resolvedEvent.Event.EventNumber); - Assert.AreEqual(_id, resolvedEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_and_events_in_it : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromBeginning(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private List _ids = new List(); - private bool _set = false; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - _ids.Add(Guid.NewGuid()); - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_ids[i], "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])) -; - } - } - - protected override Task When() - { - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - return Task.CompletedTask; - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - if (!_set) - { - _set = true; - _firstEvent = resolvedEvent; - _resetEvent.Set(); - } - - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_event_zero_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(0, _firstEvent.Event.EventNumber); - Assert.AreEqual(_ids[0], _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_not_set_and_events_in_it : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), - new byte[0])) -; - } - } - - protected override Task When() - { - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - return Task.CompletedTask; - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_no_events() - { - Assert.IsFalse(_resetEvent.WaitOne(TimeSpan.FromSeconds(1))); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_not_set_and_events_in_it_then_event_written : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private Guid _id; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), - new byte[0])) -; - } - } - - protected override async Task When() - { - _id = Guid.NewGuid(); - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _firstEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_the_written_event_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.IsNotNull(_firstEvent); - Assert.AreEqual(10, _firstEvent.Event.EventNumber); - Assert.AreEqual(_id, _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_x_set_higher_than_x_and_events_in_it_then_event_written : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFrom(11); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private Guid _id; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 11; i++) - { - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), - new byte[0])) -; - } - } - - protected override Task When() - { - _id = Guid.NewGuid(); - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _firstEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_the_written_event_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.IsNotNull(_firstEvent); - Assert.AreEqual(11, _firstEvent.Event.EventNumber); - Assert.AreEqual(_id, _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class a_nak_in_subscription_handler_in_autoack_mode_drops_the_subscription : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromBeginning(); - - private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false); - private Exception _exception; - private SubscriptionDropReason _reason; - - private const string _group = "naktest"; - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - Dropped, - DefaultData.AdminCredentials); - } - - private void Dropped(EventStorePersistentSubscriptionBase sub, SubscriptionDropReason reason, - Exception exception) - { - _exception = exception; - _reason = reason; - _resetEvent.Set(); - } - - protected override Task When() - { - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private static Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - throw new Exception("test"); - } - - [Test] - [Retry(5)] - public void the_subscription_gets_dropped() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(5))); - Assert.AreEqual(SubscriptionDropReason.EventHandlerException, _reason); - Assert.AreEqual(typeof(Exception), _exception.GetType()); - Assert.AreEqual("test", _exception.Message); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_x_set_and_events_in_it_then_event_written : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFrom(10); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private Guid _id; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), - new byte[0])) -; - } - } - - protected override Task When() - { - _id = Guid.NewGuid(); - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _firstEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_the_written_event_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.IsNotNull(_firstEvent); - Assert.AreEqual(10, _firstEvent.Event.EventNumber); - Assert.AreEqual(_id, _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_x_set_and_events_in_it - : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFrom(4); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private Guid _id; - - private const string _group = "startinx2"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - var id = Guid.NewGuid(); - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - if (i == 4) - { - _id = id; - } - } - } - - protected override Task When() - { - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private bool _set = false; - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - if (_set) - { - return Task.CompletedTask; - } - - _set = true; - _firstEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_the_written_event_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.IsNotNull(_firstEvent); - Assert.AreEqual(4, _firstEvent.Event.EventNumber); - Assert.AreEqual(_id, _firstEvent.Event.EventId); - } -} - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[Category("LongRunning")] -public class - connect_to_persistent_subscription_with_link_to_event_with_event_number_greater_than_int_maxvalue : - ExpectedVersion64Bit.MiniNodeWithExistingRecords -{ - private const string StreamName = - "connect_to_persistent_subscription_with_link_to_event_with_event_number_greater_than_int_maxvalue"; - - private const long intMaxValue = (long)int.MaxValue; - - private string _linkedStreamName = "linked-" + StreamName; - private const string _group = "group-" + StreamName; - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .ResolveLinkTos() - .StartFromBeginning(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private bool _set = false; - private Guid _event1Id; - - public override async ValueTask WriteTestScenario(CancellationToken token) - { - var event1 = await WriteSingleEvent(StreamName, intMaxValue + 1, new string('.', 3000), token: token); - await WriteSingleEvent(StreamName, intMaxValue + 2, new string('.', 3000), token: token); - _event1Id = event1.EventId; - } - - public override async Task Given() - { - _store = BuildConnection(Node); - await _store.ConnectAsync(); - - await _store.CreatePersistentSubscriptionAsync(_linkedStreamName, _group, _settings, - DefaultData.AdminCredentials); - _store.ConnectToPersistentSubscription( - _linkedStreamName, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - await _store.AppendToStreamAsync(_linkedStreamName, ExpectedVersion.Any, new EventData(Guid.NewGuid(), - SystemEventTypes.LinkTo, false, Helper.UTF8NoBom.GetBytes( - string.Format("{0}@{1}", intMaxValue + 1, StreamName)), null)); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - if (!_set) - { - _set = true; - _firstEvent = resolvedEvent; - _resetEvent.Set(); - } - - return Task.CompletedTask; - } - - [Test] - public void the_subscription_resolves_the_linked_event_correctly() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(intMaxValue + 1, _firstEvent.Event.EventNumber); - Assert.AreEqual(_event1Id, _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_persistent_subscription_with_retries : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString("N"); - protected override TimeSpan Timeout { get; } = TimeSpan.FromMinutes(5); - protected override TimeSpan StartupTimeout { get; } = TimeSpan.FromMinutes(10); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromBeginning(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private readonly Guid _id = Guid.NewGuid(); - int? _retryCount; - private const string _group = "retries"; - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials, autoAck: false); - } - - protected override Task When() - { - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent, - int? retryCount) - { - if (retryCount > 4) - { - _retryCount = retryCount; - sub.Acknowledge(resolvedEvent); - _resetEvent.Set(); - } - else - { - sub.Fail(resolvedEvent, PersistentSubscriptionNakEventAction.Retry, "Not yet tried enough times"); - } - - return Task.CompletedTask; - } - - [Test] - public void events_are_retried_until_success() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(5, _retryCount); - } -} - -//ALL - -/* - - [TestFixture, Category("LongRunning")] - public class connect_to_non_existing_persistent_all_subscription_with_permissions : SpecificationWithMiniNode - { - private Exception _caught; - - protected override void When() - { - try - { - _conn.ConnectToPersistentSubscriptionForAll("nonexisting2", - (sub, e) => Console.Write("appeared"), - (sub, reason, ex) => - { - }, - DefaultData.AdminCredentials); - throw new Exception("should have thrown"); - } - catch (Exception ex) - { - _caught = ex; - } - } - - [Test] - public void the_completion_fails() - { - Assert.IsNotNull(_caught); - } - - [Test] - public void the_exception_is_an_argument_exception() - { - Assert.IsInstanceOf(_caught.InnerException); - } - } - - [TestFixture, Category("LongRunning")] - public class connect_to_existing_persistent_all_subscription_with_permissions : SpecificationWithMiniNode - { - private EventStorePersistentSubscription _sub; - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettingsBuilder.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - protected override void When() - { - _conn.CreatePersistentSubscriptionForAllAsync("agroupname17", _settings, DefaultData.AdminCredentials).Wait(); - _sub = _conn.ConnectToPersistentSubscriptionForAll("agroupname17", - (sub, e) => Console.Write("appeared"), - (sub, reason, ex) => { }, DefaultData.AdminCredentials); - } - - [Test] - public void the_subscription_suceeds() - { - Assert.IsNotNull(_sub); - } - } - - [TestFixture, Category("LongRunning")] - public class connect_to_existing_persistent_all_subscription_without_permissions : SpecificationWithMiniNode - { - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettingsBuilder.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override void When() - { - _conn.CreatePersistentSubscriptionForAllAsync("agroupname55", _settings, - DefaultData.AdminCredentials).Wait(); - } - - [Test] - public void the_subscription_fails_to_connect() - { - try - { - _conn.ConnectToPersistentSubscriptionForAll("agroupname55", - (sub, e) => Console.Write("appeared"), - (sub, reason, ex) => { }); - throw new Exception("should have thrown."); - } - catch (Exception ex) - { - Assert.IsInstanceOf(ex); - Assert.IsInstanceOf(ex.InnerException); - } - } - } -*/ diff --git a/src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription_async.cs b/src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription_async.cs deleted file mode 100644 index f87a979b4a..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/connecting_to_a_persistent_subscription_async.cs +++ /dev/null @@ -1,412 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.ClientOperations; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("LongRunning"), Category("ClientAPI")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_non_existing_persistent_subscription_with_permissions_async : SpecificationWithMiniNode -{ - private Exception _innerEx; - - protected override async Task When() - { - _innerEx = await AssertEx.ThrowsAsync(() => _conn.ConnectToPersistentSubscriptionAsync( - "nonexisting2", - "foo", - (sub, e) => - { - Console.Write("appeared"); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, DefaultData.AdminCredentials)); - } - - [Test] - public void the_subscription_fails_to_connect_with_argument_exception() - { - Assert.IsInstanceOf(_innerEx); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_existing_persistent_subscription_with_permissions_async : SpecificationWithMiniNode -{ - private EventStorePersistentSubscriptionBase _sub; - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override async Task When() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "agroupname17", _settings, DefaultData.AdminCredentials) -; - _sub = await _conn.ConnectToPersistentSubscriptionAsync(_stream, - "agroupname17", - (sub, e) => - { - Console.Write("appeared"); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, DefaultData.AdminCredentials); - } - - [Test] - public void the_subscription_succeeds() - { - Assert.IsNotNull(_sub); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_existing_persistent_subscription_without_permissions_async : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - private Exception _innerEx; - - protected override async Task When() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "agroupname55", _settings, - DefaultData.AdminCredentials); - _innerEx = await AssertEx.ThrowsAsync(() => _conn.ConnectToPersistentSubscriptionAsync( - _stream, - "agroupname55", - (sub, e) => - { - Console.Write("appeared"); - return Task.CompletedTask; - }, - (sub, reason, ex) => Console.WriteLine("dropped."))); - } - - [Test] - public void the_subscription_fails_to_connect_with_access_denied_exception() - { - Assert.IsInstanceOf(_innerEx); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class connect_to_existing_persistent_subscription_with_max_one_client_async : SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent() - .WithMaxSubscriberCountOf(1); - - private Exception _innerEx; - - private const string _group = "startinbeginning1"; - private EventStorePersistentSubscriptionBase _firstConn; - - protected override async Task Given() - { - await base.Given(); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - // First connection - _firstConn = await _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - (s, e) => - { - s.Acknowledge(e); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - protected override async Task When() - { - _innerEx = await AssertEx.ThrowsAsync(() => - // Second connection - _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - (s, e) => - { - s.Acknowledge(e); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials)); - } - - [Test] - public void the_first_subscription_connects_successfully() - { - Assert.IsNotNull(_firstConn); - } - - [Test] - public void the_second_subscription_throws_maximum_subscribers_reached_exception() - { - Assert.IsInstanceOf(_innerEx); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_and_no_stream_async : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromBeginning(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private readonly Guid _id = Guid.NewGuid(); - private bool _set = false; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - - await _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - protected override Task When() - { - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - if (_set) - { - return Task.CompletedTask; - } - - _set = true; - _firstEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_event_zero_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(0, _firstEvent.Event.EventNumber); - Assert.AreEqual(_id, _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_two_and_no_stream_async : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFrom(2); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private readonly Guid _id = Guid.NewGuid(); - private bool _set = false; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - protected override async Task When() - { - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_id, "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - if (_set) - { - return Task.CompletedTask; - } - - _set = true; - _firstEvent = resolvedEvent; - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_event_two_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(2, _firstEvent.Event.EventNumber); - Assert.AreEqual(_id, _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_and_events_in_it_async : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromBeginning(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - private ResolvedEvent _firstEvent; - private List _ids = new List(); - private bool _set = false; - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - _ids.Add(Guid.NewGuid()); - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(_ids[i], "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), new byte[0])); - } - } - - protected override Task When() - { - return _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - if (!_set) - { - _set = true; - _firstEvent = resolvedEvent; - _resetEvent.Set(); - } - - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_event_zero_as_its_first_event() - { - Assert.IsTrue(_resetEvent.WaitOne(TimeSpan.FromSeconds(10))); - Assert.AreEqual(0, _firstEvent.Event.EventNumber); - Assert.AreEqual(_ids[0], _firstEvent.Event.EventId); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - connect_to_existing_persistent_subscription_with_start_from_beginning_not_set_and_events_in_it_async : - SpecificationWithMiniNode -{ - private readonly string _stream = "$" + Guid.NewGuid(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - private readonly AutoResetEvent _resetEvent = new AutoResetEvent(false); - - private const string _group = "startinbeginning1"; - - protected override async Task Given() - { - await WriteEvents(_conn); - await _conn.CreatePersistentSubscriptionAsync(_stream, _group, _settings, - DefaultData.AdminCredentials); - } - - private async Task WriteEvents(IEventStoreConnection connection) - { - for (int i = 0; i < 10; i++) - { - await connection.AppendToStreamAsync(_stream, ExpectedVersion.Any, DefaultData.AdminCredentials, - new EventData(Guid.NewGuid(), "test", true, Encoding.UTF8.GetBytes("{'foo' : 'bar'}"), - new byte[0])); - } - } - - protected override Task When() - { - return _conn.ConnectToPersistentSubscriptionAsync( - _stream, - _group, - HandleEvent, - (sub, reason, ex) => { }, - DefaultData.AdminCredentials); - } - - private Task HandleEvent(EventStorePersistentSubscriptionBase sub, ResolvedEvent resolvedEvent) - { - _resetEvent.Set(); - return Task.CompletedTask; - } - - [Test] - public void the_subscription_gets_no_events() - { - Assert.IsFalse(_resetEvent.WaitOne(TimeSpan.FromSeconds(1))); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/connecting_with_connection_string.cs b/src/EventStore.Core.Tests/ClientAPI/connecting_with_connection_string.cs deleted file mode 100644 index 362032a25d..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/connecting_with_connection_string.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_connecting_with_connection_string : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task should_not_throw_when_connect_to_is_set() - { - string connectionString = string.Format("ConnectTo=tcp://{0};", _node.TcpEndPoint); - using (var connection = EventStoreConnection.Create(connectionString)) - { - await connection.ConnectAsync(); - connection.Close(); - } - } - - [Test] - public void should_not_throw_when_only_gossip_seeds_is_set() - { - string connectionString = string.Format("GossipSeeds={0};", _node.HttpEndPoint); - IEventStoreConnection connection = null; - - Assert.DoesNotThrow(() => connection = EventStoreConnection.Create(connectionString)); - Assert.AreEqual(_node.HttpEndPoint, connection.Settings.GossipSeeds.First().EndPoint); - - connection.Dispose(); - } - - [Test] - public void should_throw_when_gossip_seeds_and_connect_to_is_set() - { - string connectionString = string.Format("ConnectTo=tcp://{0};GossipSeeds={1}", _node.TcpEndPoint, - _node.HttpEndPoint); - Assert.Throws(() => EventStoreConnection.Create(connectionString)); - } - - [Test] - public void should_throw_when_neither_gossip_seeds_nor_connect_to_is_set() - { - string connectionString = string.Format("HeartBeatTimeout=2000"); - Assert.Throws(() => EventStoreConnection.Create(connectionString)); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/connection_string.cs b/src/EventStore.Core.Tests/ClientAPI/connection_string.cs deleted file mode 100644 index f4d8749998..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/connection_string.cs +++ /dev/null @@ -1,69 +0,0 @@ -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[TestFixture, Category("ClientAPI")] -public class connection_string -{ - [Test] - public void can_set_bool_value_with_string() - { - var settings = ConnectionString.GetConnectionSettings("verboselogging=true"); - Assert.AreEqual(true, settings.VerboseLogging); - } - - [Test] - public void can_set_with_spaces() - { - var settings = ConnectionString.GetConnectionSettings("Verbose Logging=true"); - Assert.AreEqual(true, settings.VerboseLogging); - } - - - [Test] - public void can_set_int() - { - var settings = ConnectionString.GetConnectionSettings("maxretries=55"); - Assert.AreEqual(55, settings.MaxRetries); - } - - [Test] - public void can_set_timespan() - { - var settings = ConnectionString.GetConnectionSettings("heartbeattimeout=5555"); - Assert.AreEqual(5555, settings.HeartbeatTimeout.TotalMilliseconds); - } - - [Test] - public void can_set_multiple_values() - { - var settings = ConnectionString.GetConnectionSettings("heartbeattimeout=5555;maxretries=55"); - Assert.AreEqual(5555, settings.HeartbeatTimeout.TotalMilliseconds); - Assert.AreEqual(55, settings.MaxRetries); - } - - [Test] - public void can_set_mixed_case() - { - var settings = ConnectionString.GetConnectionSettings("heArtbeAtTimeout=5555"); - Assert.AreEqual(5555, settings.HeartbeatTimeout.TotalMilliseconds); - } - - [Test] - public void can_set_gossip_seeds() - { - var settings = - ConnectionString.GetConnectionSettings( - "gossipseeds=111.222.222.111:1111,111.222.222.111:1112,111.222.222.111:1113"); - Assert.AreEqual(3, settings.GossipSeeds.Length); - } - - [Test] - public void can_set_default_user_credentials() - { - var settings = ConnectionString.GetConnectionSettings("DefaultUserCredentials=foo:bar"); - Assert.AreEqual("foo", settings.DefaultUserCredentials.Username); - Assert.AreEqual("bar", settings.DefaultUserCredentials.Password); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/create_persistent_subscription.cs b/src/EventStore.Core.Tests/ClientAPI/create_persistent_subscription.cs deleted file mode 100644 index 6be3bd0be7..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/create_persistent_subscription.cs +++ /dev/null @@ -1,305 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_on_existing_stream : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() - { - return _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "whatever", true, Encoding.UTF8.GetBytes("{'foo' : 2}"), new Byte[0])); - } - - [Test] - public async Task the_completion_succeeds() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "existing", _settings, - DefaultData.AdminCredentials); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_on_non_existing_stream : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_completion_succeeds() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "nonexistinggroup", _settings, - DefaultData.AdminCredentials); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_on_all_stream : SpecificationWithMiniNode -{ - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_completion_fails_with_invalid_stream() - { - await AssertEx.ThrowsAsync(() => - _conn.CreatePersistentSubscriptionAsync("$all", "shitbird", _settings, DefaultData.AdminCredentials)); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_with_too_big_message_timeout : SpecificationWithMiniNode -{ - protected override Task When() => Task.CompletedTask; - - [Test] - public void the_build_fails_with_argument_exception() - { - Assert.Throws(() => - PersistentSubscriptionSettings.Create().WithMessageTimeoutOf(TimeSpan.FromDays(25 * 365)).Build()); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_with_too_big_checkpoint_after : SpecificationWithMiniNode -{ - protected override Task When() => Task.CompletedTask; - - [Test] - public void the_build_fails_with_argument_exception() - { - Assert.Throws(() => - PersistentSubscriptionSettings.Create().CheckPointAfter(TimeSpan.FromDays(25 * 365)).Build()); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_with_dont_timeout : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent() - .DontTimeoutMessages(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public void the_message_timeout_should_be_zero() - { - Assert.That(_settings.MessageTimeout == TimeSpan.Zero); - } - - [Test] - public async Task the_subscription_is_created_without_error() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "dont-timeout", _settings, - DefaultData.AdminCredentials); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_duplicate_persistent_subscription_group : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() - { - return _conn.CreatePersistentSubscriptionAsync(_stream, "group32", _settings, DefaultData.AdminCredentials); - } - - [Test] - public async Task the_completion_fails_with_invalid_operation_exception() - { - await AssertEx.ThrowsAsync( - () => _conn.CreatePersistentSubscriptionAsync(_stream, "group32", _settings, - DefaultData.AdminCredentials)); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - can_create_duplicate_persistent_subscription_group_name_on_different_streams - : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() - { - return _conn.CreatePersistentSubscriptionAsync(_stream, "group3211", _settings, DefaultData.AdminCredentials); - } - - [Test] - public async Task the_completion_succeeds() - { - await - _conn.CreatePersistentSubscriptionAsync("someother" + _stream, "group3211", _settings, - DefaultData.AdminCredentials); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_group_without_permissions : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_completion_succeeds() - { - await AssertEx.ThrowsAsync(() => - _conn.CreatePersistentSubscriptionAsync(_stream, "group57", _settings, null)); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class create_persistent_subscription_after_deleting_the_same : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override async Task When() - { - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "whatever", true, Encoding.UTF8.GetBytes("{'foo' : 2}"), new Byte[0])); - await _conn.CreatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials); - await _conn.DeletePersistentSubscriptionAsync(_stream, "existing", DefaultData.AdminCredentials); - } - - [Test] - public async Task the_completion_succeeds() - { - await _conn.CreatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials); - } -} - -//ALL -/* - - [TestFixture, Category("LongRunning")] - public class create_persistent_subscription_on_all : SpecificationWithMiniNode - { - private PersistentSubscriptionCreateResult _result; - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettingsBuilder.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override void When() - { - _result = _conn.CreatePersistentSubscriptionForAllAsync("group", _settings, DefaultData.AdminCredentials).Result; - } - - [Test] - public void the_completion_succeeds() - { - Assert.AreEqual(PersistentSubscriptionCreateStatus.Success, _result.Status); - } - } - - - [TestFixture, Category("LongRunning")] - public class create_duplicate_persistent_subscription_group_on_all : SpecificationWithMiniNode - { - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettingsBuilder.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - protected override void When() - { - _conn.CreatePersistentSubscriptionForAllAsync("group32", _settings, DefaultData.AdminCredentials).Wait(); - } - - [Test] - public void the_completion_fails_with_invalid_operation_exception() - { - try - { - _conn.CreatePersistentSubscriptionForAllAsync("group32", _settings, DefaultData.AdminCredentials).Wait(); - throw new Exception("expected exception"); - } - catch (Exception ex) - { - Assert.IsInstanceOf(typeof(AggregateException), ex); - var inner = ex.InnerException; - Assert.IsInstanceOf(typeof(InvalidOperationException), inner); - } - } - } - - [TestFixture, Category("LongRunning")] - public class create_persistent_subscription_group_on_all_without_permissions : SpecificationWithMiniNode - { - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettingsBuilder.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - protected override void When() - { - } - - [Test] - public void the_completion_succeeds() - { - try - { - _conn.CreatePersistentSubscriptionForAllAsync("group57", _settings, null).Wait(); - throw new Exception("expected exception"); - } - catch (Exception ex) - { - Assert.IsInstanceOf(typeof(AggregateException), ex); - var inner = ex.InnerException; - Assert.IsInstanceOf(typeof(AccessDeniedException), inner); - } - } - } -*/ diff --git a/src/EventStore.Core.Tests/ClientAPI/deleting_persistent_subscription.cs b/src/EventStore.Core.Tests/ClientAPI/deleting_persistent_subscription.cs deleted file mode 100644 index 641589ed1a..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/deleting_persistent_subscription.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class deleting_existing_persistent_subscription_group_with_permissions - : SpecificationWithMiniNode -{ - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - private readonly string _stream = Guid.NewGuid().ToString(); - - protected override Task When() => - _conn.CreatePersistentSubscriptionAsync(_stream, "groupname123", _settings, - DefaultData.AdminCredentials); - - [Test] - public async Task the_delete_of_group_succeeds() - { - await _conn.DeletePersistentSubscriptionAsync(_stream, "groupname123", DefaultData.AdminCredentials); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class deleting_existing_persistent_subscription_with_subscriber : SpecificationWithMiniNode -{ - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - private readonly string _stream = Guid.NewGuid().ToString(); - private readonly ManualResetEvent _called = new ManualResetEvent(false); - - protected override async Task Given() - { - await base.Given(); - await _conn.CreatePersistentSubscriptionAsync(_stream, "groupname123", _settings, - DefaultData.AdminCredentials); - _conn.ConnectToPersistentSubscription(_stream, "groupname123", - (s, e) => Task.CompletedTask, - (s, r, e) => _called.Set(), DefaultData.AdminCredentials); - } - - protected override Task When() - { - return _conn.DeletePersistentSubscriptionAsync(_stream, "groupname123", DefaultData.AdminCredentials); - } - - [Test] - public void the_subscription_is_dropped() - { - Assert.IsTrue(_called.WaitOne(TimeSpan.FromSeconds(5))); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class deleting_persistent_subscription_group_that_doesnt_exist : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_delete_fails_with_argument_exception() - { - await AssertEx.ThrowsAsync( - () => - _conn.DeletePersistentSubscriptionAsync(_stream, Guid.NewGuid().ToString(), - DefaultData.AdminCredentials)); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class deleting_persistent_subscription_group_without_permissions : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_delete_fails_with_access_denied() - { - await AssertEx.ThrowsAsync( - () => _conn.DeletePersistentSubscriptionAsync(_stream, Guid.NewGuid().ToString())); - } -} - -//ALL -/* - - [TestFixture, Category("LongRunning")] - public class deleting_existing_persistent_subscription_group_on_all_with_permissions : SpecificationWithMiniNode - { - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettingsBuilder.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - protected override void When() - { - _conn.CreatePersistentSubscriptionForAllAsync("groupname123", _settings, - DefaultData.AdminCredentials).Wait(); - } - - [Test] - public void the_delete_of_group_succeeds() - { - var result = _conn.DeletePersistentSubscriptionForAllAsync("groupname123", DefaultData.AdminCredentials).Result; - Assert.AreEqual(PersistentSubscriptionDeleteStatus.Success, result.Status); - } - } - - [TestFixture, Category("LongRunning")] - public class deleting_persistent_subscription_group_on_all_that_doesnt_exist : SpecificationWithMiniNode - { - protected override void When() - { - } - - [Test] - public void the_delete_fails_with_argument_exception() - { - try - { - _conn.DeletePersistentSubscriptionForAllAsync(Guid.NewGuid().ToString(), DefaultData.AdminCredentials).Wait(); - throw new Exception("expected exception"); - } - catch (Exception ex) - { - Assert.IsInstanceOf(typeof(AggregateException), ex); - var inner = ex.InnerException; - Assert.IsInstanceOf(typeof(InvalidOperationException), inner); - } - } - } - - - [TestFixture, Category("LongRunning")] - public class deleting_persistent_subscription_group_on_all_without_permissions : SpecificationWithMiniNode - { - protected override void When() - { - } - - [Test] - public void the_delete_fails_with_access_denied() - { - try - { - _conn.DeletePersistentSubscriptionForAllAsync(Guid.NewGuid().ToString()).Wait(); - throw new Exception("expected exception"); - } - catch (Exception ex) - { - Assert.IsInstanceOf(typeof(AggregateException), ex); - var inner = ex.InnerException; - Assert.IsInstanceOf(typeof(AccessDeniedException), inner); - } - } - } -*/ diff --git a/src/EventStore.Core.Tests/ClientAPI/deleting_stream.cs b/src/EventStore.Core.Tests/ClientAPI/deleting_stream.cs deleted file mode 100644 index 318c754e50..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/deleting_stream.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class deleting_stream : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - virtual protected IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test] - [Category("Network")] - public async Task which_doesnt_exists_should_success_when_passed_empty_stream_expected_version() - { - const string stream = "which_already_exists_should_success_when_passed_empty_stream_expected_version"; - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - await connection.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - } - } - - [Test] - [Category("Network")] - public async Task which_doesnt_exists_should_success_when_passed_any_for_expected_version() - { - const string stream = "which_already_exists_should_success_when_passed_any_for_expected_version"; - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - - await connection.DeleteStreamAsync(stream, ExpectedVersion.Any, hardDelete: true); - } - } - - [Test] - [Category("Network")] - public async Task with_invalid_expected_version_should_fail() - { - const string stream = "with_invalid_expected_version_should_fail"; - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - - await AssertEx.ThrowsAsync(() => - connection.DeleteStreamAsync(stream, 1, hardDelete: true)); - } - } - - public async Task should_return_log_position_when_writing() - { - const string stream = "delete_should_return_log_position_when_writing"; - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - - var result = await connection - .AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - var delete = await connection.DeleteStreamAsync(stream, 1, hardDelete: true); - - Assert.IsTrue(0 < result.LogPosition.PreparePosition); - Assert.IsTrue(0 < result.LogPosition.CommitPosition); - } - } - - [Test] - [Category("Network")] - public async Task which_was_already_deleted_should_fail() - { - const string stream = "which_was_allready_deleted_should_fail"; - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - - await connection.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - await AssertEx.ThrowsAsync( - () => connection.DeleteStreamAsync(stream, ExpectedVersion.Any, hardDelete: true)); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/event_store_connection_should.cs b/src/EventStore.Core.Tests/ClientAPI/event_store_connection_should.cs deleted file mode 100644 index 81fefbd37b..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/event_store_connection_should.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string), TcpType.Normal)] -[TestFixture(typeof(LogFormat.V2), typeof(string), TcpType.Ssl)] -public class event_store_connection_should : SpecificationWithDirectoryPerTestFixture -{ - private readonly TcpType _tcpType; - private MiniNode _node; - - public event_store_connection_should(TcpType tcpType) - { - _tcpType = tcpType; - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - [Category("Network")] - public void not_throw_on_close_if_connect_was_not_called() - { - var connection = TestConnection.To(_node, _tcpType); - Assert.DoesNotThrow(connection.Close); - } - - [Test] - [Category("Network")] - public async Task not_throw_on_close_if_called_multiple_times() - { - var connection = TestConnection.To(_node, _tcpType); - await connection.ConnectAsync(); - connection.Close(); - Assert.DoesNotThrow(connection.Close); - } - - /* - //TODO WEIRD TEST GFY - [Test] - [Category("Network")] - public void throw_on_connect_called_more_than_once() - { - var connection = TestConnection.To(_node, _tcpType); - Assert.DoesNotThrow(() => connection.ConnectAsync().Wait()); - - await AssertEx.ThrowsAsync<>(() => connection.ConnectAsync().Wait(), - Throws.Exception.InstanceOf().With.InnerException.InstanceOf()); - } - - [Test] - [Category("Network")] - public void throw_on_connect_called_after_close() - { - var connection = TestConnection.To(_node, _tcpType); - connection.ConnectAsync().Wait(); - connection.Close(); - - await AssertEx.ThrowsAsync<>(() => connection.ConnectAsync().Wait(), - Throws.Exception.InstanceOf().With.InnerException.InstanceOf()); - } - */ - [Test] - [Category("Network")] - public async Task throw_invalid_operation_on_every_api_call_if_connect_was_not_called() - { - var connection = TestConnection.To(_node, _tcpType); - - const string s = "stream"; - var events = new[] { TestEvent.NewTestEvent() }; - - await AssertEx.ThrowsAsync(() => connection.DeleteStreamAsync(s, 0)); - - await AssertEx.ThrowsAsync(() => connection.AppendToStreamAsync(s, 0, events)); - - await AssertEx.ThrowsAsync( - () => connection.ReadStreamEventsForwardAsync(s, 0, 1, resolveLinkTos: false)); - - await AssertEx.ThrowsAsync( - () => connection.ReadStreamEventsBackwardAsync(s, 0, 1, resolveLinkTos: false)); - - await AssertEx.ThrowsAsync( - () => connection.ReadAllEventsForwardAsync(Position.Start, 1, false)); - - await AssertEx.ThrowsAsync(() => - connection.ReadAllEventsBackwardAsync(Position.End, 1, false)); - - await AssertEx.ThrowsAsync(() => connection.StartTransactionAsync(s, 0)); - - await AssertEx.ThrowsAsync( - () => connection.SubscribeToStreamAsync(s, false, (_, __) => Task.CompletedTask, (_, __, ___) => { })); - - await AssertEx.ThrowsAsync( - () => connection.SubscribeToAllAsync(false, (_, __) => Task.CompletedTask, (_, __, ___) => { })); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/isjson_flag_on_event.cs b/src/EventStore.Core.Tests/ClientAPI/isjson_flag_on_event.cs deleted file mode 100644 index ea916aa847..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/isjson_flag_on_event.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Common.Utils; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using EventStore.Core.TransactionLog.LogRecords; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class isjson_flag_on_event : SpecificationWithDirectory -{ - private MiniNode _node; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [TearDown] - public override async Task TearDown() - { - await _node.Shutdown(); - await base.TearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, TcpType.Ssl); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task should_be_preserved_with_all_possible_write_and_read_methods() - { - const string stream = "should_be_preserved_with_all_possible_write_methods"; - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - - await connection.AppendToStreamAsync( - stream, - ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "some-type", true, - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}"), null), - new EventData(Guid.NewGuid(), "some-type", true, null, - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}")), - new EventData(Guid.NewGuid(), "some-type", true, - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}"), - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}"))); - var expectedEvents = 3; - - if (LogFormatHelper.SupportsExplicitTransactions) - { - using (var transaction = await connection.StartTransactionAsync(stream, ExpectedVersion.Any)) - { - await transaction.WriteAsync( - new EventData(Guid.NewGuid(), "some-type", true, - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}"), null), - new EventData(Guid.NewGuid(), "some-type", true, null, - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}")), - new EventData(Guid.NewGuid(), "some-type", true, - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}"), - Helper.UTF8NoBom.GetBytes("{\"some\":\"json\"}"))); - await transaction.CommitAsync(); - expectedEvents += 3; - } - } - - var done = new ManualResetEventSlim(); - _node.Node.MainQueue.Publish(new ClientMessage.ReadStreamEventsForward( - Guid.NewGuid(), Guid.NewGuid(), new CallbackEnvelope(message => - { - Assert.IsInstanceOf(message); - var msg = (ClientMessage.ReadStreamEventsForwardCompleted)message; - Assert.AreEqual(Data.ReadStreamResult.Success, msg.Result); - Assert.AreEqual(expectedEvents, msg.Events.Count); - Assert.IsTrue(msg.Events.All(x => (x.OriginalEvent.Flags & PrepareFlags.IsJson) != 0)); - - done.Set(); - }), stream, 0, 100, false, false, null, null, replyOnExpired: false)); - Assert.IsTrue(done.Wait(10000), "Read was not completed in time."); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/persistent_connect_integration_tests.cs b/src/EventStore.Core.Tests/ClientAPI/persistent_connect_integration_tests.cs deleted file mode 100644 index 9479e4c9c0..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/persistent_connect_integration_tests.cs +++ /dev/null @@ -1,487 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class happy_case_writing_and_subscribing_to_normal_events_manual_ack - : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromCurrent() - .ResolveLinkTos() - .Build(); - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - subscription.Acknowledge(resolvedEvent); - - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}).", reason, exception), - bufferSize: 10, autoAck: false, userCredentials: DefaultData.AdminCredentials); - - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - } -} - - -[Category("LongRunning")] -[NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class happy_case_writing_and_subscribing_to_normal_events_auto_ack : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromCurrent() - .ResolveLinkTos() - .Build(); - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials) -; - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}).", reason, exception), - userCredentials: DefaultData.AdminCredentials); - - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(15))) - { - throw new Exception($"Timed out waiting for events, received {_eventReceivedCount} event(s)."); - } - } -} - - -[Category("LongRunning")] -[NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class happy_case_catching_up_to_normal_events_auto_ack : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromBeginning() - .ResolveLinkTos() - .Build(); - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - { - Console.WriteLine($"Subscription dropped (reason:{reason}, exception:{exception})."); - }, - userCredentials: DefaultData.AdminCredentials, - autoAck: true); - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - } -} - - -[Category("LongRunning")] -[NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class happy_case_catching_up_to_normal_events_manual_ack : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromBeginning() - .ResolveLinkTos() - .Build(); - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - subscription.Acknowledge(resolvedEvent); - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}).", reason, exception), - userCredentials: DefaultData.AdminCredentials, - autoAck: false); - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - } -} - - -[Category("LongRunning")] -[NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class happy_case_catching_up_to_link_to_events_manual_ack : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromBeginning() - .ResolveLinkTos() - .Build(); - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - await _conn.AppendToStreamAsync(streamName + "original", ExpectedVersion.Any, DefaultData.AdminCredentials, - eventData); - } - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - subscription.Acknowledge(resolvedEvent); - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}).", reason, exception), - userCredentials: DefaultData.AdminCredentials, - autoAck: false); - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), SystemEventTypes.LinkTo, false, - Encoding.UTF8.GetBytes(i + "@" + streamName + "original"), null); - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - } -} - - -[Category("LongRunning")] -[NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class happy_case_catching_up_to_link_to_events_auto_ack : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromBeginning() - .ResolveLinkTos() - .Build(); - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - await _conn.AppendToStreamAsync(streamName + "original", ExpectedVersion.Any, DefaultData.AdminCredentials, - eventData); - } - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}).", reason, exception), - userCredentials: DefaultData.AdminCredentials, - autoAck: true); - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), SystemEventTypes.LinkTo, false, - Encoding.UTF8.GetBytes(i + "@" + streamName + "original"), null); - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - } -} - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_writing_and_subscribing_to_normal_events_manual_nack - : SpecificationWithMiniNode -{ - private const int BufferCount = 10; - private const int EventWriteCount = BufferCount * 2; - - private readonly ManualResetEvent _eventsReceived = new ManualResetEvent(false); - private int _eventReceivedCount; - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task Test() - { - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromCurrent() - .ResolveLinkTos() - .Build(); - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, - (subscription, resolvedEvent) => - { - subscription.Fail(resolvedEvent, PersistentSubscriptionNakEventAction.Park, "fail"); - - if (Interlocked.Increment(ref _eventReceivedCount) == EventWriteCount) - { - _eventsReceived.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, exception) => - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}).", reason, exception), - bufferSize: 10, autoAck: false, userCredentials: DefaultData.AdminCredentials); - - for (var i = 0; i < EventWriteCount; i++) - { - var eventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, eventData); - } - - if (!_eventsReceived.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - } -} - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_connection_drops_messages_that_have_run_out_of_retries_are_not_retried - : SpecificationWithMiniNode -{ - private readonly TaskCompletionSource _subscriptionDropped = new TaskCompletionSource(); - private readonly TaskCompletionSource _eventReceived = new TaskCompletionSource(); - private ResolvedEvent _receivedEvent; - - protected override Task When() => Task.CompletedTask; - - [Test] - [Retry(10)] - public void Test() => Assert.DoesNotThrowAsync(async () => - { - await CloseConnectionAndWait(_conn); - _conn = BuildConnection(_node); - AddLogging(_conn); - await _conn.ConnectAsync(); - - var streamName = Guid.NewGuid().ToString(); - var groupName = Guid.NewGuid().ToString(); - var settings = PersistentSubscriptionSettings - .Create() - .StartFromCurrent() - .WithMaxRetriesOf(0) // Don't retry messages - .Build(); - - await _conn.CreatePersistentSubscriptionAsync(streamName, groupName, settings, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, async (subscription, resolvedEvent) => - { - await CloseConnectionAndWait(_conn); - }, - (sub, reason, exception) => - { - Console.WriteLine("Subscription dropped (reason:{0}, exception:{1}). @ {2}", reason, exception, DateTime.Now); - _subscriptionDropped.TrySetResult(true); - }, - bufferSize: 10, autoAck: false, userCredentials: DefaultData.AdminCredentials); - - var parkedEventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, parkedEventData); - - await _subscriptionDropped.Task.WithTimeout(); - - _conn = BuildConnection(_node); - AddLogging(_conn); - await _conn.ConnectAsync(); - - await _conn.ConnectToPersistentSubscriptionAsync(streamName, groupName, (subscription, resolvedEvent) => - { - subscription.Acknowledge(resolvedEvent); - _receivedEvent = resolvedEvent; - _eventReceived.TrySetResult(true); - }, (sub, reason, exception) => - { - Console.WriteLine("Second Subscription dropped (reason:{0}, exception:{1}).", reason, exception); - }, bufferSize: 10, autoAck: false, userCredentials: DefaultData.AdminCredentials); - - // Ensure we only get the new event, not the previous one - var newEventData = new EventData(Guid.NewGuid(), "SomeEvent", false, new byte[0], new byte[0]); - await _conn.AppendToStreamAsync(streamName, ExpectedVersion.Any, DefaultData.AdminCredentials, newEventData); - - await _eventReceived.Task.WithTimeout(); - Assert.AreEqual(newEventData.EventId, _receivedEvent.Event.EventId); - - //flaky: temporarily added for debugging - void AddLogging(IEventStoreConnection conn) - { - conn.AuthenticationFailed += (_, args) => Console.WriteLine($"_conn.AuthenticationFailed: {args.Connection.ConnectionName} @ {DateTime.Now} {TestContext.CurrentContext.CurrentRepeatCount}"); - conn.Closed += (_, args) => Console.WriteLine($"_conn.Closed: {args.Connection.ConnectionName} @ {DateTime.Now} {TestContext.CurrentContext.CurrentRepeatCount}"); - conn.Connected += (_, args) => Console.WriteLine($"_conn.Connected: {args.Connection.ConnectionName} @ {DateTime.Now} {TestContext.CurrentContext.CurrentRepeatCount}"); - conn.Disconnected += (_, args) => Console.WriteLine($"_conn.Disconnected: {args.Connection.ConnectionName} @ {DateTime.Now} {TestContext.CurrentContext.CurrentRepeatCount}"); - conn.ErrorOccurred += (_, args) => Console.WriteLine($"_conn.ErrorOccurred: {args.Connection.ConnectionName} @ {DateTime.Now} {TestContext.CurrentContext.CurrentRepeatCount}"); - conn.Reconnecting += (_, args) => Console.WriteLine($"_conn.Reconnecting: {args.Connection.ConnectionName} @ {DateTime.Now} {TestContext.CurrentContext.CurrentRepeatCount}"); - } - }); -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_filtered_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_filtered_should.cs deleted file mode 100644 index 3c48c685ce..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_filtered_should.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_backward_filtered_should - : SpecificationWithMiniNode -{ - private List _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - DefaultData.AdminCredentials); - - _testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "AEvent")) - .ToList(); - _testEvents.AddRange( - Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "BEvent")) - .ToList()); - - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.OddEvents()); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_with_a_given_stream_prefix() - { - var filter = Filter.StreamId.Prefix("stream-a"); - - var read = await _conn.FilteredReadAllEventsBackwardAsync(Position.End, 4096, false, filter, 4096); - Assert.That(EventDataComparer.Equal( - _testEvents.EvenEvents().ReverseEvents(), - read.Events.Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_with_a_given_event_prefix() - { - var filter = Filter.EventType.Prefix("AE"); - - // Have to order the events as we are writing to two streams and can't guarantee ordering - - var read = await _conn.FilteredReadAllEventsBackwardAsync(Position.End, 4096, false, filter, 4096); - Assert.AreEqual(ReadDirection.Backward, read.ReadDirection); - Assert.That(EventDataComparer.Equal( - _testEvents.Where(e => e.Type == "AEvent").OrderBy(x => x.EventId).ToArray(), - read.Events.Select(x => x.Event).OrderBy(x => x.EventId).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_that_satisfy_a_given_stream_regex() - { - var filter = Filter.StreamId.Regex(new Regex(@"^.*m-b.*$")); - - var read = await _conn.FilteredReadAllEventsBackwardAsync(Position.End, 4096, false, filter, 4096); - Assert.AreEqual(ReadDirection.Backward, read.ReadDirection); - Assert.That(EventDataComparer.Equal( - _testEvents.OddEvents().ReverseEvents(), - read.Events.Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_that_satisfy_a_given_event_regex() - { - var filter = Filter.EventType.Regex(new Regex(@"^.*BEv.*$")); - - // Have to order the events as we are writing to two streams and can't guarantee ordering - var read = await _conn.FilteredReadAllEventsBackwardAsync(Position.End, 4096, false, filter, 4096); - Assert.AreEqual(ReadDirection.Backward, read.ReadDirection); - Assert.That(EventDataComparer.Equal( - _testEvents.Where(e => e.Type == "BEvent").OrderBy(x => x.EventId).ToArray(), - read.Events.Select(x => x.Event).OrderBy(x => x.EventId).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_that_are_not_system_events() - { - var filter = Filter.ExcludeSystemEvents; - - // Have to order the events as we are writing to two streams and can't guarantee ordering - var read = await _conn.FilteredReadAllEventsBackwardAsync(Position.End, 4096, false, filter, 4096); - Assert.AreEqual(ReadDirection.Backward, read.ReadDirection); - Assert.That(!read.Events.Any(e => e.Event.EventType.StartsWith("$"))); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_should.cs deleted file mode 100644 index 414484c094..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_backward_should.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_backward_should - : SpecificationWithMiniNode -{ - private EventData[] _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - DefaultData.AdminCredentials); - - _testEvents = Enumerable.Range(0, 20).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - await _conn.AppendToStreamAsync("stream", ExpectedVersion.NoStream, _testEvents); - } - - [Test, Category("LongRunning")] - public async Task return_empty_slice_if_asked_to_read_from_start() - { - var read = await _conn.ReadAllEventsBackwardAsync(Position.Start, 1, false); - Assert.That(read.IsEndOfStream, Is.True); - Assert.That(read.Events.Length, Is.EqualTo(0)); - } - - [Test, Category("LongRunning")] - public async Task return_partial_slice_if_not_enough_events() - { - var read = await _conn.ReadAllEventsBackwardAsync(Position.End, 40, false); - Assert.That(read.Events.Length, Is.LessThan(40)); - Assert.That(EventDataComparer.Equal(_testEvents.Reverse().ToArray(), - read.Events.Take(_testEvents.Length).Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task return_events_in_reversed_order_compared_to_written() - { - var read = await _conn.ReadAllEventsBackwardAsync(Position.End, _testEvents.Length, false); - Assert.That(EventDataComparer.Equal(_testEvents.Reverse().ToArray(), - read.Events.Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task be_able_to_read_all_one_by_one_until_end_of_stream() - { - var all = new List(); - var position = Position.End; - AllEventsSlice slice; - - while (!(slice = await _conn.ReadAllEventsBackwardAsync(position, 1, false)).IsEndOfStream) - { - all.Add(slice.Events.Single().Event); - position = slice.NextPosition; - } - - Assert.That( - EventDataComparer.Equal(_testEvents.Reverse().ToArray(), all.Take(_testEvents.Length).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task be_able_to_read_events_slice_at_time() - { - var all = new List(); - var position = Position.End; - AllEventsSlice slice; - - while (!(slice = await _conn.ReadAllEventsBackwardAsync(position, 5, false)).IsEndOfStream) - { - all.AddRange(slice.Events.Select(x => x.Event)); - position = slice.NextPosition; - } - - Assert.That( - EventDataComparer.Equal(_testEvents.Reverse().ToArray(), all.Take(_testEvents.Length).ToArray())); - } - - [Test] - [Category("Network")] - public async Task throw_when_got_int_max_value_as_maxcount() - { - await AssertEx.ThrowsAsync( - () => _conn.ReadAllEventsBackwardAsync(Position.Start, int.MaxValue, resolveLinkTos: false)); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_filtered_paging_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_filtered_paging_should.cs deleted file mode 100644 index f0e56d7c42..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_filtered_paging_should.cs +++ /dev/null @@ -1,150 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_filtered_paging_should - : SpecificationWithMiniNode -{ - private List _testEvents = new List(); - - private List _testEventsA; - - private List _testEventsC; - - public read_all_events_filtered_paging_should() : base(chunkSize: 2 * 1024 * 1024) { } - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - DefaultData.AdminCredentials); - - _testEventsA = Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "AEvent")) - .ToList(); - - var testEventsB = Enumerable - .Range(0, 10000) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "BEvent")) - .ToList(); - - _testEventsC = Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "CEvent")) - .ToList(); - - _testEvents.AddRange(_testEventsA); - _testEvents.AddRange(testEventsB); - _testEvents.AddRange(_testEventsC); - - _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents).Wait(); - } - - [Test, Category("LongRunning")] - public void handle_paging_between_events_forward() - { - var numberOfEmptySlicesRead = 0; - - var filter = Filter.EventType.Prefix("CE"); - var sliceStart = Position.Start; - var read = new List(); - AllEventsSlice slice; - - do - { - slice = _conn.FilteredReadAllEventsForwardAsync(sliceStart, 50, false, filter, maxSearchWindow: 100) - .GetAwaiter() - .GetResult(); - - if (slice.Events.Length == 0) - { - numberOfEmptySlicesRead++; - } - else - { - read.AddRange(slice.Events); - } - - sliceStart = slice.NextPosition; - } while (!slice.IsEndOfStream); - - Assert.That(EventDataComparer.Equal( - _testEventsC.ToArray(), - read.Select(x => x.Event).ToArray())); - - Assert.AreEqual(100, numberOfEmptySlicesRead); - } - - [Test, Category("LongRunning")] - public void handle_paging_between_events_backward() - { - var numberOfEmptySlicesRead = 0; - - var filter = Filter.EventType.Prefix("AE"); - var sliceStart = Position.End; - var read = new List(); - AllEventsSlice slice; - - do - { - slice = _conn.FilteredReadAllEventsBackwardAsync(sliceStart, 50, false, filter, maxSearchWindow: 100) - .GetAwaiter() - .GetResult(); - if (slice.Events.Length == 0) - { - numberOfEmptySlicesRead++; - } - else - { - read.AddRange(slice.Events); - } - - sliceStart = slice.NextPosition; - } while (!slice.IsEndOfStream); - - Assert.That(EventDataComparer.Equal( - _testEventsA.ReverseEvents(), - read.Select(x => x.Event).ToArray())); - - Assert.AreEqual(100, numberOfEmptySlicesRead); - } - - [Test, Category("LongRunning")] - public void handle_paging_between_events_returns_correct_number_of_events_for_max_search_window_forward() - { - var filter = Filter.EventType.Prefix("BE"); - - var slice = _conn.FilteredReadAllEventsForwardAsync(Position.Start, 30, false, filter, maxSearchWindow: 30) - .GetAwaiter() - .GetResult(); - - // Includes system events at start of stream (inc epoch-information) - var expectedCount = 11; - - Assert.AreEqual(expectedCount, slice.Events.Length); - } - - [Test, Category("LongRunning")] - public void handle_paging_between_events_returns_correct_number_of_events_for_max_search_window_backward() - { - var filter = Filter.EventType.Prefix("BE"); - - var slice = _conn.FilteredReadAllEventsBackwardAsync(Position.End, 20, false, filter, maxSearchWindow: 20) - .GetAwaiter() - .GetResult(); - - Assert.AreEqual(10, slice.Events.Length); // Includes system events - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_filtered_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_filtered_should.cs deleted file mode 100644 index 3dd8a928a5..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_filtered_should.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_forward_filtered_should - : SpecificationWithMiniNode -{ - private List _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - DefaultData.AdminCredentials); - - _testEvents = Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "AEvent")) - .ToList(); - - _testEvents.AddRange( - Enumerable - .Range(0, 10) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "BEvent")).ToList()); - - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.OddEvents()); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_with_a_given_stream_prefix() - { - var filter = Filter.StreamId.Prefix("stream-a"); - - var read = await _conn.FilteredReadAllEventsForwardAsync(Position.Start, 1000, false, filter, 1000); - Assert.That(EventDataComparer.Equal( - _testEvents.EvenEvents().ToArray(), - read.Events.Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_with_a_given_event_prefix() - { - var filter = Filter.EventType.Prefix("AE"); - - // Have to order the events as we are writing to two streams and can't guarantee ordering - var read = await _conn.FilteredReadAllEventsForwardAsync(Position.Start, 1000, false, filter, 1000); - Assert.That(EventDataComparer.Equal( - _testEvents.Where(e => e.Type == "AEvent").OrderBy(x => x.EventId).ToArray(), - read.Events.Select(x => x.Event).OrderBy(x => x.EventId).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_that_satisfy_a_given_stream_regex() - { - var filter = Filter.StreamId.Regex(new Regex(@"^.*eam-b.*$")); - - var read = await _conn.FilteredReadAllEventsForwardAsync(Position.Start, 1000, false, filter, 1000); - Assert.AreEqual(ReadDirection.Forward, read.ReadDirection); - Assert.That(EventDataComparer.Equal( - _testEvents.OddEvents().ToArray(), - read.Events.Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_that_satisfy_a_given_event_regex() - { - var filter = Filter.EventType.Regex(new Regex(@"^.*BEv.*$")); - - // Have to order the events as we are writing to two streams and can't guarantee ordering - var read = await _conn.FilteredReadAllEventsForwardAsync(Position.Start, 1000, false, filter, 1000); - Assert.AreEqual(ReadDirection.Forward, read.ReadDirection); - Assert.That(EventDataComparer.Equal( - _testEvents.Where(e => e.Type == "BEvent").OrderBy(x => x.EventId).ToArray(), - read.Events.Select(x => x.Event).OrderBy(x => x.EventId).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task only_return_events_that_are_not_system_events() - { - var filter = Filter.ExcludeSystemEvents; - - // Have to order the events as we are writing to two streams and can't guarantee ordering - var read = await _conn.FilteredReadAllEventsForwardAsync(Position.Start, 1000, false, filter, 1000); - Assert.AreEqual(ReadDirection.Forward, read.ReadDirection); - Assert.That(!read.Events.Any(e => e.Event.EventType.StartsWith("$"))); - } -} - -internal static class EventDataExtensions -{ - internal static List EvenEvents(this List ed) => ed.Where((c, i) => i % 2 == 0).ToList(); - - internal static List OddEvents(this List ed) => ed.Where((c, i) => i % 2 != 0).ToList(); - - internal static EventData[] ReverseEvents(this List ed) => ed.ToArray().Reverse().ToArray(); -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_should.cs deleted file mode 100644 index 8fda5abf07..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_should.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_forward_should : SpecificationWithMiniNode -{ - private EventData[] _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - DefaultData.AdminCredentials); - - _testEvents = Enumerable.Range(0, 20).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - await _conn.AppendToStreamAsync("stream", ExpectedVersion.NoStream, _testEvents); - } - - [Test, Category("LongRunning")] - public async Task return_empty_slice_if_asked_to_read_from_end() - { - var read = await _conn.ReadAllEventsForwardAsync(Position.End, 1, false); - Assert.That(read.IsEndOfStream, Is.True); - Assert.That(read.Events.Length, Is.EqualTo(0)); - } - - [Test, Category("LongRunning")] - public async Task return_events_in_same_order_as_written() - { - var read = await _conn.ReadAllEventsForwardAsync(Position.Start, _testEvents.Length + 20, false); - Assert.That(EventDataComparer.Equal( - _testEvents.ToArray(), - read.Events.Skip(read.Events.Length - _testEvents.Length).Select(x => x.Event).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task be_able_to_read_all_one_by_one_until_end_of_stream() - { - var all = new List(); - var position = Position.Start; - AllEventsSlice slice; - - while (!(slice = await _conn.ReadAllEventsForwardAsync(position, 1, false)).IsEndOfStream) - { - all.Add(slice.Events.Single().Event); - position = slice.NextPosition; - } - - Assert.That(EventDataComparer.Equal(_testEvents, all.Skip(all.Count - _testEvents.Length).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task be_able_to_read_events_slice_at_time() - { - var all = new List(); - var position = Position.Start; - AllEventsSlice slice; - - while (!(slice = await _conn.ReadAllEventsForwardAsync(position, 5, false)).IsEndOfStream) - { - all.AddRange(slice.Events.Select(x => x.Event)); - position = slice.NextPosition; - } - - Assert.That(EventDataComparer.Equal(_testEvents, all.Skip(all.Count - _testEvents.Length).ToArray())); - } - - [Test, Category("LongRunning")] - public async Task return_partial_slice_if_not_enough_events() - { - var read = await _conn.ReadAllEventsForwardAsync(Position.Start, 40, false); - Assert.That(read.Events.Length, Is.LessThan(40)); - Assert.That(EventDataComparer.Equal( - _testEvents, - read.Events.Skip(read.Events.Length - _testEvents.Length).Select(x => x.Event).ToArray())); - } - - [Test] - [Category("Network")] - public async Task throw_when_got_int_max_value_as_maxcount() - { - await AssertEx.ThrowsAsync( - () => _conn.ReadAllEventsForwardAsync(Position.Start, int.MaxValue, resolveLinkTos: false)); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_hard_deleted_stream_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_hard_deleted_stream_should.cs deleted file mode 100644 index c70c149509..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_hard_deleted_stream_should.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Data; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_forward_with_hard_deleted_stream_should - : SpecificationWithMiniNode -{ - private EventData[] _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync( - "$all", -1, StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - - _testEvents = Enumerable.Range(0, 20).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - await _conn.AppendToStreamAsync("stream", ExpectedVersion.NoStream, _testEvents); - await _conn.DeleteStreamAsync("stream", ExpectedVersion.Any, hardDelete: true); - } - - [Test, Category("LongRunning")] - public async Task ensure_deleted_stream() - { - var res = await _conn.ReadStreamEventsForwardAsync("stream", 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamDeleted, res.Status); - Assert.AreEqual(0, res.Events.Length); - } - - [Test, Category("LongRunning")] - public async Task returns_all_events_including_tombstone() - { - AllEventsSlice read = await _conn.ReadAllEventsForwardAsync(Position.Start, _testEvents.Length + 20, false) -; - Assert.That( - EventDataComparer.Equal( - _testEvents.ToArray(), - read.Events.Skip(read.Events.Length - _testEvents.Length - 1) - .Take(_testEvents.Length) - .Select(x => x.Event) - .ToArray())); - var lastEvent = read.Events.Last().Event; - Assert.AreEqual("stream", lastEvent.EventStreamId); - Assert.AreEqual(SystemEventTypes.StreamDeleted, lastEvent.EventType); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_passed_max_count.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_passed_max_count.cs deleted file mode 100644 index 0869ba4264..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_passed_max_count.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_forward_with_linkto_passed_max_count : SpecificationWithLinkToToMaxCountDeletedEvents -{ - private StreamEventsSlice _read; - - protected override async Task When() - { - _read = await _conn.ReadStreamEventsForwardAsync(LinkedStreamName, 0, 1, true); - } - - [Test] - public void one_event_is_read() - { - Assert.AreEqual(1, _read.Events.Length); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_to_deleted_event.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_to_deleted_event.cs deleted file mode 100644 index f98b47457a..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_linkto_to_deleted_event.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_forward_with_linkto_to_deleted_event : SpecificationWithLinkToToDeletedEvents -{ - private StreamEventsSlice _read; - - protected override async Task When() - { - _read = await _conn.ReadStreamEventsForwardAsync(LinkedStreamName, 0, 1, true, null); - } - - [Test] - public void one_event_is_read() - { - Assert.AreEqual(1, _read.Events.Length); - } - - [Test] - public void the_linked_event_is_not_resolved() - { - Assert.IsNull(_read.Events[0].Event); - } - - [Test] - public void the_link_event_is_included() - { - Assert.IsNotNull(_read.Events[0].OriginalEvent); - } - - [Test] - public void the_event_is_not_resolved() - { - Assert.IsFalse(_read.Events[0].IsResolved); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_soft_deleted_stream_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_soft_deleted_stream_should.cs deleted file mode 100644 index e190d70eef..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_all_events_forward_with_soft_deleted_stream_should.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Data; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_all_events_forward_with_soft_deleted_stream_should - : SpecificationWithMiniNode -{ - private EventData[] _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync( - "$all", -1, StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - - _testEvents = Enumerable.Range(0, 20).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - await _conn.AppendToStreamAsync("stream", ExpectedVersion.NoStream, _testEvents); - await _conn.DeleteStreamAsync("stream", ExpectedVersion.Any); - } - - [Test, Category("LongRunning")] - public async Task ensure_deleted_stream() - { - var res = await _conn.ReadStreamEventsForwardAsync("stream", 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamNotFound, res.Status); - Assert.AreEqual(0, res.Events.Length); - } - - [Test, Category("LongRunning")] - public async Task returns_all_events_including_tombstone() - { - AllEventsSlice read = await _conn.ReadAllEventsForwardAsync(Position.Start, _testEvents.Length + 20, false) -; - Assert.That( - EventDataComparer.Equal( - _testEvents.ToArray(), - read.Events.Skip(read.Events.Length - _testEvents.Length - 1) - .Take(_testEvents.Length) - .Select(x => x.Event) - .ToArray())); - var lastEvent = read.Events.Last().Event; - Assert.AreEqual("$$stream", lastEvent.EventStreamId); - Assert.AreEqual(SystemEventTypes.StreamMetadata, lastEvent.EventType); - var metadata = StreamMetadata.FromJsonBytes(lastEvent.Data); - Assert.AreEqual(EventNumber.DeletedStream, metadata.TruncateBefore); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_allevents_backward_with_linkto_deleted_event.cs b/src/EventStore.Core.Tests/ClientAPI/read_allevents_backward_with_linkto_deleted_event.cs deleted file mode 100644 index 055d9f3997..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_allevents_backward_with_linkto_deleted_event.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Threading.Tasks; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_event_of_linkto_to_deleted_event : SpecificationWithLinkToToDeletedEvents -{ - private EventReadResult _read; - - protected override async Task When() - { - _read = await _conn.ReadEventAsync(LinkedStreamName, 0, true); - } - - [Test] - public void the_linked_event_is_returned() - { - Assert.IsNotNull(_read.Event.Value.Link); - } - - [Test] - public void the_deleted_event_is_not_resolved() - { - Assert.IsNull(_read.Event.Value.Event); - } - - [Test] - public void the_status_is_success() - { - Assert.AreEqual(EventReadStatus.Success, _read.Status); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_allevents_backward_with_linkto_deleted_event : SpecificationWithLinkToToDeletedEvents -{ - private StreamEventsSlice _read; - - protected override async Task When() - { - _read = await _conn.ReadStreamEventsBackwardAsync(LinkedStreamName, 0, 1, true, null); - } - - [Test] - public void one_event_is_read() - { - Assert.AreEqual(1, _read.Events.Length); - } - - [Test] - public void the_linked_event_is_not_resolved() - { - Assert.IsNull(_read.Events[0].Event); - } - - [Test] - public void the_link_event_is_included() - { - Assert.IsNotNull(_read.Events[0].OriginalEvent); - } - - [Test] - public void the_event_is_not_resolved() - { - Assert.IsFalse(_read.Events[0].IsResolved); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_event_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_event_should.cs deleted file mode 100644 index 58380d9de1..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_event_should.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_event_should - : SpecificationWithMiniNode -{ - private Guid _eventId0; - private Guid _eventId1; - - protected override async Task When() - { - _eventId0 = Guid.NewGuid(); - _eventId1 = Guid.NewGuid(); - - await _conn.AppendToStreamAsync("test-stream", - -1, - new EventData(_eventId0, "event0", false, new byte[3], new byte[2]), - new EventData(_eventId1, "event1", true, new byte[7], new byte[10])); - await _conn.DeleteStreamAsync("deleted-stream", -1, hardDelete: true); - } - - [Test, Category("Network")] - public async Task throw_if_stream_id_is_null() - { - await AssertEx.ThrowsAsync(() => _conn.ReadEventAsync(null, 0, resolveLinkTos: false)); - } - - [Test, Category("Network")] - public async Task throw_if_stream_id_is_empty() - { - await AssertEx.ThrowsAsync(() => _conn.ReadEventAsync("", 0, resolveLinkTos: false)); - } - - [Test, Category("Network")] - public async Task throw_if_event_number_is_less_than_minus_one() - { - await AssertEx.ThrowsAsync(() => - _conn.ReadEventAsync("stream", -2, resolveLinkTos: false)); - } - - [Test, Category("Network")] - public async Task notify_using_status_code_if_stream_not_found() - { - var res = await _conn.ReadEventAsync("unexisting-stream", 5, false); - - Assert.AreEqual(EventReadStatus.NoStream, res.Status); - Assert.IsNull(res.Event); - Assert.AreEqual("unexisting-stream", res.Stream); - Assert.AreEqual(5, res.EventNumber); - } - - [Test, Category("Network")] - public async Task return_no_stream_if_requested_last_event_in_empty_stream() - { - var res = await _conn.ReadEventAsync("some-really-empty-stream", -1, false); - Assert.AreEqual(EventReadStatus.NoStream, res.Status); - } - - [Test, Category("Network")] - public async Task notify_using_status_code_if_stream_was_deleted() - { - var res = await _conn.ReadEventAsync("deleted-stream", 5, false); - - Assert.AreEqual(EventReadStatus.StreamDeleted, res.Status); - Assert.IsNull(res.Event); - Assert.AreEqual("deleted-stream", res.Stream); - Assert.AreEqual(5, res.EventNumber); - } - - [Test, Category("Network")] - public async Task notify_using_status_code_if_stream_does_not_have_event() - { - var res = await _conn.ReadEventAsync("test-stream", 5, false); - - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - Assert.IsNull(res.Event); - Assert.AreEqual("test-stream", res.Stream); - Assert.AreEqual(5, res.EventNumber); - } - - [Test, Category("Network")] - public async Task return_existing_event() - { - var res = await _conn.ReadEventAsync("test-stream", 0, false); - - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(res.Event.Value.OriginalEvent.EventId, _eventId0); - Assert.AreEqual("test-stream", res.Stream); - Assert.AreEqual(0, res.EventNumber); - Assert.AreNotEqual(DateTime.MinValue, res.Event.Value.OriginalEvent.Created); - Assert.AreNotEqual(0, res.Event.Value.OriginalEvent.CreatedEpoch); - } - - [Test, Category("Network")] - public async Task retrieve_the_is_json_flag_properly() - { - var res = await _conn.ReadEventAsync("test-stream", 1, false); - - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(res.Event.Value.OriginalEvent.EventId, _eventId1); - Assert.IsTrue(res.Event.Value.OriginalEvent.IsJson); - } - - [Test, Category("Network")] - public async Task return_last_event_in_stream_if_event_number_is_minus_one() - { - var res = await _conn.ReadEventAsync("test-stream", -1, false); - - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(res.Event.Value.OriginalEvent.EventId, _eventId1); - Assert.AreEqual("test-stream", res.Stream); - Assert.AreEqual(-1, res.EventNumber); - Assert.AreNotEqual(DateTime.MinValue, res.Event.Value.OriginalEvent.Created); - Assert.AreNotEqual(0, res.Event.Value.OriginalEvent.CreatedEpoch); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_event_stream_backward_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_event_stream_backward_should.cs deleted file mode 100644 index 8886a6f3cf..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_event_stream_backward_should.cs +++ /dev/null @@ -1,243 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_event_stream_backward_should : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - virtual protected IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test] - [Category("Network")] - public async Task throw_if_count_le_zero() - { - const string stream = "read_event_stream_backward_should_throw_if_count_le_zero"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await AssertEx.ThrowsAsync(() => - store.ReadStreamEventsBackwardAsync(stream, 0, 0, resolveLinkTos: false)); - } - } - - [Test] - [Category("Network")] - public async Task throw_if_start_less_than_minus_one() - { - const string stream = "read_event_stream_backward_should_throw_if_start_less_than_minus_one"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await AssertEx.ThrowsAsync(() => - store.ReadStreamEventsBackwardAsync(stream, -2, 100, resolveLinkTos: false)); - } - } - - - [Test] - [Category("Network")] - public async Task notify_using_status_code_if_stream_not_found() - { - const string stream = "read_event_stream_backward_should_notify_using_status_code_if_stream_not_found"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var read = await store.ReadStreamEventsBackwardAsync(stream, StreamPosition.End, 1, - resolveLinkTos: false); - - Assert.That(read.Status, Is.EqualTo(SliceReadStatus.StreamNotFound)); - } - } - - [Test] - [Category("Network")] - public async Task notify_using_status_code_if_stream_was_deleted() - { - const string stream = "read_event_stream_backward_should_notify_using_status_code_if_stream_was_deleted"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - var read = await store.ReadStreamEventsBackwardAsync(stream, StreamPosition.End, 1, - resolveLinkTos: false); - - Assert.That(read.Status, Is.EqualTo(SliceReadStatus.StreamDeleted)); - } - } - - [Test] - [Category("Network")] - public async Task return_no_events_when_called_on_empty_stream() - { - const string stream = "read_event_stream_backward_should_return_single_event_when_called_on_empty_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var read = await store.ReadStreamEventsBackwardAsync(stream, StreamPosition.End, 1, - resolveLinkTos: false); - - Assert.That(read.Events.Length, Is.EqualTo(0)); - } - } - - [Test] - [Category("Network")] - public async Task return_partial_slice_if_no_enough_events_in_stream() - { - const string stream = - "read_event_stream_backward_should_return_partial_slice_if_no_enough_events_in_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent((x + 1).ToString())) - .ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsBackwardAsync(stream, 1, 5, resolveLinkTos: false); - - Assert.That(read.Events.Length, Is.EqualTo(2)); - } - } - - [Test] - [Category("Network")] - public async Task return_events_reversed_compared_to_written() - { - const string stream = "read_event_stream_backward_should_return_events_reversed_compared_to_written"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent((x + 1).ToString())) - .ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsBackwardAsync(stream, StreamPosition.End, testEvents.Length, - resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents.Reverse().ToArray(), - read.Events.Select(x => x.Event).ToArray())); - } - } - - [Test] - [Category("Network")] - public async Task be_able_to_read_single_event_from_arbitrary_position() - { - const string stream = - "read_event_stream_backward_should_be_able_to_read_single_event_from_arbitrary_position"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsBackwardAsync(stream, 7, 1, resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents[7], read.Events.Single().Event)); - } - } - - [Test] - [Category("Network")] - public async Task be_able_to_read_first_event() - { - const string stream = "read_event_stream_backward_should_be_able_to_read_first_event"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent((x + 1).ToString())) - .ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsBackwardAsync(stream, StreamPosition.Start, 1, - resolveLinkTos: false); - - Assert.That(read.Events.Length, Is.EqualTo(1)); - } - } - - [Test] - [Category("Network")] - public async Task be_able_to_read_last_event() - { - const string stream = "read_event_stream_backward_should_be_able_to_read_last_event"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsBackwardAsync(stream, StreamPosition.End, 1, - resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents.Last(), read.Events.Single().Event)); - } - } - - [Test] - [Category("Network")] - public async Task be_able_to_read_slice_from_arbitrary_position() - { - const string stream = "read_event_stream_backward_should_be_able_to_read_slice_from_arbitrary_position"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsBackwardAsync(stream, 3, 2, resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents.Skip(2).Take(2).Reverse().ToArray(), - read.Events.Select(x => x.Event).ToArray())); - } - } - - [Test] - [Category("Network")] - public async Task throw_when_got_int_max_value_as_maxcount() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await AssertEx.ThrowsAsync(() => - store.ReadStreamEventsBackwardAsync("foo", StreamPosition.Start, int.MaxValue, - resolveLinkTos: false)); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_event_stream_forward_should.cs b/src/EventStore.Core.Tests/ClientAPI/read_event_stream_forward_should.cs deleted file mode 100644 index 45a4fec0b3..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_event_stream_forward_should.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_event_stream_forward_should : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - virtual protected IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test] - [Category("Network")] - public async Task throw_if_count_le_zero() - { - const string stream = "read_event_stream_forward_should_throw_if_count_le_zero"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await AssertEx.ThrowsAsync(() => - store.ReadStreamEventsForwardAsync(stream, 0, 0, resolveLinkTos: false)); - } - } - - [Test] - [Category("Network")] - public async Task throw_if_start_lt_zero() - { - const string stream = "read_event_stream_forward_should_throw_if_start_lt_zero"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await AssertEx.ThrowsAsync(() => - store.ReadStreamEventsForwardAsync(stream, -1, 1, resolveLinkTos: false)); - } - } - - [Test] - [Category("Network")] - public async Task notify_using_status_code_if_stream_not_found() - { - const string stream = "read_event_stream_forward_should_notify_using_status_code_if_stream_not_found"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 1, resolveLinkTos: false); - - Assert.That(read.Status, Is.EqualTo(SliceReadStatus.StreamNotFound)); - } - } - - [Test] - [Category("Network")] - public async Task notify_using_status_code_if_stream_was_deleted() - { - const string stream = "read_event_stream_forward_should_notify_using_status_code_if_stream_was_deleted"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var delete = await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 1, resolveLinkTos: false); - - Assert.That(read.Status, Is.EqualTo(SliceReadStatus.StreamDeleted)); - } - } - - [Test] - [Category("Network")] - public async Task return_no_events_when_called_on_empty_stream() - { - const string stream = "read_event_stream_forward_should_return_single_event_when_called_on_empty_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var read = await store.ReadStreamEventsForwardAsync(stream, 0, 1, resolveLinkTos: false); - - Assert.That(read.Events.Length, Is.EqualTo(0)); - } - } - - [Test] - [Category("Network")] - public async Task return_empty_slice_when_called_on_non_existing_range() - { - const string stream = - "read_event_stream_forward_should_return_empty_slice_when_called_on_non_existing_range"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var write10 = await store.AppendToStreamAsync(stream, - ExpectedVersion.NoStream, - Enumerable.Range(0, 10).Select(x => - TestEvent.NewTestEvent((x + 1).ToString(CultureInfo.InvariantCulture)))); - - var read = await store.ReadStreamEventsForwardAsync(stream, 11, 5, resolveLinkTos: false); - - Assert.That(read.Events.Length, Is.EqualTo(0)); - } - } - - [Test] - [Category("Network")] - public async Task return_partial_slice_if_not_enough_events_in_stream() - { - const string stream = "read_event_stream_forward_should_return_partial_slice_if_no_enough_events_in_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var write10 = await store.AppendToStreamAsync(stream, - ExpectedVersion.NoStream, - Enumerable.Range(0, 10).Select(x => - TestEvent.NewTestEvent((x + 1).ToString(CultureInfo.InvariantCulture)))); - - var read = await store.ReadStreamEventsForwardAsync(stream, 9, 5, resolveLinkTos: false); - - Assert.That(read.Events.Length, Is.EqualTo(1)); - } - } - - [Test] - [Category("Network")] - public async Task throw_when_got_int_max_value_as_maxcount() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - await AssertEx.ThrowsAsync(() => - store.ReadStreamEventsForwardAsync("foo", StreamPosition.Start, int.MaxValue, - resolveLinkTos: false)); - } - } - - [Test] - [Category("Network")] - public async Task return_events_in_same_order_as_written() - { - const string stream = "read_event_stream_forward_should_return_events_in_same_order_as_written"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsForwardAsync(stream, StreamPosition.Start, testEvents.Length, - resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents, read.Events.Select(x => x.Event).ToArray())); - } - } - - [Test] - [Category("Network")] - public async Task be_able_to_read_single_event_from_arbitrary_position() - { - const string stream = "read_event_stream_forward_should_be_able_to_read_from_arbitrary_position"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsForwardAsync(stream, 5, 1, resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents[5], read.Events.Single().Event)); - } - } - - [Test] - [Category("Network")] - public async Task be_able_to_read_slice_from_arbitrary_position() - { - const string stream = "read_event_stream_forward_should_be_able_to_read_slice_from_arbitrary_position"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var testEvents = Enumerable.Range(0, 10).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - var write10 = await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, testEvents); - - var read = await store.ReadStreamEventsForwardAsync(stream, 5, 2, resolveLinkTos: false); - - Assert.That(EventDataComparer.Equal(testEvents.Skip(5).Take(2).ToArray(), - read.Events.Select(x => x.Event).ToArray())); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_event_with_hash_collision.cs b/src/EventStore.Core.Tests/ClientAPI/read_event_with_hash_collision.cs deleted file mode 100644 index 47046963a9..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_event_with_hash_collision.cs +++ /dev/null @@ -1,82 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Storage.HashCollisions; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_event_with_hash_collision : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, TcpType.Ssl); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task should_return_not_found() - { - const string stream1 = "account--696193173"; - const string stream2 = "LPN-FC002_LPK51001"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - //Write event to stream 1 - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream1, ExpectedVersion.NoStream, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - //Write 100 events to stream 2 which will have the same hash as stream 1. - for (int i = 0; i < 100; i++) - { - Assert.AreEqual(i, - (await store.AppendToStreamAsync(stream2, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - } - } - - var tcpPort = _node.TcpEndPoint.Port; - var httpPort = _node.HttpEndPoint.Port; - await _node.Shutdown(keepDb: true); - - //Restart the node to ensure the read index stream info cache is empty - _node = new MiniNode(PathName, - tcpPort, httpPort, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(EventReadStatus.NoStream, (await store.ReadEventAsync(stream1, 0, true)).Status); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_from_persistent_subscription_with_link_resolution_when_stream_name_contains_at_symbol.cs b/src/EventStore.Core.Tests/ClientAPI/read_from_persistent_subscription_with_link_resolution_when_stream_name_contains_at_symbol.cs deleted file mode 100644 index 014e6807c0..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_from_persistent_subscription_with_link_resolution_when_stream_name_contains_at_symbol.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("LongRunning"), Category("ClientAPI")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_from_persistent_subscription_with_link_resolution_when_stream_name_contains_at_symbol : SpecificationWithMiniNode -{ - private string _result; - - protected override async Task When() - { - var task = new TaskCompletionSource(); - - var setts = PersistentSubscriptionSettings.Create() - .ResolveLinkTos() - .StartFromBeginning(); - - await _conn.CreatePersistentSubscriptionAsync("link", "Agroup", setts, DefaultData.AdminCredentials); - await _conn.ConnectToPersistentSubscriptionAsync( - "link", - "Agroup", - (sub, @event) => - { - var data = Encoding.Default.GetString(@event.Event.Data); - task.TrySetResult(data); - return Task.CompletedTask; - }, - (sub, reason, ex) => { }, DefaultData.AdminCredentials); - - await _conn.AppendToStreamAsync("target@me@com", ExpectedVersion.NoStream, TestEvent.NewTestEvent("data", eventName: "AEvent")); - await _conn.AppendToStreamAsync("link", ExpectedVersion.NoStream, TestEvent.NewTestEvent("0@target@me@com", eventName: "$>")); - - _result = await Task.WhenAny(task.Task, Task.Delay(TimeSpan.FromSeconds(30)).ContinueWith(_ => "timeout")).Result; - } - - [Test] - public void the_subscription_resolve_the_link_properly() - { - Assert.AreEqual(_result, "data"); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_stream_events_backward_with_hash_collision.cs b/src/EventStore.Core.Tests/ClientAPI/read_stream_events_backward_with_hash_collision.cs deleted file mode 100644 index 93a934927c..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_stream_events_backward_with_hash_collision.cs +++ /dev/null @@ -1,84 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Storage.HashCollisions; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_stream_events_backward_with_hash_collision - : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, TcpType.Ssl); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task should_return_stream_not_found() - { - const string stream1 = "account--696193173"; - const string stream2 = "LPN-FC002_LPK51001"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - //Write event to stream 1 - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream1, ExpectedVersion.NoStream, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - //Write 100 events to stream 2 which will have the same hash as stream 1. - for (int i = 0; i < 100; i++) - { - Assert.AreEqual(i, - (await store.AppendToStreamAsync(stream2, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - } - } - - var tcpPort = _node.TcpEndPoint.Port; - var httpPort = _node.HttpEndPoint.Port; - await _node.Shutdown(keepDb: true); - - //Restart the node to ensure the read index stream info cache is empty - _node = new MiniNode(PathName, - tcpPort, httpPort, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(SliceReadStatus.StreamNotFound, - (await store.ReadStreamEventsBackwardAsync(stream1, 0, 1, true)).Status); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_stream_events_forward_with_hash_collision.cs b/src/EventStore.Core.Tests/ClientAPI/read_stream_events_forward_with_hash_collision.cs deleted file mode 100644 index 57f63a0c93..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_stream_events_forward_with_hash_collision.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Storage.HashCollisions; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_stream_events_forward_with_hash_collision : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.To(node, TcpType.Ssl); - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task should_return_stream_not_found() - { - const string stream1 = "account--696193173"; - const string stream2 = "LPN-FC002_LPK51001"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - //Write event to stream 1 - Assert.AreEqual(0, - (await store.AppendToStreamAsync(stream1, ExpectedVersion.NoStream, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - //Write 100 events to stream 2 which will have the same hash as stream 1. - for (int i = 0; i < 100; i++) - { - Assert.AreEqual(i, - (await store.AppendToStreamAsync(stream2, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "TestEvent", true, null, null))).NextExpectedVersion); - } - } - - var tcpPort = _node.TcpEndPoint.Port; - var httpPort = _node.HttpEndPoint.Port; - await _node.Shutdown(keepDb: true); - - //Restart the node to ensure the read index stream info cache is empty - _node = new MiniNode(PathName, - tcpPort, httpPort, - memTableSize: 20, - hashCollisionReadLimit: 1, - indexBitnessVersion: EventStore.Core.Index.PTableVersions.IndexV4, - hash32bit: true, - streamExistenceFilterSize: 0); - await _node.Start(); - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - Assert.AreEqual(SliceReadStatus.StreamNotFound, - (await store.ReadStreamEventsForwardAsync(stream1, 0, 1, true)).Status); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/read_stream_events_with_unresolved_linkto.cs b/src/EventStore.Core.Tests/ClientAPI/read_stream_events_with_unresolved_linkto.cs deleted file mode 100644 index a2e16fe74f..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/read_stream_events_with_unresolved_linkto.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Data; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class read_stream_events_with_unresolved_linkto : SpecificationWithMiniNode -{ - private EventData[] _testEvents; - - protected override async Task When() - { - await _conn.SetStreamMetadataAsync( - "$all", -1, StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - - _testEvents = Enumerable.Range(0, 20).Select(x => TestEvent.NewTestEvent(x.ToString())).ToArray(); - await _conn.AppendToStreamAsync("stream", ExpectedVersion.NoStream, _testEvents); - await _conn.AppendToStreamAsync( - "links", ExpectedVersion.NoStream, - new EventData( - Guid.NewGuid(), EventStore.ClientAPI.Common.SystemEventTypes.LinkTo, false, - Encoding.UTF8.GetBytes("0@stream"), null)); - await _conn.DeleteStreamAsync("stream", ExpectedVersion.Any); - } - - [Test, Category("LongRunning")] - public async Task ensure_deleted_stream() - { - var res = await _conn.ReadStreamEventsForwardAsync("stream", 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamNotFound, res.Status); - Assert.AreEqual(0, res.Events.Length); - } - - [Test, Category("LongRunning")] - public async Task returns_unresolved_linkto() - { - var read = await _conn.ReadStreamEventsForwardAsync("links", 0, 1, true); - Assert.AreEqual(1, read.Events.Length); - Assert.IsNull(read.Events[0].Event); - Assert.IsNotNull(read.Events[0].Link); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/soft_delete.cs b/src/EventStore.Core.Tests/ClientAPI/soft_delete.cs deleted file mode 100644 index ac4ecf2629..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/soft_delete.cs +++ /dev/null @@ -1,385 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.Internal; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class soft_delete : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - private IEventStoreConnection _conn; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - _conn = BuildConnection(_node); - await _conn.ConnectAsync(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - _conn.Close(); - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return EventStoreConnection.Create( - ConnectionSettings.Create().DisableServerCertificateValidation().Build(), - node.TcpEndPoint.ToESTcpUri()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_returns_no_stream_and_no_events_on_read() - { - const string stream = "soft_deleted_stream_returns_no_stream_and_no_events_on_read"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamNotFound, res.Status); - Assert.AreEqual(0, res.Events.Length); - Assert.AreEqual(1, res.LastEventNumber); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_allows_recreation_when_expver_any() - { - const string stream = "soft_deleted_stream_allows_recreation_when_expver_any"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - - var events = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - Assert.AreEqual(4, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, events)).NextExpectedVersion); - - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.LastEventNumber); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(events.Select(x => x.EventId), res.Events.Select(x => x.OriginalEvent.EventId)); - Assert.AreEqual(new[] { 2, 3, 4 }, res.Events.Select(x => x.OriginalEvent.EventNumber)); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(1, meta.MetastreamVersion); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_allows_recreation_when_expver_no_stream() - { - const string stream = "soft_deleted_stream_allows_recreation_when_expver_no_stream"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - - var events = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - Assert.AreEqual(4, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.LastEventNumber); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(events.Select(x => x.EventId), res.Events.Select(x => x.OriginalEvent.EventId)); - Assert.AreEqual(new[] { 2, 3, 4 }, res.Events.Select(x => x.OriginalEvent.EventNumber)); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(1, meta.MetastreamVersion); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_allows_recreation_when_expver_is_exact() - { - const string stream = "soft_deleted_stream_allows_recreation_when_expver_is_exact"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - - var events = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - Assert.AreEqual(4, (await _conn.AppendToStreamAsync(stream, 1, events)).NextExpectedVersion); - - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.LastEventNumber); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(events.Select(x => x.EventId), res.Events.Select(x => x.OriginalEvent.EventId)); - Assert.AreEqual(new[] { 2, 3, 4 }, res.Events.Select(x => x.OriginalEvent.EventNumber)); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(1, meta.MetastreamVersion); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_when_recreated_preserves_metadata_except_truncatebefore() - { - const string stream = "soft_deleted_stream_when_recreated_preserves_metadata_except_truncatebefore"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - - Assert.AreEqual(0, (await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(long.MaxValue) - .SetMaxCount(100) - .SetDeleteRole("some-role") - .SetCustomProperty("key1", true) - .SetCustomProperty("key2", 17) - .SetCustomProperty("key3", "some value"))).NextExpectedVersion); - - var events = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - Assert.AreEqual(4, (await _conn.AppendToStreamAsync(stream, 1, events)).NextExpectedVersion); - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.LastEventNumber); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(events.Select(x => x.EventId), res.Events.Select(x => x.OriginalEvent.EventId)); - Assert.AreEqual(new[] { 2, 3, 4 }, res.Events.Select(x => x.OriginalEvent.EventNumber)); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(100, meta.StreamMetadata.MaxCount); - Assert.AreEqual("some-role", meta.StreamMetadata.Acl.DeleteRole); - Assert.AreEqual(true, meta.StreamMetadata.GetValue("key1")); - Assert.AreEqual(17, meta.StreamMetadata.GetValue("key2")); - Assert.AreEqual("some value", meta.StreamMetadata.GetValue("key3")); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_can_be_hard_deleted() - { - const string stream = "soft_deleted_stream_can_be_deleted"; - - Assert.AreEqual(1, - actual: (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - await _conn.DeleteStreamAsync(stream, ExpectedVersion.Any, hardDelete: true); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamDeleted, res.Status); - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(true, meta.IsStreamDeleted); - - await AssertEx.ThrowsAsync(() => - _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_allows_recreation_only_for_first_write() - { - const string stream = "soft_deleted_stream_allows_recreation_only_for_first_write"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - - var events = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - Assert.AreEqual(4, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, events)).NextExpectedVersion); - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - await AssertEx.ThrowsAsync(() => - _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent())); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.LastEventNumber); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(events.Select(x => x.EventId), res.Events.Select(x => x.OriginalEvent.EventId)); - Assert.AreEqual(new[] { 2, 3, 4 }, res.Events.Select(x => x.OriginalEvent.EventNumber)); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(1, meta.MetastreamVersion); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task soft_deleted_stream_appends_both_writes_when_expver_any() - { - const string stream = "soft_deleted_stream_appends_both_concurrent_writes_when_expver_any"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1); - - var events1 = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - var events2 = new[] { TestEvent.NewTestEvent(), TestEvent.NewTestEvent() }; - Assert.AreEqual(4, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, events1)).NextExpectedVersion); - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - Assert.AreEqual(6, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, events2)).NextExpectedVersion); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(6, res.LastEventNumber); - Assert.AreEqual(5, res.Events.Length); - Assert.AreEqual(events1.Concat(events2).Select(x => x.EventId), - res.Events.Select(x => x.OriginalEvent.EventId)); - Assert.AreEqual(new[] { 2, 3, 4, 5, 6 }, res.Events.Select(x => x.OriginalEvent.EventNumber)); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(1, meta.MetastreamVersion); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task - setting_json_metadata_on_empty_soft_deleted_stream_recreates_stream_not_overriding_metadataAsync() - { - const string stream = - "setting_json_metadata_on_empty_soft_deleted_stream_recreates_stream_not_overriding_metadata"; - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.Any, StreamMetadata.Build() - .SetTruncateBefore(long.MaxValue)); - - Assert.AreEqual(1, (await _conn.SetStreamMetadataAsync(stream, 0, - StreamMetadata.Build().SetMaxCount(100) - .SetDeleteRole("some-role") - .SetCustomProperty("key1", true) - .SetCustomProperty("key2", 17) - .SetCustomProperty("key3", "some value"))).NextExpectedVersion); - - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamNotFound, res.Status); - Assert.AreEqual(-1, res.LastEventNumber); - Assert.AreEqual(0, res.Events.Length); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.MetastreamVersion); - Assert.AreEqual(0, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(100, meta.StreamMetadata.MaxCount); - Assert.AreEqual("some-role", meta.StreamMetadata.Acl.DeleteRole); - Assert.AreEqual(true, meta.StreamMetadata.GetValue("key1")); - Assert.AreEqual(17, meta.StreamMetadata.GetValue("key2")); - Assert.AreEqual("some value", meta.StreamMetadata.GetValue("key3")); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task - setting_json_metadata_on_nonempty_soft_deleted_stream_recreates_stream_not_overriding_metadataAsync() - { - const string stream = - "setting_json_metadata_on_nonempty_soft_deleted_stream_recreates_stream_not_overriding_metadata"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1, hardDelete: false); - - Assert.AreEqual(1, (await _conn.SetStreamMetadataAsync(stream, 0, - StreamMetadata.Build().SetMaxCount(100) - .SetDeleteRole("some-role") - .SetCustomProperty("key1", true) - .SetCustomProperty("key2", 17) - .SetCustomProperty("key3", "some value"))).NextExpectedVersion); - - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(1, res.LastEventNumber); - Assert.AreEqual(0, res.Events.Length); - - var meta = await _conn.GetStreamMetadataAsync(stream); - Assert.AreEqual(2, meta.MetastreamVersion); - Assert.AreEqual(2, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(100, meta.StreamMetadata.MaxCount); - Assert.AreEqual("some-role", meta.StreamMetadata.Acl.DeleteRole); - Assert.AreEqual(true, meta.StreamMetadata.GetValue("key1")); - Assert.AreEqual(17, meta.StreamMetadata.GetValue("key2")); - Assert.AreEqual("some value", meta.StreamMetadata.GetValue("key3")); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task - setting_nonjson_metadata_on_empty_soft_deleted_stream_recreates_stream_keeping_original_metadataAsync() - { - const string stream = - "setting_nonjson_metadata_on_empty_soft_deleted_stream_recreates_stream_overriding_metadata"; - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.Any, StreamMetadata.Build() - .SetTruncateBefore(long.MaxValue)); - - Assert.AreEqual(1, (await _conn.SetStreamMetadataAsync(stream, 0, new byte[256])).NextExpectedVersion); - - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.StreamNotFound, res.Status); - Assert.AreEqual(-1, res.LastEventNumber); - Assert.AreEqual(0, res.Events.Length); - - var meta = await _conn.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(new byte[256], meta.StreamMetadata); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task - setting_nonjson_metadata_on_nonempty_soft_deleted_stream_recreates_stream_keeping_original_metadataAsync() - { - const string stream = - "setting_nonjson_metadata_on_nonempty_soft_deleted_stream_recreates_stream_overriding_metadata"; - - Assert.AreEqual(1, - (await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - await _conn.DeleteStreamAsync(stream, 1, hardDelete: false); - - Assert.AreEqual(1, (await _conn.SetStreamMetadataAsync(stream, 0, new byte[256])).NextExpectedVersion); - await Task.Delay(50); //TODO: This is a workaround until github issue #1744 is fixed - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(1, res.LastEventNumber); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(new[] { 0, 1 }, res.Events.Select(x => x.OriginalEventNumber).ToArray()); - - var meta = await _conn.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(new byte[256], meta.StreamMetadata); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/subscribe_should.cs b/src/EventStore.Core.Tests/ClientAPI/subscribe_should.cs deleted file mode 100644 index 3bda338b71..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/subscribe_should.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_should : SpecificationWithDirectoryPerTestFixture -{ - private const int Timeout = 10000; - - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test, Category("LongRunning")] - public async Task be_able_to_subscribe_to_non_existing_stream_and_then_catch_new_event() - { - const string stream = - "subscribe_should_be_able_to_subscribe_to_non_existing_stream_and_then_catch_created_event"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new CountdownEvent(1); - var dropped = new CountdownEvent(1); - - using (await store.SubscribeToStreamAsync(stream, false, (s, x) => - { - appeared.Signal(); - return; - }, (s, r, e) => dropped.Signal())) - { - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - Assert.IsTrue(appeared.Wait(Timeout), "Appeared countdown event timed out."); - } - } - } - - [Test, Category("LongRunning")] - public async Task allow_multiple_subscriptions_to_same_stream() - { - const string stream = "subscribe_should_allow_multiple_subscriptions_to_same_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new CountdownEvent(2); - var dropped = new CountdownEvent(2); - - using (await store.SubscribeToStreamAsync(stream, false, (s, x) => - { - appeared.Signal(); - return; - }, (s, r, e) => dropped.Signal())) - using (await store.SubscribeToStreamAsync(stream, false, (s, x) => - { - appeared.Signal(); - return; - }, (s, r, e) => dropped.Signal())) - { - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - Assert.IsTrue(appeared.Wait(Timeout), "Appeared countdown event timed out."); - } - } - } - - [Test, Category("LongRunning")] - public async Task call_dropped_callback_after_unsubscribe_method_call() - { - const string stream = "subscribe_should_call_dropped_callback_after_unsubscribe_method_call"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var dropped = new CountdownEvent(1); - using (var subscription = await store.SubscribeToStreamAsync(stream, false, (s, x) => Task.CompletedTask, - (s, r, e) => dropped.Signal())) - { - subscription.Unsubscribe(); - } - - Assert.IsTrue(dropped.Wait(Timeout), "Dropped countdown event timed out."); - } - } - - [Test, Category("LongRunning")] - public async Task catch_deleted_events_as_well() - { - const string stream = "subscribe_should_catch_created_and_deleted_events_as_well"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var appeared = new CountdownEvent(1); - var dropped = new CountdownEvent(1); - using (await store.SubscribeToStreamAsync(stream, false, (s, x) => - { - appeared.Signal(); - return; - }, - (s, r, e) => dropped.Signal())) - { - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - Assert.IsTrue(appeared.Wait(Timeout), "Appeared countdown event timed out."); - } - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_catching_up_should.cs b/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_catching_up_should.cs deleted file mode 100644 index c8ffbb398a..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_catching_up_should.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning"), NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_to_all_catching_up_should : SpecificationWithDirectory -{ - private static readonly ILogger Log = Serilog.Log.ForContext>(); - private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(60); - private const int LongRunningTimeout = 300000; - private static readonly TimeSpan StartupTimeout = TimeSpan.FromMinutes(5); - private static readonly TimeSpan ConnectionCloseTimeout = TimeSpan.FromSeconds(10); - - private MiniNode _node; - private IEventStoreConnection _conn; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(TimeSpan.FromSeconds(60)); - - _conn = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - conn => conn.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - } - - [TearDown] - public override async Task TearDown() - { - try - { - if (_conn != null) - { - await TestConnectionLifecycle.CloseConnectionAndWait(_conn, ConnectionCloseTimeout); - } - } - catch - { - if (_conn != null) - { - TestConnectionLifecycle.TryCloseConnection(_conn); - } - } - finally - { - TestConnectionLifecycle.DisposeIfNeeded(_conn); - } - - await _node.Shutdown(); - await base.TearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task call_dropped_callback_after_stop_method_call() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var dropped = new CountdownEvent(1); - var subscription = store.SubscribeToAllFrom(null, - CatchUpSubscriptionSettings.Default, - (x, y) => Task.CompletedTask, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task call_dropped_callback_when_an_error_occurs_while_processing_an_event() - { - const string stream = "call_dropped_callback_when_an_error_occurs_while_processing_an_event"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await store.AppendToStreamAsync(stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "event", false, new byte[3], null)); - - var dropped = new CountdownEvent(1); - store.SubscribeToAllFrom(null, CatchUpSubscriptionSettings.Default, - (x, y) => { throw new Exception("Error"); }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task be_able_to_subscribe_to_empty_db() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new ManualResetEventSlim(false); - var dropped = new CountdownEvent(1); - - var subscription = store.SubscribeToAllFrom(null, - CatchUpSubscriptionSettings.Default, - (_, x) => - { - if (!SystemStreams.IsSystemStream(x.OriginalEvent.EventStreamId)) - { - appeared.Set(); - } - - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (_, __, ___) => dropped.Signal()); - - await Task.Delay(100); // give time for first pull phase - await store.SubscribeToAllAsync(false, (s, x) => Task.CompletedTask, (s, r, e) => { }); - await Task.Delay(100); - - Assert.IsFalse(appeared.Wait(0), "Some event appeared."); - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task read_all_existing_events_and_keep_listening_to_new_ones() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new List(); - var appeared = new CountdownEvent(20); - var dropped = new CountdownEvent(1); - - for (int i = 0; i < 10; ++i) - { - await store.AppendToStreamAsync("stream-" + i.ToString(), -1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - var subscription = store.SubscribeToAllFrom(null, - CatchUpSubscriptionSettings.Default, - (x, y) => - { - if (!SystemStreams.IsSystemStream(y.OriginalEvent.EventStreamId)) - { - events.Add(y); - appeared.Signal(); - } - - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - for (int i = 10; i < 20; ++i) - { - await store.AppendToStreamAsync("stream-" + i.ToString(), -1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Assert.AreEqual(20, events.Count); - for (int i = 0; i < 20; ++i) - { - Assert.AreEqual("et-" + i.ToString(), events[i].OriginalEvent.EventType); - } - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task filter_events_and_keep_listening_to_new_ones() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new List(); - var appeared = new CountdownEvent(10); - var dropped = new CountdownEvent(1); - - for (int i = 0; i < 10; ++i) - { - await store.AppendToStreamAsync("stream-" + i.ToString(), -1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - var allSlice = await store.ReadAllEventsForwardAsync(Position.Start, 100, false); - var lastEvent = allSlice.Events.Last(); - - var subscription = store.SubscribeToAllFrom(lastEvent.OriginalPosition, - CatchUpSubscriptionSettings.Default, - (x, y) => - { - if (SystemStreams.IsSystemStream(y.Event.EventStreamId)) - { - return Task.CompletedTask; - } - events.Add(y); - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => - { - Log.Information("Subscription dropped: {0}, {1}.", y, z); - dropped.Signal(); - }); - - for (int i = 10; i < 20; ++i) - { - await store.AppendToStreamAsync("stream-" + i.ToString(), -1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - Log.Information("Waiting for events..."); - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Log.Information("Events appeared..."); - Assert.AreEqual(10, events.Count); - for (int i = 0; i < 10; ++i) - { - Assert.AreEqual("et-" + (10 + i).ToString(), events[i].OriginalEvent.EventType); - } - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - - Assert.AreEqual(events.Last().OriginalPosition, subscription.LastProcessedPosition); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task filter_events_and_work_if_nothing_was_written_after_subscription() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new List(); - var appeared = new CountdownEvent(1); - var dropped = new CountdownEvent(1); - - for (int i = 0; i < 10; ++i) - { - await store.AppendToStreamAsync("stream-" + i.ToString(), -1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - var allSlice = await store.ReadAllEventsForwardAsync(Position.Start, 100, false); - var lastEvent = allSlice.Events[allSlice.Events.Length - 2]; - - var subscription = store.SubscribeToAllFrom(lastEvent.OriginalPosition, - CatchUpSubscriptionSettings.Default, - (x, y) => - { - events.Add(y); - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => - { - Log.Information("Subscription dropped: {0}, {1}.", y, z); - dropped.Signal(); - }); - - Log.Information("Waiting for events..."); - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Log.Information("Events appeared..."); - Assert.AreEqual(1, events.Count); - Assert.AreEqual("et-9", events[0].OriginalEvent.EventType); - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - - Assert.AreEqual(events.Last().OriginalPosition, subscription.LastProcessedPosition); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_filtered_should.cs b/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_filtered_should.cs deleted file mode 100644 index 7ebcb15d99..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_filtered_should.cs +++ /dev/null @@ -1,388 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning"), NonParallelizable] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_to_all_filtered_should : SpecificationWithDirectory -{ - private const int Timeout = 10000; - private const int LongRunningTimeout = 600000; - private static readonly TimeSpan StartupTimeout = TimeSpan.FromMinutes(10); - private static readonly TimeSpan ConnectionCloseTimeout = TimeSpan.FromSeconds(10); - - private MiniNode _node; - private IEventStoreConnection _conn; - private List _testEvents; - private List _fakeSystemEvents; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(TimeSpan.FromSeconds(60)); - - _conn = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - conn => conn.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - await _conn.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - - _testEvents = Enumerable - .Range(0, 5) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "AEvent")) - .ToList(); - - _testEvents.AddRange( - Enumerable - .Range(0, 5) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "BEvent")).ToList()); - - _fakeSystemEvents = Enumerable - .Range(0, 5) - .Select(x => TestEvent.NewTestEvent(x.ToString(), eventName: "$systemEvent")) - .ToList(); - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task only_return_events_with_a_given_stream_prefix() - { - var filter = Filter.StreamId.Prefix("stream-a"); - var foundEvents = new List(); - var store = await CreateStoreConnection(); - try - { - var appeared = CreateAppearedSignal(); - var subscription = await store.FilteredSubscribeToAllAsync(false, filter, (s, e) => - { - foundEvents.Add(e); - if (foundEvents.Count == 5) - { - appeared.TrySetResult(true); - } - return Task.CompletedTask; - }, (s, p) => Task.CompletedTask, 100, CreateDropHandler(appeared)); - try - { - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.OddEvents()); - - await appeared.Task.WithTimeout(Timeout); - - Assert.True(foundEvents.All(e => e.Event.EventStreamId == "stream-a")); - } - finally - { - StopSubscription(subscription); - } - } - finally - { - await CloseConnection(store); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task only_return_events_with_a_given_event_prefix() - { - var filter = Filter.EventType.Prefix("AE"); - var foundEvents = new List(); - var store = await CreateStoreConnection(); - try - { - var appeared = CreateAppearedSignal(); - var subscription = await store.FilteredSubscribeToAllAsync(false, filter, (s, e) => - { - if (e.OriginalStreamId == SystemStreams.EventTypesStream) - { - return Task.CompletedTask; - } - - foundEvents.Add(e); - if (foundEvents.Count == 5) - { - appeared.TrySetResult(true); - } - return Task.CompletedTask; - }, (s, p) => Task.CompletedTask, 100, CreateDropHandler(appeared)); - try - { - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.OddEvents()); - - await appeared.Task.WithTimeout(Timeout); - - Assert.True(foundEvents.All(e => e.Event.EventType == "AEvent")); - } - finally - { - StopSubscription(subscription); - } - } - finally - { - await CloseConnection(store); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task only_return_events_that_satisfy_a_given_stream_regex() - { - var filter = Filter.StreamId.Regex(new Regex(@"^.*eam-b.*$")); - var foundEvents = new List(); - var store = await CreateStoreConnection(); - try - { - var appeared = CreateAppearedSignal(); - var subscription = await store.FilteredSubscribeToAllAsync(false, filter, (s, e) => - { - foundEvents.Add(e); - if (foundEvents.Count == 5) - { - appeared.TrySetResult(true); - } - return Task.CompletedTask; - }, (s, p) => Task.CompletedTask, 100, CreateDropHandler(appeared)); - try - { - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.OddEvents()); - - await appeared.Task.WithTimeout(Timeout); - - Assert.True(foundEvents.All(e => e.Event.EventStreamId == "stream-b")); - } - finally - { - StopSubscription(subscription); - } - } - finally - { - await CloseConnection(store); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task only_return_events_that_satisfy_a_given_event_regex() - { - var filter = Filter.EventType.Regex(new Regex(@"^.*BEv.*$")); - var foundEvents = new ConcurrentBag(); - var store = await CreateStoreConnection(); - try - { - var appeared = CreateAppearedSignal(); - var subscription = await store.FilteredSubscribeToAllAsync(false, filter, (s, e) => - { - foundEvents.Add(e); - if (foundEvents.Count == 5) - { - appeared.TrySetResult(true); - } - return Task.CompletedTask; - }, (s, p) => Task.CompletedTask, 100, CreateDropHandler(appeared)); - try - { - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.OddEvents()); - - await appeared.Task.WithTimeout(Timeout); - - Assert.True(foundEvents.All(e => e.Event.EventType == "BEvent")); - } - finally - { - StopSubscription(subscription); - } - } - finally - { - await CloseConnection(store); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task only_return_events_that_are_not_system_events() - { - var filter = Filter.ExcludeSystemEvents; - var foundEvents = new List(); - var store = await CreateStoreConnection(); - try - { - var appeared = CreateAppearedSignal(); - var subscription = await store.FilteredSubscribeToAllAsync(false, filter, (s, e) => - { - foundEvents.Add(e); - if (foundEvents.Count == 5) - { - appeared.TrySetResult(true); - } - return Task.CompletedTask; - }, (s, p) => Task.CompletedTask, 100, CreateDropHandler(appeared)); - try - { - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _fakeSystemEvents); - await _conn.AppendToStreamAsync("stream-b", ExpectedVersion.NoStream, _testEvents.EvenEvents()); - - await appeared.Task.WithTimeout(Timeout); - - Assert.True(foundEvents.All(e => !e.Event.EventType.StartsWith("$"))); - } - finally - { - StopSubscription(subscription); - } - } - finally - { - await CloseConnection(store); - } - } - - [Test, Category("LongRunning")] - public async Task throw_an_exception_if_interval_is_negative() - { - var filter = Filter.ExcludeSystemEvents; - var store = await CreateStoreConnection(); - try - { - Assert.Throws(() => - { - store.FilteredSubscribeToAllAsync( - false, - filter, - (s, e) => Task.CompletedTask, - (s, p) => Task.CompletedTask, 0).Wait(); - }); - } - finally - { - await CloseConnection(store); - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task calls_checkpoint_reached_according_to_checkpoint_message_count() - { - var filter = Filter.ExcludeSystemEvents; - const int expectedEvents = 10; - const int expectedCheckpoints = 5; - var store = await CreateStoreConnection(); - - void TryComplete(TaskCompletionSource appeared, List eventsSeen, int checkpointsSeen) - { - if (checkpointsSeen >= expectedCheckpoints && eventsSeen.Count >= expectedEvents) - { - appeared.TrySetResult(true); - } - } - - try - { - var appeared = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var eventsSeen = new List(); - var checkpointsSeen = 0; - - var subscription = await store.FilteredSubscribeToAllAsync(false, - filter, - (s, e) => - { - eventsSeen.Add(e); - TryComplete(appeared, eventsSeen, checkpointsSeen); - return Task.CompletedTask; - }, - (s, p) => - { - checkpointsSeen++; - TryComplete(appeared, eventsSeen, checkpointsSeen); - return Task.CompletedTask; - }, - checkpointInterval: 2, - CreateDropHandler(appeared)); - try - { - await _conn.AppendToStreamAsync("stream-a", ExpectedVersion.NoStream, _testEvents); - - await appeared.Task.WithTimeout(Timeout); - - Assert.AreEqual(expectedCheckpoints * 2 /*considered events*/, eventsSeen.Count); - Assert.True(eventsSeen.All(x => x.Event.EventStreamId == "stream-a")); - } - finally - { - StopSubscription(subscription); - } - } - finally - { - await CloseConnection(store); - } - } - - [TearDown] - public override async Task TearDown() - { - await CloseConnection(_conn); - await _node.Shutdown(); - await base.TearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - private Task CreateStoreConnection() => - TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - connection => connection.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - - private static TaskCompletionSource CreateAppearedSignal() => - new(TaskCreationOptions.RunContinuationsAsynchronously); - - private static Action CreateDropHandler( - TaskCompletionSource appeared) => - (_, reason, ex) => appeared.TrySetException(ex ?? new InvalidOperationException($"Subscription dropped: {reason}")); - - private static async Task CloseConnection(IEventStoreConnection connection) - { - try - { - await TestConnectionLifecycle.CloseConnectionAndWait(connection, ConnectionCloseTimeout); - } - catch - { - TestConnectionLifecycle.TryCloseConnection(connection); - } - - TestConnectionLifecycle.DisposeIfNeeded(connection); - } - - private static void StopSubscription(EventStoreSubscription subscription) - { - try - { - subscription.Dispose(); - } - catch - { - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_should.cs b/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_should.cs deleted file mode 100644 index 811012e89f..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_all_should.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_to_all_should : SpecificationWithDirectory -{ - private const int Timeout = 10000; - private const int LongRunningTimeout = 120000; - - private MiniNode _node; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - using (var connection = BuildConnection(_node)) - { - await connection.ConnectAsync(); - await connection.SetStreamMetadataAsync("$all", -1, - StreamMetadata.Build().SetReadRole(SystemRoles.All), - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword)); - } - } - - [TearDown] - public override async Task TearDown() - { - await _node.Shutdown(); - await base.TearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test, Category("LongRunning")] - public async Task allow_multiple_subscriptions() - { - const string stream = "subscribe_to_all_should_allow_multiple_subscriptions"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new CountdownEvent(2); - var dropped = new CountdownEvent(2); - - using (await store.SubscribeToAllAsync(false, (s, x) => - { - appeared.Signal(); - return; - }, (s, r, e) => dropped.Signal())) - using (await store.SubscribeToAllAsync(false, (s, x) => - { - appeared.Signal(); - return; - }, (s, r, e) => dropped.Signal())) - { - var create = - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - Assert.IsTrue(appeared.Wait(Timeout), "Appeared countdown event timed out."); - } - } - } - - [Test, Category("LongRunning"), Timeout(LongRunningTimeout)] - public async Task catch_deleted_events_as_well() - { - const string stream = "subscribe_to_all_should_catch_created_and_deleted_events_as_well"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new CountdownEvent(1); - var dropped = new CountdownEvent(1); - - var completed = false; - var subscription = await store.SubscribeToAllAsync(false, (s, x) => - { - appeared.Signal(); - return Task.CompletedTask; - }, - (s, r, e) => dropped.Signal()); - - try - { - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - Assert.IsTrue(appeared.Wait(Timeout), "Appeared countdown event didn't fire in time."); - completed = true; - } - finally - { - subscription.Unsubscribe(); - - if (completed) - { - Assert.IsTrue(dropped.Wait(Timeout), "Dropped countdown event timed out."); - } - else - { - dropped.Wait(Timeout); - } - } - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_stream_catching_up_should.cs b/src/EventStore.Core.Tests/ClientAPI/subscribe_to_stream_catching_up_should.cs deleted file mode 100644 index 478d459d8b..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/subscribe_to_stream_catching_up_should.cs +++ /dev/null @@ -1,361 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class subscribe_to_stream_catching_up_should : SpecificationWithDirectoryPerTestFixture -{ - private static readonly ILogger Log = - Serilog.Log.ForContext>(); - - private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(500); - - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - virtual protected IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test, Category("LongRunning")] - public async Task be_able_to_subscribe_to_non_existing_stream() - { - const string stream = "be_able_to_subscribe_to_non_existing_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new ManualResetEventSlim(false); - var dropped = new CountdownEvent(1); - - var subscription = store.SubscribeToStreamFrom(stream, - null, - CatchUpSubscriptionSettings.Default, - (_, x) => - { - appeared.Set(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (_, __, ___) => dropped.Signal()); - - await Task.Delay(100); // give time for first pull phase - await store.SubscribeToStreamAsync(stream, false, (s, x) => Task.CompletedTask, (s, r, e) => { }); - await Task.Delay(100); - Assert.IsFalse(appeared.Wait(0), "Some event appeared."); - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning")] - public async Task be_able_to_subscribe_to_non_existing_stream_and_then_catch_event() - { - const string stream = "be_able_to_subscribe_to_non_existing_stream_and_then_catch_event"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new CountdownEvent(1); - var dropped = new CountdownEvent(1); - - var subscription = store.SubscribeToStreamFrom(stream, - null, - CatchUpSubscriptionSettings.Default, - (_, x) => - { - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (_, __, ___) => dropped.Signal()); - - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Appeared countdown event timed out."); - } - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning")] - public async Task allow_multiple_subscriptions_to_same_stream() - { - const string stream = "allow_multiple_subscriptions_to_same_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var appeared = new CountdownEvent(2); - var dropped1 = new ManualResetEventSlim(false); - var dropped2 = new ManualResetEventSlim(false); - - var sub1 = store.SubscribeToStreamFrom(stream, - null, - CatchUpSubscriptionSettings.Default, - (_, e) => - { - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped1.Set()); - var sub2 = store.SubscribeToStreamFrom(stream, - null, - CatchUpSubscriptionSettings.Default, - (_, e) => - { - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped2.Set()); - - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped1.Wait(0), "Subscription1 was dropped prematurely."); - Assert.IsFalse(dropped2.Wait(0), "Subscription2 was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Assert.IsFalse(dropped1.Wait(0)); - sub1.Stop(Timeout); - Assert.IsTrue(dropped1.Wait(Timeout)); - - Assert.IsFalse(dropped2.Wait(0)); - sub2.Stop(Timeout); - Assert.IsTrue(dropped2.Wait(Timeout)); - } - } - - [Test, Category("LongRunning")] - public async Task call_dropped_callback_after_stop_method_call() - { - const string stream = "call_dropped_callback_after_stop_method_call"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var dropped = new CountdownEvent(1); - var subscription = store.SubscribeToStreamFrom(stream, - null, - CatchUpSubscriptionSettings.Default, - (x, y) => Task.CompletedTask, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning")] - public async Task call_dropped_callback_when_an_error_occurs_while_processing_an_event() - { - const string stream = "call_dropped_callback_when_an_error_occurs_while_processing_an_event"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - await store.AppendToStreamAsync(stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "event", false, new byte[3], null)); - - var dropped = new CountdownEvent(1); - store.SubscribeToStreamFrom(stream, null, - CatchUpSubscriptionSettings.Default, - (x, y) => { throw new Exception("Error"); }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning")] - public async Task read_all_existing_events_and_keep_listening_to_new_ones() - { - const string stream = "read_all_existing_events_and_keep_listening_to_new_ones"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new List(); - var appeared = new CountdownEvent(20); // events - var dropped = new CountdownEvent(1); - - for (int i = 0; i < 10; ++i) - { - await store.AppendToStreamAsync(stream, i - 1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - var subscription = store.SubscribeToStreamFrom(stream, - null, - CatchUpSubscriptionSettings.Default, - (x, y) => - { - events.Add(y); - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - for (int i = 10; i < 20; ++i) - { - await store.AppendToStreamAsync(stream, i - 1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Assert.AreEqual(20, events.Count); - for (int i = 0; i < 20; ++i) - { - Assert.AreEqual("et-" + i.ToString(), events[i].OriginalEvent.EventType); - } - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - } - } - - [Test, Category("LongRunning")] - public async Task filter_events_and_keep_listening_to_new_ones() - { - const string stream = "filter_events_and_keep_listening_to_new_ones"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new List(); - var appeared = new CountdownEvent(20); // skip first 10 events - var dropped = new CountdownEvent(1); - - for (int i = 0; i < 20; ++i) - { - await store.AppendToStreamAsync(stream, i - 1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - var subscription = store.SubscribeToStreamFrom(stream, - 9, - CatchUpSubscriptionSettings.Default, - (x, y) => - { - events.Add(y); - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - for (int i = 20; i < 30; ++i) - { - await store.AppendToStreamAsync(stream, i - 1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Assert.AreEqual(20, events.Count); - for (int i = 0; i < 20; ++i) - { - Assert.AreEqual("et-" + (i + 10).ToString(), events[i].OriginalEvent.EventType); - } - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - - Assert.AreEqual(events.Last().OriginalEventNumber, subscription.LastProcessedEventNumber); - - subscription.Stop(TimeSpan.FromSeconds(0)); - } - } - - [Test, Category("LongRunning")] - public async Task filter_events_and_work_if_nothing_was_written_after_subscription() - { - const string stream = "filter_events_and_work_if_nothing_was_written_after_subscription"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var events = new List(); - var appeared = new CountdownEvent(10); - var dropped = new CountdownEvent(1); - - for (int i = 0; i < 20; ++i) - { - await store.AppendToStreamAsync(stream, i - 1, - new EventData(Guid.NewGuid(), "et-" + i.ToString(), false, new byte[3], null)); - } - - var subscription = store.SubscribeToStreamFrom(stream, - 9, - CatchUpSubscriptionSettings.Default, - (x, y) => - { - events.Add(y); - appeared.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Live processing started."), - (x, y, z) => dropped.Signal()); - if (!appeared.Wait(Timeout)) - { - Assert.IsFalse(dropped.Wait(0), "Subscription was dropped prematurely."); - Assert.Fail("Could not wait for all events."); - } - - Assert.AreEqual(10, events.Count); - for (int i = 0; i < 10; ++i) - { - Assert.AreEqual("et-" + (i + 10).ToString(), events[i].OriginalEvent.EventType); - } - - Assert.IsFalse(dropped.Wait(0)); - subscription.Stop(Timeout); - Assert.IsTrue(dropped.Wait(Timeout)); - - Assert.AreEqual(events.Last().OriginalEventNumber, subscription.LastProcessedEventNumber); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/transaction.cs b/src/EventStore.Core.Tests/ClientAPI/transaction.cs deleted file mode 100644 index c64e37f12f..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/transaction.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Common.Utils; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class Transaction : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test] - [Category("Network")] - public async Task should_start_on_non_existing_stream_with_correct_exp_ver_and_create_stream_on_commit() - { - const string stream = - "should_start_on_non_existing_stream_with_correct_exp_ver_and_create_stream_on_commit"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.NoStream)) - { - await transaction.WriteAsync(TestEvent.NewTestEvent()); - Assert.AreEqual(0, (await transaction.CommitAsync()).NextExpectedVersion); - } - } - } - - [Test] - [Category("Network")] - public async Task should_start_on_non_existing_stream_with_exp_ver_any_and_create_stream_on_commit() - { - const string stream = "should_start_on_non_existing_stream_with_exp_ver_any_and_create_stream_on_commit"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.Any)) - { - await transaction.WriteAsync(TestEvent.NewTestEvent()); - Assert.AreEqual(0, (await transaction.CommitAsync()).NextExpectedVersion); - } - } - } - - [Test] - [Category("Network")] - public async Task should_fail_to_commit_non_existing_stream_with_wrong_exp_ver() - { - const string stream = "should_fail_to_commit_non_existing_stream_with_wrong_exp_ver"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, 1)) - { - await transaction.WriteAsync(TestEvent.NewTestEvent()); - await AssertEx.ThrowsAsync(() => transaction.CommitAsync()); - } - } - } - - [Test] - [Category("Network")] - public async Task should_do_nothing_if_commits_no_events_to_empty_stream() - { - const string stream = "should_do_nothing_if_commits_no_events_to_empty_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.NoStream)) - { - Assert.AreEqual(-1, (await transaction.CommitAsync()).NextExpectedVersion); - } - - var result = await store.ReadStreamEventsForwardAsync(stream, 0, 1, resolveLinkTos: false); - Assert.That(result.Events.Length, Is.EqualTo(0)); - } - } - - [Test, Category("Network")] - public async Task should_do_nothing_if_transactionally_writing_no_events_to_empty_stream() - { - const string stream = "should_do_nothing_if_transactionally_writing_no_events_to_empty_stream"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.NoStream)) - { - await transaction.WriteAsync(); - Assert.AreEqual(-1, (await transaction.CommitAsync()).NextExpectedVersion); - } - - var result = await store.ReadStreamEventsForwardAsync(stream, 0, 1, resolveLinkTos: false); - Assert.That(result.Events.Length, Is.EqualTo(0)); - } - } - - [Test] - [Category("Network")] - public async Task should_validate_expectations_on_commit() - { - const string stream = "should_validate_expectations_on_commit"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, 100500)) - { - await transaction.WriteAsync(TestEvent.NewTestEvent()); - await AssertEx.ThrowsAsync(() => transaction.CommitAsync()); - } - } - } - - [Test] - [Category("Network")] - public async Task should_commit_when_writing_with_exp_ver_any_even_while_somene_is_writing_in_parallel() - { - const string stream = - "should_commit_when_writing_with_exp_ver_any_even_while_somene_is_writing_in_parallel"; - - const int totalTranWrites = 500; - const int totalPlainWrites = 500; - - async Task PlainWrites() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.Any)) - { - var writes = new List(); - for (int i = 0; i < totalTranWrites; i++) - { - writes.Add(transaction.WriteAsync(TestEvent.NewTestEvent(i.ToString(), "trans write"))); - } - - await Task.WhenAll(writes); - await transaction.CommitAsync(); - } - } - } - - async Task ParallelWrites() - { - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var writes = new List(); - for (int i = 0; i < totalPlainWrites; i++) - { - writes.Add(store.AppendToStreamAsync(stream, - ExpectedVersion.Any, TestEvent.NewTestEvent(i.ToString(), "plain write"))); - } - - await Task.WhenAll(writes); - } - } - - await Task.WhenAll(ParallelWrites(), PlainWrites()); - - // check all written - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - var slice = await store.ReadStreamEventsForwardAsync(stream, 0, totalTranWrites + totalPlainWrites, - false); - Assert.That(slice.Events.Length, Is.EqualTo(totalTranWrites + totalPlainWrites)); - - Assert.That(slice.Events.Count(ent => Helper.UTF8NoBom.GetString(ent.Event.Metadata) == "trans write"), - Is.EqualTo(totalTranWrites)); - Assert.That(slice.Events.Count(ent => Helper.UTF8NoBom.GetString(ent.Event.Metadata) == "plain write"), - Is.EqualTo(totalPlainWrites)); - } - } - - [Test] - [Category("Network")] - public async Task should_fail_to_commit_if_started_with_correct_ver_but_committing_with_bad() - { - const string stream = "should_fail_to_commit_if_started_with_correct_ver_but_committing_with_bad"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.NoStream)) - { - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()) - ; - await transaction.WriteAsync(TestEvent.NewTestEvent()); - await AssertEx.ThrowsAsync(() => transaction.CommitAsync()); - } - } - } - - [Test, Category("Network")] - public async Task should_not_fail_to_commit_if_started_with_wrong_ver_but_committing_with_correct_ver() - { - const string stream = "should_not_fail_to_commit_if_started_with_wrong_ver_but_committing_with_correct_ver"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, 0)) - { - await store.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()) - ; - await transaction.WriteAsync(TestEvent.NewTestEvent()); - var writeResult = await transaction.CommitAsync(); - Assert.AreEqual(1, writeResult.NextExpectedVersion); - } - } - } - - [Test] - [Category("Network")] - public async Task should_fail_to_commit_if_started_with_correct_ver_but_on_commit_stream_was_deleted() - { - const string stream = "should_fail_to_commit_if_started_with_correct_ver_but_on_commit_stream_was_deleted"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - using (var transaction = await store.StartTransactionAsync(stream, ExpectedVersion.NoStream)) - { - await transaction.WriteAsync(TestEvent.NewTestEvent()); - await store.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - await AssertEx.ThrowsAsync(() => transaction.CommitAsync()); - } - } - } - - [Test, Category("LongRunning")] - public async Task idempotency_is_correct_for_explicit_transactions_with_expected_version_any() - { - const string streamId = "idempotency_is_correct_for_explicit_transactions_with_expected_version_any"; - using (var store = BuildConnection(_node)) - { - await store.ConnectAsync(); - - var e = new EventData(Guid.NewGuid(), "SomethingHappened", true, - Helper.UTF8NoBom.GetBytes("{Value:42}"), null); - - var transaction1 = await store.StartTransactionAsync(streamId, ExpectedVersion.Any); - await transaction1.WriteAsync(e); - Assert.AreEqual(0, (await transaction1.CommitAsync()).NextExpectedVersion); - - var transaction2 = await store.StartTransactionAsync(streamId, ExpectedVersion.Any); - await transaction2.WriteAsync(e); - var writeResult = await transaction2.CommitAsync(); - Assert.AreEqual(0, writeResult.NextExpectedVersion); - - var res = await store.ReadStreamEventsForwardAsync(streamId, 0, 100, false); - Assert.AreEqual(1, res.Events.Length); - Assert.AreEqual(e.EventId, res.Events[0].Event.EventId); - } - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/update_persistent_subscription.cs b/src/EventStore.Core.Tests/ClientAPI/update_persistent_subscription.cs deleted file mode 100644 index 087e884922..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/update_persistent_subscription.cs +++ /dev/null @@ -1,140 +0,0 @@ -using System; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class update_existing_persistent_subscription : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override async Task Given() - { - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "whatever", true, Encoding.UTF8.GetBytes("{'foo' : 2}"), new Byte[0])); - await _conn.CreatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials); - } - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_completion_succeeds() - { - await _conn.UpdatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class update_existing_persistent_subscription_with_subscribers : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - private readonly AutoResetEvent _dropped = new AutoResetEvent(false); - private SubscriptionDropReason _reason; - private Exception _exception; - private Exception _caught = null; - - protected override async Task Given() - { - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "whatever", true, Encoding.UTF8.GetBytes("{'foo' : 2}"), new Byte[0])); - await _conn.CreatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials) -; - _conn.ConnectToPersistentSubscription(_stream, "existing", (x, y) => Task.CompletedTask, - (sub, reason, ex) => - { - _dropped.Set(); - _reason = reason; - _exception = ex; - }, DefaultData.AdminCredentials); - } - - protected override async Task When() - { - try - { - await _conn.UpdatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials); - } - catch (Exception ex) - { - _caught = ex; - } - } - - [Test] - public void the_completion_succeeds() - { - Assert.IsNull(_caught); - } - - [Test] - public void existing_subscriptions_are_dropped() - { - Assert.IsTrue(_dropped.WaitOne(TimeSpan.FromSeconds(5))); - Assert.AreEqual(SubscriptionDropReason.UserInitiated, _reason); - Assert.IsNull(_exception); - } -} - - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class update_non_existing_persistent_subscription : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task the_completion_fails_with_not_found() - { - await AssertEx.ThrowsAsync( - () => _conn.UpdatePersistentSubscriptionAsync(_stream, "existing", _settings, - DefaultData.AdminCredentials)); - } -} - -[Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class update_existing_persistent_subscription_without_permissions : SpecificationWithMiniNode -{ - private readonly string _stream = Guid.NewGuid().ToString(); - - private readonly PersistentSubscriptionSettings _settings = PersistentSubscriptionSettings.Create() - .DoNotResolveLinkTos() - .StartFromCurrent(); - - protected override async Task When() - { - await _conn.AppendToStreamAsync(_stream, ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "whatever", true, Encoding.UTF8.GetBytes("{'foo' : 2}"), new Byte[0])); - await _conn.CreatePersistentSubscriptionAsync(_stream, "existing", _settings, DefaultData.AdminCredentials) -; - } - - [Test] - public async Task the_completion_fails_with_access_denied() - { - await AssertEx.ThrowsAsync( - () => _conn.UpdatePersistentSubscriptionAsync(_stream, "existing", _settings, null)); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/when_committing_empty_transaction.cs b/src/EventStore.Core.Tests/ClientAPI/when_committing_empty_transaction.cs deleted file mode 100644 index 2fa1a065ec..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/when_committing_empty_transaction.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_committing_empty_transaction : SpecificationWithDirectory -{ - private static readonly TimeSpan StartupTimeout = TimeSpan.FromMinutes(5); - private const int LongRunningTimeout = 120000; - private MiniNode _node; - private IEventStoreConnection _connection; - private EventData _firstEvent; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(TimeSpan.FromSeconds(60)); - - _firstEvent = TestEvent.NewTestEvent(); - - _connection = await TestConnectionLifecycle.ReconnectUntilReady( - () => BuildConnection(_node), - connection => connection.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - StartupTimeout); - - Assert.AreEqual(2, (await _connection.AppendToStreamAsync("test-stream", - ExpectedVersion.NoStream, - _firstEvent, - TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - - using (var transaction = await _connection.StartTransactionAsync("test-stream", 2)) - { - Assert.AreEqual(2, (await transaction.CommitAsync()).NextExpectedVersion); - } - } - - [TearDown] - public override async Task TearDown() - { - try - { - await TestConnectionLifecycle.CloseConnectionAndWait(_connection, TimeSpan.FromSeconds(10)); - } - catch - { - TestConnectionLifecycle.TryCloseConnection(_connection); - } - - TestConnectionLifecycle.DisposeIfNeeded(_connection); - await _node.Shutdown(); - await base.TearDown(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [Test, Timeout(LongRunningTimeout)] - public async Task following_append_with_correct_expected_version_are_commited_correctly() - { - Assert.AreEqual(4, - (await _connection.AppendToStreamAsync("test-stream", 2, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent()) - ).NextExpectedVersion); - - var res = await _connection.ReadStreamEventsForwardAsync("test-stream", 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(5, res.Events.Length); - for (int i = 0; i < 5; ++i) - { - Assert.AreEqual(i, res.Events[i].OriginalEventNumber); - } - } - - [Test, Timeout(LongRunningTimeout)] - public async Task following_append_with_expected_version_any_are_commited_correctly() - { - Assert.AreEqual(4, - (await _connection.AppendToStreamAsync("test-stream", ExpectedVersion.Any, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent())).NextExpectedVersion); - - var res = await _connection.ReadStreamEventsForwardAsync("test-stream", 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(5, res.Events.Length); - for (int i = 0; i < 5; ++i) - { - Assert.AreEqual(i, res.Events[i].OriginalEventNumber); - } - } - - [Test, Timeout(LongRunningTimeout)] - public async Task committing_first_event_with_expected_version_no_stream_is_idempotent() - { - Assert.AreEqual(0, - (await _connection.AppendToStreamAsync("test-stream", ExpectedVersion.NoStream, _firstEvent)) - .NextExpectedVersion); - - var res = await _connection.ReadStreamEventsForwardAsync("test-stream", 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - for (int i = 0; i < 3; ++i) - { - Assert.AreEqual(i, res.Events[i].OriginalEventNumber); - } - } - - [Test, Timeout(LongRunningTimeout)] - public async Task trying_to_append_new_events_with_expected_version_no_stream_fails() - { - await AssertEx.ThrowsAsync(() => - _connection.AppendToStreamAsync("test-stream", ExpectedVersion.NoStream, TestEvent.NewTestEvent())); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/when_having_max_count_set_for_stream.cs b/src/EventStore.Core.Tests/ClientAPI/when_having_max_count_set_for_stream.cs deleted file mode 100644 index b037129c28..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/when_having_max_count_set_for_stream.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_having_max_count_set_for_stream : SpecificationWithDirectory -{ - private const string Stream = "max-count-test-stream"; - - private MiniNode _node; - private IEventStoreConnection _connection; - private EventData[] _testEvents; - - [SetUp] - public override async Task SetUp() - { - await base.SetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - _connection = TestConnection.Create(_node.TcpEndPoint); - await _connection.ConnectAsync(); - - await _connection.SetStreamMetadataAsync(Stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetMaxCount(3)); - - _testEvents = Enumerable.Range(0, 5).Select(x => TestEvent.NewTestEvent(data: x.ToString())).ToArray(); - await _connection.AppendToStreamAsync(Stream, ExpectedVersion.NoStream, _testEvents); - } - - [TearDown] - public override async Task TearDown() - { - _connection.Close(); - await _node.Shutdown(); - await base.TearDown(); - } - - [Test] - public async Task read_stream_forward_respects_max_count() - { - var res = await _connection.ReadStreamEventsForwardAsync(Stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test] - public async Task read_stream_backward_respects_max_count() - { - var res = await _connection.ReadStreamEventsBackwardAsync(Stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } - - [Test] - public async Task after_setting_less_strict_max_count_read_stream_forward_reads_more_events() - { - var res = await _connection.ReadStreamEventsForwardAsync(Stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - - await _connection.SetStreamMetadataAsync(Stream, 0, StreamMetadata.Build().SetMaxCount(4)); - - res = await _connection.ReadStreamEventsForwardAsync(Stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(1).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test] - public async Task after_setting_more_strict_max_count_read_stream_forward_reads_less_events() - { - var res = await _connection.ReadStreamEventsForwardAsync(Stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - - await _connection.SetStreamMetadataAsync(Stream, 0, StreamMetadata.Build().SetMaxCount(2)); - - res = await _connection.ReadStreamEventsForwardAsync(Stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(3).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test] - public async Task after_setting_less_strict_max_count_read_stream_backward_reads_more_events() - { - var res = await _connection.ReadStreamEventsBackwardAsync(Stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - - await _connection.SetStreamMetadataAsync(Stream, 0, StreamMetadata.Build().SetMaxCount(4)); - - res = await _connection.ReadStreamEventsBackwardAsync(Stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(1).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } - - [Test] - public async Task after_setting_more_strict_max_count_read_stream_backward_reads_less_events() - { - var res = await _connection.ReadStreamEventsBackwardAsync(Stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - - await _connection.SetStreamMetadataAsync(Stream, 0, StreamMetadata.Build().SetMaxCount(2)); - - res = await _connection.ReadStreamEventsBackwardAsync(Stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(3).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/when_having_truncatebefore_set_for_stream.cs b/src/EventStore.Core.Tests/ClientAPI/when_having_truncatebefore_set_for_stream.cs deleted file mode 100644 index 69868b8ba3..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/when_having_truncatebefore_set_for_stream.cs +++ /dev/null @@ -1,379 +0,0 @@ -using System.Linq; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_having_truncatebefore_set_for_stream : SpecificationWithMiniNode -{ - private EventData[] _testEvents; - - protected override Task When() - { - _testEvents = Enumerable.Range(0, 5).Select(x => TestEvent.NewTestEvent(data: x.ToString())).ToArray(); - return Task.CompletedTask; - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task read_event_respects_truncatebefore() - { - const string stream = "read_event_respects_truncatebefore"; - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[2].EventId, res.Event.Value.OriginalEvent.EventId); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task read_stream_forward_respects_truncatebefore() - { - const string stream = "read_stream_forward_respects_truncatebefore"; - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task read_stream_backward_respects_truncatebefore() - { - const string stream = "read_stream_backward_respects_truncatebefore"; - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task after_setting_less_strict_truncatebefore_read_event_reads_more_events() - { - const string stream = "after_setting_less_strict_truncatebefore_read_event_reads_more_events"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[2].EventId, res.Event.Value.OriginalEvent.EventId); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(1)); - - res = await _conn.ReadEventAsync(stream, 0, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[1].EventId, res.Event.Value.OriginalEvent.EventId); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task after_setting_more_strict_truncatebefore_read_event_reads_less_events() - { - const string stream = "after_setting_more_strict_truncatebefore_read_event_reads_less_events"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[2].EventId, res.Event.Value.OriginalEvent.EventId); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(3)); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 3, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[3].EventId, res.Event.Value.OriginalEvent.EventId); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task less_strict_max_count_doesnt_change_anything_for_event_read() - { - const string stream = "less_strict_max_count_doesnt_change_anything_for_event_read"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[2].EventId, res.Event.Value.OriginalEvent.EventId); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(2).SetMaxCount(4)); - - res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[2].EventId, res.Event.Value.OriginalEvent.EventId); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task more_strict_max_count_gives_less_events_for_event_read() - { - const string stream = "more_strict_max_count_gives_less_events_for_event_read"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadEventAsync(stream, 1, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[2].EventId, res.Event.Value.OriginalEvent.EventId); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(2).SetMaxCount(2)); - - res = await _conn.ReadEventAsync(stream, 2, false); - Assert.AreEqual(EventReadStatus.NotFound, res.Status); - - res = await _conn.ReadEventAsync(stream, 3, false); - Assert.AreEqual(EventReadStatus.Success, res.Status); - Assert.AreEqual(_testEvents[3].EventId, res.Event.Value.OriginalEvent.EventId); - } - - - [Test, Category("LongRunning"), Category("Network")] - public async Task after_setting_less_strict_truncatebefore_read_stream_forward_reads_more_events() - { - const string stream = "after_setting_less_strict_truncatebefore_read_stream_forward_reads_more_events"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(1)); - - res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(1).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task after_setting_more_strict_truncatebefore_read_stream_forward_reads_less_events() - { - const string stream = "after_setting_more_strict_truncatebefore_read_stream_forward_reads_less_events"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(3)); - - res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(3).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task less_strict_max_count_doesnt_change_anything_for_stream_forward_read() - { - const string stream = "less_strict_max_count_doesnt_change_anything_for_stream_forward_read"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(2).SetMaxCount(4)); - - res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task more_strict_max_count_gives_less_events_for_stream_forward_read() - { - const string stream = "more_strict_max_count_gives_less_events_for_stream_forward_read"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(2).SetMaxCount(2)); - - res = await _conn.ReadStreamEventsForwardAsync(stream, 0, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(3).Select(x => x.EventId).ToArray(), - res.Events.Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task after_setting_less_strict_truncatebefore_read_stream_backward_reads_more_events() - { - const string stream = "after_setting_less_strict_truncatebefore_read_stream_backward_reads_more_events"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(1)); - - res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(4, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(1).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task after_setting_more_strict_truncatebefore_read_stream_backward_reads_less_events() - { - const string stream = "after_setting_more_strict_truncatebefore_read_stream_backward_reads_less_events"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(3)); - - res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(3).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task less_strict_max_count_doesnt_change_anything_for_stream_backward_read() - { - const string stream = "less_strict_max_count_doesnt_change_anything_for_stream_backward_read"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(2).SetMaxCount(4)); - - res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } - - [Test, Category("LongRunning"), Category("Network")] - public async Task more_strict_max_count_gives_less_events_for_stream_backward_read() - { - const string stream = "more_strict_max_count_gives_less_events_for_stream_backward_read"; - - await _conn.AppendToStreamAsync(stream, ExpectedVersion.NoStream, _testEvents); - - await _conn.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, - StreamMetadata.Build().SetTruncateBefore(2)); - - var res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(3, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(2).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - - await _conn.SetStreamMetadataAsync(stream, 0, StreamMetadata.Build().SetTruncateBefore(2).SetMaxCount(2)); - - res = await _conn.ReadStreamEventsBackwardAsync(stream, -1, 100, false); - Assert.AreEqual(SliceReadStatus.Success, res.Status); - Assert.AreEqual(2, res.Events.Length); - Assert.AreEqual(_testEvents.Skip(3).Select(x => x.EventId).ToArray(), - res.Events.Reverse().Select(x => x.Event.EventId).ToArray()); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/when_working_with_metadata.cs b/src/EventStore.Core.Tests/ClientAPI/when_working_with_metadata.cs deleted file mode 100644 index b876703470..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/when_working_with_metadata.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using Newtonsoft.Json.Linq; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_working_with_metadata : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - private IEventStoreConnection _connection; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - _connection = BuildConnection(_node); - await _connection.ConnectAsync(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - _connection.Close(); - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task when_getting_metadata_for_an_existing_stream_and_no_metadata_exists() - { - const string stream = "when_getting_metadata_for_an_existing_stream_and_no_metadata_exists"; - - await _connection.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(-1, meta.MetastreamVersion); - Assert.AreEqual(Helper.UTF8NoBom.GetBytes(""), meta.StreamMetadata); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_byte_array.cs b/src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_byte_array.cs deleted file mode 100644 index cb976f3b6f..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_byte_array.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Data; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_working_with_stream_metadata_as_byte_array : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - private IEventStoreConnection _connection; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - _connection = BuildConnection(_node); - await _connection.ConnectAsync(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - _connection.Close(); - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task setting_empty_metadata_works() - { - const string stream = "setting_empty_metadata_works"; - - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, (byte[])null); - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(new byte[0], meta.StreamMetadata); - } - - [Test] - public async Task setting_metadata_few_times_returns_last_metadata() - { - const string stream = "setting_metadata_few_times_returns_last_metadata"; - - var metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadataBytes); - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadataBytes, meta.StreamMetadata); - - metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, 0, metadataBytes); - meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(metadataBytes, meta.StreamMetadata); - } - - [Test] - public async Task trying_to_set_metadata_with_wrong_expected_version_fails() - { - const string stream = "trying_to_set_metadata_with_wrong_expected_version_fails"; - await AssertEx.ThrowsAsync(() => _connection.SetStreamMetadataAsync(stream, 5, new byte[100])); - } - - [Test] - public async Task setting_metadata_with_expected_version_any_works() - { - const string stream = "setting_metadata_with_expected_version_any_works"; - - var metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.Any, metadataBytes); - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadataBytes, meta.StreamMetadata); - - metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.Any, metadataBytes); - meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(metadataBytes, meta.StreamMetadata); - } - - [Test] - public async Task setting_metadata_for_not_existing_stream_works() - { - const string stream = "setting_metadata_for_not_existing_stream_works"; - var metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadataBytes); - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadataBytes, meta.StreamMetadata); - } - - [Test] - public async Task setting_metadata_for_existing_stream_works() - { - const string stream = "setting_metadata_for_existing_stream_works"; - - await _connection.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent(), - TestEvent.NewTestEvent()); - - var metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadataBytes); - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadataBytes, meta.StreamMetadata); - } - - [Test] - public async Task setting_metadata_for_deleted_stream_throws_stream_deleted_exception() - { - const string stream = "setting_metadata_for_deleted_stream_throws_stream_deleted_exception"; - - await _connection.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - var metadataBytes = Guid.NewGuid().ToByteArray(); - await AssertEx.ThrowsAsync( - () => _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadataBytes)); - } - - [Test] - public async Task getting_metadata_for_nonexisting_stream_returns_empty_byte_array() - { - const string stream = "getting_metadata_for_nonexisting_stream_returns_empty_byte_array"; - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(-1, meta.MetastreamVersion); - Assert.AreEqual(new byte[0], meta.StreamMetadata); - } - - [Test] - public async Task getting_metadata_for_deleted_stream_returns_empty_byte_array_and_signals_stream_deletion() - { - const string stream = - "getting_metadata_for_deleted_stream_returns_empty_byte_array_and_signals_stream_deletion"; - - var metadataBytes = Guid.NewGuid().ToByteArray(); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadataBytes); - - await _connection.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(true, meta.IsStreamDeleted); - Assert.AreEqual(EventNumber.DeletedStream, meta.MetastreamVersion); - Assert.AreEqual(new byte[0], meta.StreamMetadata); - } -} diff --git a/src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_structured_info.cs b/src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_structured_info.cs deleted file mode 100644 index 02f516d4c7..0000000000 --- a/src/EventStore.Core.Tests/ClientAPI/when_working_with_stream_metadata_as_structured_info.cs +++ /dev/null @@ -1,384 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using StreamMetadata = EventStore.ClientAPI.StreamMetadata; - -namespace EventStore.Core.Tests.ClientAPI; - -[Category("ClientAPI"), Category("LongRunning")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_working_with_stream_metadata_as_structured_info : SpecificationWithDirectoryPerTestFixture -{ - private MiniNode _node; - private IEventStoreConnection _connection; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - _node = new MiniNode(PathName); - await _node.Start(); - - _connection = BuildConnection(_node); - await _connection.ConnectAsync(); - } - - protected virtual IEventStoreConnection BuildConnection(MiniNode node) - { - return TestConnection.Create(node.TcpEndPoint); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - _connection.Close(); - await _node.Shutdown(); - await base.TestFixtureTearDown(); - } - - [Test] - public async Task setting_empty_metadata_works() - { - const string stream = "setting_empty_metadata_works"; - - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, StreamMetadata.Create()); - - var meta = await _connection.GetStreamMetadataAsRawBytesAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(Helper.UTF8NoBom.GetBytes("{}"), meta.StreamMetadata); - } - - [Test] - public async Task setting_metadata_few_times_returns_last_metadata_info() - { - const string stream = "setting_metadata_few_times_returns_last_metadata_info"; - var metadata = - StreamMetadata.Create(17, TimeSpan.FromSeconds(0xDEADBEEF), 10, TimeSpan.FromSeconds(0xABACABA)); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadata); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadata.MaxCount, meta.StreamMetadata.MaxCount); - Assert.AreEqual(metadata.MaxAge, meta.StreamMetadata.MaxAge); - Assert.AreEqual(metadata.TruncateBefore, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(metadata.CacheControl, meta.StreamMetadata.CacheControl); - - metadata = StreamMetadata.Create(37, TimeSpan.FromSeconds(0xBEEFDEAD), 24, - TimeSpan.FromSeconds(0xDABACABAD)); - await _connection.SetStreamMetadataAsync(stream, 0, metadata); - - meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(metadata.MaxCount, meta.StreamMetadata.MaxCount); - Assert.AreEqual(metadata.MaxAge, meta.StreamMetadata.MaxAge); - Assert.AreEqual(metadata.TruncateBefore, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(metadata.CacheControl, meta.StreamMetadata.CacheControl); - } - - [Test] - public async Task trying_to_set_metadata_with_wrong_expected_version_fails() - { - const string stream = "trying_to_set_metadata_with_wrong_expected_version_fails"; - await AssertEx.ThrowsAsync(() => - _connection.SetStreamMetadataAsync(stream, 2, StreamMetadata.Create())); - } - - [Test] - public async Task setting_metadata_with_expected_version_any_works() - { - const string stream = "setting_metadata_with_expected_version_any_works"; - var metadata = - StreamMetadata.Create(17, TimeSpan.FromSeconds(0xDEADBEEF), 10, TimeSpan.FromSeconds(0xABACABA)); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.Any, metadata); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadata.MaxCount, meta.StreamMetadata.MaxCount); - Assert.AreEqual(metadata.MaxAge, meta.StreamMetadata.MaxAge); - Assert.AreEqual(metadata.TruncateBefore, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(metadata.CacheControl, meta.StreamMetadata.CacheControl); - - metadata = StreamMetadata.Create(37, TimeSpan.FromSeconds(0xBEEFDEAD), 24, - TimeSpan.FromSeconds(0xDABACABAD)); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.Any, metadata); - - meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(1, meta.MetastreamVersion); - Assert.AreEqual(metadata.MaxCount, meta.StreamMetadata.MaxCount); - Assert.AreEqual(metadata.MaxAge, meta.StreamMetadata.MaxAge); - Assert.AreEqual(metadata.TruncateBefore, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(metadata.CacheControl, meta.StreamMetadata.CacheControl); - } - - [Test] - public async Task setting_metadata_for_not_existing_stream_works() - { - const string stream = "setting_metadata_for_not_existing_stream_works"; - var metadata = - StreamMetadata.Create(17, TimeSpan.FromSeconds(0xDEADBEEF), 10, TimeSpan.FromSeconds(0xABACABA)); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadata); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadata.MaxCount, meta.StreamMetadata.MaxCount); - Assert.AreEqual(metadata.MaxAge, meta.StreamMetadata.MaxAge); - Assert.AreEqual(metadata.TruncateBefore, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(metadata.CacheControl, meta.StreamMetadata.CacheControl); - } - - [Test] - public async Task setting_metadata_for_existing_stream_works() - { - const string stream = "setting_metadata_for_existing_stream_works"; - - await _connection.AppendToStreamAsync(stream, ExpectedVersion.NoStream, TestEvent.NewTestEvent()); - - var metadata = - StreamMetadata.Create(17, TimeSpan.FromSeconds(0xDEADBEEF), 10, TimeSpan.FromSeconds(0xABACABA)); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadata); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(metadata.MaxCount, meta.StreamMetadata.MaxCount); - Assert.AreEqual(metadata.MaxAge, meta.StreamMetadata.MaxAge); - Assert.AreEqual(metadata.TruncateBefore, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(metadata.CacheControl, meta.StreamMetadata.CacheControl); - } - - [Test] - public async Task getting_metadata_for_nonexisting_stream_returns_empty_stream_metadata() - { - const string stream = "getting_metadata_for_nonexisting_stream_returns_empty_stream_metadata"; - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(-1, meta.MetastreamVersion); - Assert.AreEqual(null, meta.StreamMetadata.MaxCount); - Assert.AreEqual(null, meta.StreamMetadata.MaxAge); - Assert.AreEqual(null, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(null, meta.StreamMetadata.CacheControl); - } - - [Test, Ignore("You can't get stream metadata for metastream through ClientAPI")] - public async Task getting_metadata_for_metastream_returns_correct_metadata() - { - const string stream = "$$getting_metadata_for_metastream_returns_correct_metadata"; - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(-1, meta.MetastreamVersion); - Assert.AreEqual(1, meta.StreamMetadata.MaxCount); - Assert.AreEqual(null, meta.StreamMetadata.MaxAge); - Assert.AreEqual(null, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(null, meta.StreamMetadata.CacheControl); - } - - [Test] - public async Task getting_metadata_for_deleted_stream_returns_empty_stream_metadata_and_signals_stream_deletion() - { - const string stream = - "getting_metadata_for_deleted_stream_returns_empty_stream_metadata_and_signals_stream_deletion"; - - var metadata = - StreamMetadata.Create(17, TimeSpan.FromSeconds(0xDEADBEEF), 10, TimeSpan.FromSeconds(0xABACABA)); - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadata); - - await _connection.DeleteStreamAsync(stream, ExpectedVersion.NoStream, hardDelete: true); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(true, meta.IsStreamDeleted); - Assert.AreEqual(EventNumber.DeletedStream, meta.MetastreamVersion); - Assert.AreEqual(null, meta.StreamMetadata.MaxCount); - Assert.AreEqual(null, meta.StreamMetadata.MaxAge); - Assert.AreEqual(null, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(null, meta.StreamMetadata.CacheControl); - Assert.AreEqual(null, meta.StreamMetadata.Acl); - } - - [Test] - public async Task setting_correctly_formatted_metadata_as_raw_allows_to_read_it_as_structured_metadata() - { - const string stream = - "setting_correctly_formatted_metadata_as_raw_allows_to_read_it_as_structured_metadata"; - - var rawMeta = Helper.UTF8NoBom.GetBytes(@"{ - ""$maxCount"": 17, - ""$maxAge"": 123321, - ""$tb"": 23, - ""$cacheControl"": 7654321, - ""$acl"": { - ""$r"": ""readRole"", - ""$w"": ""writeRole"", - ""$d"": ""deleteRole"", - ""$mw"": ""metaWriteRole"" - }, - ""customString"": ""a string"", - ""customInt"": -179, - ""customDouble"": 1.7, - ""customLong"": 123123123123123123, - ""customBool"": true, - ""customNullable"": null, - ""customRawJson"": { - ""subProperty"": 999 - } - }"); - - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, rawMeta); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(17, meta.StreamMetadata.MaxCount); - Assert.AreEqual(TimeSpan.FromSeconds(123321), meta.StreamMetadata.MaxAge); - Assert.AreEqual(23, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(TimeSpan.FromSeconds(7654321), meta.StreamMetadata.CacheControl); - - Assert.NotNull(meta.StreamMetadata.Acl); - Assert.AreEqual("readRole", meta.StreamMetadata.Acl.ReadRole); - Assert.AreEqual("writeRole", meta.StreamMetadata.Acl.WriteRole); - Assert.AreEqual("deleteRole", meta.StreamMetadata.Acl.DeleteRole); - // meta role removed to allow reading - // Assert.AreEqual("metaReadRole", meta.StreamMetadata.Acl.MetaReadRole); - Assert.AreEqual("metaWriteRole", meta.StreamMetadata.Acl.MetaWriteRole); - - Assert.AreEqual("a string", meta.StreamMetadata.GetValue("customString")); - Assert.AreEqual(-179, meta.StreamMetadata.GetValue("customInt")); - Assert.AreEqual(1.7, meta.StreamMetadata.GetValue("customDouble")); - Assert.AreEqual(123123123123123123L, meta.StreamMetadata.GetValue("customLong")); - Assert.AreEqual(true, meta.StreamMetadata.GetValue("customBool")); - Assert.AreEqual(null, meta.StreamMetadata.GetValue("customNullable")); - Assert.AreEqual(@"{""subProperty"":999}", meta.StreamMetadata.GetValueAsRawJsonString("customRawJson")); - } - - [Test] - public async Task setting_structured_metadata_with_custom_properties_returns_them_untouched() - { - const string stream = "setting_structured_metadata_with_custom_properties_returns_them_untouched"; - - StreamMetadata metadata = StreamMetadata.Build() - .SetMaxCount(17) - .SetMaxAge(TimeSpan.FromSeconds(123321)) - .SetTruncateBefore(23) - .SetCacheControl(TimeSpan.FromSeconds(7654321)) - .SetReadRole("readRole") - .SetWriteRole("writeRole") - .SetDeleteRole("deleteRole") - //.SetMetadataReadRole("metaReadRole") - .SetMetadataWriteRole("metaWriteRole") - .SetCustomProperty("customString", "a string") - .SetCustomProperty("customInt", -179) - .SetCustomProperty("customDouble", 1.7) - .SetCustomProperty("customLong", 123123123123123123L) - .SetCustomProperty("customBool", true) - .SetCustomProperty("customNullable", new int?()) - .SetCustomPropertyWithValueAsRawJsonString("customRawJson", - @"{ - ""subProperty"": 999 - }"); - - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadata); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - Assert.AreEqual(17, meta.StreamMetadata.MaxCount); - Assert.AreEqual(TimeSpan.FromSeconds(123321), meta.StreamMetadata.MaxAge); - Assert.AreEqual(23, meta.StreamMetadata.TruncateBefore); - Assert.AreEqual(TimeSpan.FromSeconds(7654321), meta.StreamMetadata.CacheControl); - - Assert.NotNull(meta.StreamMetadata.Acl); - Assert.AreEqual("readRole", meta.StreamMetadata.Acl.ReadRole); - Assert.AreEqual("writeRole", meta.StreamMetadata.Acl.WriteRole); - Assert.AreEqual("deleteRole", meta.StreamMetadata.Acl.DeleteRole); - //Assert.AreEqual("metaReadRole", meta.StreamMetadata.Acl.MetaReadRole); - Assert.AreEqual("metaWriteRole", meta.StreamMetadata.Acl.MetaWriteRole); - - Assert.AreEqual("a string", meta.StreamMetadata.GetValue("customString")); - Assert.AreEqual(-179, meta.StreamMetadata.GetValue("customInt")); - Assert.AreEqual(1.7, meta.StreamMetadata.GetValue("customDouble")); - Assert.AreEqual(123123123123123123L, meta.StreamMetadata.GetValue("customLong")); - Assert.AreEqual(true, meta.StreamMetadata.GetValue("customBool")); - Assert.AreEqual(null, meta.StreamMetadata.GetValue("customNullable")); - Assert.AreEqual(@"{""subProperty"":999}", meta.StreamMetadata.GetValueAsRawJsonString("customRawJson")); - } - - [Test] - public async Task setting_structured_metadata_with_multiple_roles_can_be_read_back() - { - const string stream = "setting_structured_metadata_with_multiple_roles_can_be_read_back"; - - StreamMetadata metadata = StreamMetadata.Build() - .SetReadRoles(new[] { "r1", "r2", "r3" }) - .SetWriteRoles(new[] { "w1", "w2" }) - .SetDeleteRoles(new[] { "d1", "d2", "d3", "d4" }) - .SetMetadataWriteRoles(new[] { "mw1", "mw2" }); - - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, metadata); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - - Assert.NotNull(meta.StreamMetadata.Acl); - Assert.AreEqual(new[] { "r1", "r2", "r3" }, meta.StreamMetadata.Acl.ReadRoles); - Assert.AreEqual(new[] { "w1", "w2" }, meta.StreamMetadata.Acl.WriteRoles); - Assert.AreEqual(new[] { "d1", "d2", "d3", "d4" }, meta.StreamMetadata.Acl.DeleteRoles); - Assert.AreEqual(new[] { "mw1", "mw2" }, meta.StreamMetadata.Acl.MetaWriteRoles); - } - - [Test] - public async Task setting_correct_metadata_with_multiple_roles_in_acl_allows_to_read_it_as_structured_metadata() - { - const string stream = - "setting_correct_metadata_with_multiple_roles_in_acl_allows_to_read_it_as_structured_metadata"; - - var rawMeta = Helper.UTF8NoBom.GetBytes(@"{ - ""$acl"": { - ""$r"": [""r1"", ""r2"", ""r3""], - ""$w"": [""w1"", ""w2""], - ""$d"": [""d1"", ""d2"", ""d3"", ""d4""], - ""$mw"": [""mw1"", ""mw2""], - } - }"); - - await _connection.SetStreamMetadataAsync(stream, ExpectedVersion.NoStream, rawMeta); - - var meta = await _connection.GetStreamMetadataAsync(stream); - Assert.AreEqual(stream, meta.Stream); - Assert.AreEqual(false, meta.IsStreamDeleted); - Assert.AreEqual(0, meta.MetastreamVersion); - - Assert.NotNull(meta.StreamMetadata.Acl); - Assert.AreEqual(new[] { "r1", "r2", "r3" }, meta.StreamMetadata.Acl.ReadRoles); - Assert.AreEqual(new[] { "w1", "w2" }, meta.StreamMetadata.Acl.WriteRoles); - Assert.AreEqual(new[] { "d1", "d2", "d3", "d4" }, meta.StreamMetadata.Acl.DeleteRoles); - Assert.AreEqual(new[] { "mw1", "mw2" }, meta.StreamMetadata.Acl.MetaWriteRoles); - } -} diff --git a/src/EventStore.Core.Tests/ClientOperations/specification_with_bare_vnode.cs b/src/EventStore.Core.Tests/ClientOperations/specification_with_bare_vnode.cs index 12331d412f..806e90125d 100644 --- a/src/EventStore.Core.Tests/ClientOperations/specification_with_bare_vnode.cs +++ b/src/EventStore.Core.Tests/ClientOperations/specification_with_bare_vnode.cs @@ -9,7 +9,7 @@ using EventStore.Core.Bus; using EventStore.Core.Certificates; using EventStore.Core.Messaging; -using EventStore.Core.Tests.Services.Transport.Tcp; +using EventStore.Core.Tests.Helpers; using Microsoft.AspNetCore.Builder; namespace EventStore.Core.Tests.ClientOperations; @@ -26,8 +26,8 @@ public void CreateTestNode() var options = new ClusterVNodeOptions() .ReduceMemoryUsageForTests() .RunOnDisk(_dbPath) - .Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate()); + .Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate()); _node = new ClusterVNode(options, logFormatFactory, new AuthenticationProviderFactory( c => new InternalAuthenticationProviderFactory(c, options.DefaultUser)), diff --git a/src/EventStore.Core.Tests/ClientOperations/when_committing_a_transaction_with_data.cs b/src/EventStore.Core.Tests/ClientOperations/when_committing_a_transaction_with_data.cs index 94d3acd5a1..d4e1311df3 100644 --- a/src/EventStore.Core.Tests/ClientOperations/when_committing_a_transaction_with_data.cs +++ b/src/EventStore.Core.Tests/ClientOperations/when_committing_a_transaction_with_data.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Threading; -using EventStore.ClientAPI.Common.Utils; +using EventStore.Common.Utils; using EventStore.Core.Data; using EventStore.Core.Messages; using EventStore.Core.Messaging; diff --git a/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs b/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs index 14d94644ce..ddab6eaabe 100644 --- a/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs +++ b/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs @@ -16,12 +16,7 @@ public void member_with_dns_endpoint_should_equal() var memberWithDnsEndPoint = EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - new DnsEndPoint(ipAddress, port), - null, 0, 0, - 0, false); + null, 0, 0, false); var ipEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); var dnsEndPoint = new DnsEndPoint(ipAddress, port); @@ -38,11 +33,7 @@ public void member_with_ip_endpoint_should_equal() var memberWithDnsEndPoint = EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - null, 0, 0, 0, false); + null, 0, 0, false); var ipEndPoint = new IPEndPoint(IPAddress.Parse(ipAddress), port); var dnsEndPoint = new DnsEndPoint(ipAddress, port); diff --git a/src/EventStore.Core.Tests/DefaultData.cs b/src/EventStore.Core.Tests/DefaultData.cs index 3c647b08fc..262f7312dc 100644 --- a/src/EventStore.Core.Tests/DefaultData.cs +++ b/src/EventStore.Core.Tests/DefaultData.cs @@ -1,5 +1,4 @@ using System.Net; -using EventStore.ClientAPI.SystemData; using EventStore.Core.Services; namespace EventStore.Core.Tests; @@ -8,7 +7,6 @@ public class DefaultData { public static string AdminUsername = SystemUsers.Admin; public static string AdminPassword = SystemUsers.DefaultAdminPassword; - public static UserCredentials AdminCredentials = new UserCredentials(AdminUsername, AdminPassword); public static NetworkCredential AdminNetworkCredentials = new NetworkCredential(AdminUsername, AdminPassword); public static ClusterVNodeOptions.DefaultUserOptions DefaultUserOptions = new ClusterVNodeOptions.DefaultUserOptions() { diff --git a/src/EventStore.Core.Tests/EventStore.Core.Tests.csproj b/src/EventStore.Core.Tests/EventStore.Core.Tests.csproj index 3be13ef772..84a5cbb4c1 100644 --- a/src/EventStore.Core.Tests/EventStore.Core.Tests.csproj +++ b/src/EventStore.Core.Tests/EventStore.Core.Tests.csproj @@ -4,7 +4,6 @@ true - @@ -28,11 +27,8 @@ - - - diff --git a/src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs b/src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs deleted file mode 100644 index 862b03493f..0000000000 --- a/src/EventStore.Core.Tests/Helpers/ClientApiLoggerBridge.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Tests.Helpers; - -public class ClientApiLoggerBridge : EventStore.ClientAPI.ILogger -{ - public static readonly ClientApiLoggerBridge Default = - new ClientApiLoggerBridge(Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, - "client-api")); - - private readonly Serilog.ILogger _log; - - public ClientApiLoggerBridge(ILogger log) - { - Ensure.NotNull(log, "log"); - _log = log; - } - - public void Error(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Error(format); - } - else - { - _log.Error(format, args); - } - } - - public void Error(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Error(ex, format); - } - else - { - _log.Error(ex, format, args); - } - } - - public void Info(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Information(format); - } - else - { - _log.Information(format, args); - } - } - - public void Info(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Information(ex, format); - } - else - { - _log.Information(ex, format, args); - } - } - - public void Debug(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Debug(format); - } - else - { - _log.Debug(format, args); - } - } - - public void Debug(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Debug(ex, format); - } - else - { - _log.Debug(ex, format, args); - } - } -} diff --git a/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs b/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs index 444ab592d2..c36b939bca 100644 --- a/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs +++ b/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs @@ -20,10 +20,8 @@ using EventStore.Core.Services.Monitoring; using EventStore.Core.Services.PersistentSubscription.ConsumerStrategy; using EventStore.Core.Services.Storage.ReaderIndex; -using EventStore.Core.Tests.Services.Transport.Tcp; using EventStore.Core.TransactionLog.Chunks; using EventStore.Plugins.Subsystems; -using EventStore.TcpUnitTestPlugin; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.Configuration; @@ -42,8 +40,6 @@ public class MiniClusterNode private static readonly ILogger Log = Serilog.Log.ForContext>(); - public IPEndPoint InternalTcpEndPoint { get; } - public IPEndPoint ExternalTcpEndPoint { get; } public IPEndPoint HttpEndPoint { get; } public readonly int DebugIndex; @@ -62,11 +58,11 @@ public class MiniClusterNode public VNodeState NodeState = VNodeState.Unknown; private readonly IHost _host; - public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, IPEndPoint externalTcp, - IPEndPoint httpEndPoint, EndPoint[] gossipSeeds, ISubsystem[] subsystems = null, + public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, + EndPoint[] gossipSeeds, ISubsystem[] subsystems = null, bool enableTrustedAuth = false, int memTableSize = 1000, bool disableFlushToDisk = false, bool readOnlyReplica = false, int nodePriority = 0, - string intHostAdvertiseAs = null, IExpiryStrategy expiryStrategy = null, + IExpiryStrategy expiryStrategy = null, ArchiveOptions archiveOptions = null, bool archiver = false, int clusterSize = 3, bool unsafeAllowSurplusNodes = false) { @@ -75,19 +71,16 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, RunCount += 1; DebugIndex = debugIndex; - InternalTcpEndPoint = internalTcp; - ExternalTcpEndPoint = externalTcp; - HttpEndPoint = httpEndPoint; + HttpEndPoint = nodeEndPoint; _dbPath = Path.Combine( pathname, - $"mini-cluster-node-db-{externalTcp.Port}-{httpEndPoint.Port}"); + $"mini-cluster-node-db-{nodeEndPoint.Port}"); Directory.CreateDirectory(_dbPath); FileStreamExtensions.ConfigureFlush(disableFlushToDisk); subsystems ??= []; - subsystems = [.. subsystems, new TcpApiTestPlugin()]; var options = new ClusterVNodeOptions { @@ -117,14 +110,9 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, }, Interface = new() { - ReplicationIp = InternalTcpEndPoint.Address, - NodeIp = ExternalTcpEndPoint.Address, - ReplicationPort = InternalTcpEndPoint.Port, + NodeIp = HttpEndPoint.Address, NodePort = HttpEndPoint.Port, - ReplicationHeartbeatTimeout = 2_000, - ReplicationHeartbeatInterval = 2_000, - EnableTrustedAuth = enableTrustedAuth, - ReplicationHostAdvertiseAs = intHostAdvertiseAs + EnableTrustedAuth = enableTrustedAuth }, Database = new() { @@ -149,14 +137,7 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, PlugableComponents = subsystems }; - var configuration = new List> { - new("EventStore:TcpPlugin:NodeTcpPort", externalTcp.Port.ToString()), - new("EventStore:TcpPlugin:EnableExternalTcp", "true"), - new("EventStore:TcpUnitTestPlugin:NodeTcpPort", externalTcp.Port.ToString()), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatInterval", "10000"), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatTimeout", "10000"), - new("EventStore:TcpUnitTestPlugin:Insecure", options.Application.Insecure.ToString()), - }; + var configuration = new List>(); if (archiveOptions is not null) { @@ -176,9 +157,9 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, var inMemConf = new ConfigurationBuilder() .AddInMemoryCollection(configuration) .Build(); - var serverCertificate = ssl_connections.GetServerCertificate(); + var serverCertificate = TestCertificates.GetServerCertificate(); var trustedRootCertificates = - new X509Certificate2Collection(ssl_connections.GetRootCertificate()); + new X509Certificate2Collection(TestCertificates.GetRootCertificate()); options = options.Secure(trustedRootCertificates, serverCertificate); _isReadOnlyReplica = readOnlyReplica; @@ -191,8 +172,8 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint internalTcp, RuntimeInformation.RuntimeMode, "GC:", GC.MaxGeneration == 0 ? "NON-GENERATION (PROBABLY BOEHM)" - : $"{GC.MaxGeneration + 1} GENERATIONS", "DBPATH:", _dbPath, "ExTCP ENDPOINT:", - ExternalTcpEndPoint, "ExHTTP ENDPOINT:", HttpEndPoint); + : $"{GC.MaxGeneration + 1} GENERATIONS", "DBPATH:", _dbPath, "NODE ENDPOINT:", + HttpEndPoint, "HTTP ENDPOINT:", HttpEndPoint); var logFormatFactory = LogFormatHelper.LogFormatFactory; Node = new ClusterVNode(options, logFormatFactory, new AuthenticationProviderFactory( diff --git a/src/EventStore.Core.Tests/Helpers/MiniNode.cs b/src/EventStore.Core.Tests/Helpers/MiniNode.cs index 9a41dc772b..95dd4d7d5e 100644 --- a/src/EventStore.Core.Tests/Helpers/MiniNode.cs +++ b/src/EventStore.Core.Tests/Helpers/MiniNode.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Net; using System.Net.Http; -using System.Net.Sockets; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using EventStore.Common.Utils; @@ -20,13 +19,11 @@ using EventStore.Core.Services.Monitoring; using EventStore.Core.Services.Storage.ReaderIndex; using EventStore.Core.Tests.Index.Hashers; -using EventStore.Core.Tests.Services.Transport.Tcp; using EventStore.Core.TransactionLog.Chunks; using EventStore.Plugins.Authentication; using EventStore.Plugins.Authorization; using EventStore.Plugins.Subsystems; using EventStore.Plugins.Transforms; -using EventStore.TcpUnitTestPlugin; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Https; @@ -45,8 +42,6 @@ public class MiniNode public const int CachedChunkSize = ChunkSize + ChunkHeader.Size + ChunkFooter.Size; protected static readonly ILogger Log = Serilog.Log.ForContext(); - public IPEndPoint TcpEndPoint { get; protected set; } - public IPEndPoint IntTcpEndPoint { get; protected set; } public IPEndPoint HttpEndPoint { get; protected set; } } @@ -71,7 +66,7 @@ public class MiniNode : MiniNode, IAsyncDisposable public Task AdminUserCreated => _adminUserCreated.Task; public MiniNode(string pathname, - int? tcpPort = null, int? httpPort = null, + int? httpPort = null, ISubsystem[] subsystems = null, int chunkSize = ChunkSize, int cachedChunkSize = CachedChunkSize, bool enableTrustedAuth = false, int memTableSize = 1000, @@ -97,26 +92,21 @@ public MiniNode(string pathname, var ip = IPAddress.Loopback; - int extTcpPort = tcpPort ?? PortsHelper.GetAvailablePort(ip); int httpEndPointPort = httpPort ?? PortsHelper.GetAvailablePort(ip); - int intTcpPort = PortsHelper.GetAvailablePort(ip); if (string.IsNullOrEmpty(dbPath)) { DbPath = Path.Combine(pathname, - $"mini-node-db-{extTcpPort}-{httpEndPointPort}"); + $"mini-node-db-{httpEndPointPort}"); } else { DbPath = dbPath; } - TcpEndPoint = new IPEndPoint(ip, extTcpPort); - IntTcpEndPoint = new IPEndPoint(ip, intTcpPort); HttpEndPoint = new IPEndPoint(ip, httpEndPointPort); subsystems ??= []; - subsystems = [.. subsystems, new TcpApiTestPlugin()]; var options = new ClusterVNodeOptions { @@ -130,8 +120,6 @@ public MiniNode(string pathname, }, Interface = new() { - ReplicationHeartbeatInterval = 10_000, - ReplicationHeartbeatTimeout = 10_000, EnableTrustedAuth = enableTrustedAuth }, Cluster = new() @@ -162,21 +150,11 @@ public MiniNode(string pathname, LoadedOptions = ClusterVNodeOptions.GetLoadedOptions(new ConfigurationBuilder() .AddEventStoreDefaultValues() .Build()), - }.Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate()) - .WithReplicationEndpointOn(IntTcpEndPoint) - .WithExternalTcpOn(TcpEndPoint) + }.Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate()) .WithNodeEndpointOn(HttpEndPoint); - var inMemConf = new ConfigurationBuilder() - .AddInMemoryCollection(new KeyValuePair[] { - new("EventStore:TcpPlugin:NodeTcpPort", extTcpPort.ToString()), - new("EventStore:TcpPlugin:EnableExternalTcp", "true"), - new("EventStore:TcpUnitTestPlugin:NodeTcpPort", extTcpPort.ToString()), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatInterval", "10000"), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatTimeout", "10000"), - new("EventStore:TcpUnitTestPlugin:Insecure", options.Application.Insecure.ToString()), - }).Build(); + var inMemConf = new ConfigurationBuilder().Build(); if (advertisedExtHostAddress != null) { @@ -200,7 +178,7 @@ public MiniNode(string pathname, ? "NON-GENERATION (PROBABLY BOEHM)" : $"{GC.MaxGeneration + 1} GENERATIONS", "DBPATH:", DbPath, - "TCP ENDPOINT:", TcpEndPoint, + "NODE ENDPOINT:", HttpEndPoint, "HTTP ENDPOINT:", HttpEndPoint); var logFormatFactory = LogFormatHelper.LogFormatFactory @@ -244,12 +222,12 @@ public MiniNode(string pathname, { options.UseHttps(new HttpsConnectionAdapterOptions { - ServerCertificate = ssl_connections.GetServerCertificate(), + ServerCertificate = TestCertificates.GetServerCertificate(), ClientCertificateMode = ClientCertificateMode.AllowCertificate, ClientCertificateValidation = (certificate, chain, sslPolicyErrors) => { var (isValid, error) = - ClusterVNode.ValidateClientCertificate(certificate, chain, sslPolicyErrors, () => null, () => new X509Certificate2Collection(ssl_connections.GetRootCertificate())); + ClusterVNode.ValidateClientCertificate(certificate, chain, sslPolicyErrors, () => null, () => new X509Certificate2Collection(TestCertificates.GetRootCertificate())); if (!isValid && error != null) { Log.Error("Client certificate validation error: {e}", error); @@ -335,25 +313,6 @@ void WaitForAdminUser(StorageMessage.EventCommitted m) Log.Information("MiniNode successfully started!"); } - public async Task WaitForTcpEndPoint() - { - while (true) - { - using var client = new TcpClient(); - - try - { - await client.ConnectAsync(TcpEndPoint.Address, TcpEndPoint.Port) - .WaitAsync(TimeSpan.FromMilliseconds(250)); - return; - } - catch (Exception ex) when (ex is SocketException or TimeoutException) - { - await Task.Delay(100); - } - } - } - public async Task Shutdown(bool keepDb = false) { diff --git a/src/EventStore.Core.Tests/Helpers/TestCertificates.cs b/src/EventStore.Core.Tests/Helpers/TestCertificates.cs new file mode 100644 index 0000000000..2014a19fb9 --- /dev/null +++ b/src/EventStore.Core.Tests/Helpers/TestCertificates.cs @@ -0,0 +1,66 @@ +using System; +using System.Net; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace EventStore.Core.Tests.Helpers; + +public static class TestCertificates +{ + private static readonly X509Certificate2 Root = CreateRootCertificate("Test Root CA"); + private static readonly X509Certificate2 Server = CreateServerCertificate("localhost", Root); + private static readonly X509Certificate2 OtherServer = CreateServerCertificate("other-node", Root); + private static readonly X509Certificate2 UntrustedRoot = CreateRootCertificate("Untrusted Test Root CA"); + private static readonly X509Certificate2 Untrusted = CreateServerCertificate("untrusted", UntrustedRoot); + + public static X509Certificate2 GetRootCertificate() => + X509CertificateLoader.LoadCertificate(Root.Export(X509ContentType.Cert)); + + public static X509Certificate2 GetServerCertificate() => CloneWithPrivateKey(Server); + + public static X509Certificate2 GetOtherServerCertificate() => CloneWithPrivateKey(OtherServer); + + public static X509Certificate2 GetUntrustedCertificate() => CloneWithPrivateKey(Untrusted); + + private static X509Certificate2 CreateRootCertificate(string commonName) + { + using var key = RSA.Create(2048); + var request = new CertificateRequest( + $"CN={commonName}", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, false, 0, true)); + request.CertificateExtensions.Add(new X509KeyUsageExtension( + X509KeyUsageFlags.KeyCertSign | X509KeyUsageFlags.CrlSign, true)); + request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false)); + + using var certificate = request.CreateSelfSigned( + DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddYears(1)); + return CloneWithPrivateKey(certificate); + } + + private static X509Certificate2 CreateServerCertificate(string commonName, X509Certificate2 issuer) + { + using var key = RSA.Create(2048); + var request = new CertificateRequest( + $"CN={commonName}", key, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); + request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, true)); + request.CertificateExtensions.Add(new X509KeyUsageExtension( + X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, true)); + request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension( + [new Oid("1.3.6.1.5.5.7.3.1"), new Oid("1.3.6.1.5.5.7.3.2")], true)); + var names = new SubjectAlternativeNameBuilder(); + names.AddDnsName(commonName); + names.AddDnsName("localhost"); + names.AddIpAddress(IPAddress.Loopback); + request.CertificateExtensions.Add(names.Build()); + + var serial = RandomNumberGenerator.GetBytes(16); + using var certificate = request.Create( + issuer, DateTimeOffset.UtcNow.AddDays(-1), DateTimeOffset.UtcNow.AddMonths(6), serial); + using var certificateWithKey = certificate.CopyWithPrivateKey(key); + return CloneWithPrivateKey(certificateWithKey); + } + + private static X509Certificate2 CloneWithPrivateKey(X509Certificate2 certificate) => + X509CertificateLoader.LoadPkcs12( + certificate.Export(X509ContentType.Pkcs12), string.Empty, X509KeyStorageFlags.Exportable); +} diff --git a/src/EventStore.Core.Tests/Helpers/TestFixtureWithExistingEvents.cs b/src/EventStore.Core.Tests/Helpers/TestFixtureWithExistingEvents.cs index 2544afaf57..af29a907ef 100644 --- a/src/EventStore.Core.Tests/Helpers/TestFixtureWithExistingEvents.cs +++ b/src/EventStore.Core.Tests/Helpers/TestFixtureWithExistingEvents.cs @@ -534,7 +534,7 @@ private void ProcessWrite(IEnvelope envelope, Guid correlationId, string stre _streams[streamId] = list; } - if (expectedVersion != EventStore.ClientAPI.ExpectedVersion.Any) + if (expectedVersion != EventStore.Core.Data.ExpectedVersion.Any) { if (expectedVersion != list.Count - 1) { diff --git a/src/EventStore.Core.Tests/Http/HealthChecks/when_performing_a_live_check.cs b/src/EventStore.Core.Tests/Http/HealthChecks/when_performing_a_live_check.cs index 8b64f5953c..27087c5e43 100644 --- a/src/EventStore.Core.Tests/Http/HealthChecks/when_performing_a_live_check.cs +++ b/src/EventStore.Core.Tests/Http/HealthChecks/when_performing_a_live_check.cs @@ -1,8 +1,6 @@ using System; using System.Net.Http; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; using EventStore.Core.Tests.Helpers; using Grpc.Health.V1; using Grpc.Net.Client; @@ -128,11 +126,5 @@ private async Task StartNodeAndWaitForReadiness() { await _node.Start(); _nodeStarted = true; - await _node.WaitForTcpEndPoint().WithTimeout(ReadinessTimeout); - - using var connection = await TestConnectionLifecycle.ReconnectUntilReady( - () => TestConnection.CreateMiniNodeClient(_node.TcpEndPoint), - conn => conn.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - ReadinessTimeout); } } diff --git a/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs b/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs index 781da7f1ce..e4c7e658db 100644 --- a/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs +++ b/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs @@ -2,11 +2,12 @@ using System.IO; using System.Linq; using System.Net; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Amazon.S3; using Amazon.S3.Model; -using EventStore.ClientAPI; +using EventStore.Client.Streams; using EventStore.Core.Data; using EventStore.Core.Messages; using EventStore.Core.Messaging; @@ -18,7 +19,11 @@ using EventStore.Core.Tests.Helpers; using EventStore.Core.TransactionLog.Chunks.TFChunk; using EventStore.Core.TransactionLog.FileNamingStrategy; +using Google.Protobuf; +using Grpc.Net.Client; using NUnit.Framework; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; +using StreamsClient = EventStore.Client.Streams.Streams.StreamsClient; namespace EventStore.Core.Tests.Integration.Archive; @@ -44,6 +49,8 @@ public class when_archiving_and_restoring_a_cluster private long _archivedCheckpoint; private int _restoredNodeIndex; private int _completedIterations; + private GrpcChannel _channel; + private StreamsClient _client; protected override int NodeCount => 4; protected override TimeSpan GivenTimeout => SoakTimeout; @@ -117,19 +124,12 @@ protected override MiniClusterNode CreateNode( new( PathName, index, - endpoints.InternalTcp, - endpoints.ExternalTcp, - endpoints.HttpEndPoint, + endpoints.NodeEndPoint, gossipSeeds, readOnlyReplica: index == ArchiverNodeIndex, archiveOptions: _archiveOptions.Enabled ? _archiveOptions : null, archiver: index == ArchiverNodeIndex); - protected override IEventStoreConnection CreateConnection() => - EventStoreConnection.Create( - ConnectionSettings.Create().DisableServerCertificateValidation(), - GetLeader().ExternalTcpEndPoint); - protected override async Task Given() { var payload = new byte[256 * 1024]; @@ -141,10 +141,30 @@ protected override async Task Given() var leader = await ReconnectToLeader(); for (var eventNumber = 0; eventNumber < EventsPerIteration; eventNumber++) { - await _conn.AppendToStreamAsync( - Stream, - EventStore.ClientAPI.ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "archive-event", isJson: false, payload, Array.Empty())); + using var call = _client.Append(); + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(Stream) } + } + }); + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Core.Services.Transport.Grpc.Uuid.NewUuid().ToDto(), + Data = ByteString.CopyFrom(payload), + CustomMetadata = ByteString.Empty, + Metadata = { + { GrpcMetadata.Type, "archive-event" }, + { GrpcMetadata.ContentType, GrpcMetadata.ContentTypes.ApplicationOctetStream } + } + } + }); + await call.RequestStream.CompleteAsync(); + await call.ResponseAsync; } AssertEx.IsOrBecomesTrue( @@ -175,11 +195,16 @@ await _conn.AppendToStreamAsync( private async Task> ReconnectToLeader() { var leader = GetLeader(); - _conn?.Close(); - _conn = EventStoreConnection.Create( - ConnectionSettings.Create().DisableServerCertificateValidation(), - leader.ExternalTcpEndPoint); - await _conn.ConnectAsync(); + _channel?.Dispose(); + _channel = GrpcChannel.ForAddress(new Uri($"https://{leader.HttpEndPoint}"), + new GrpcChannelOptions + { + HttpHandler = new SocketsHttpHandler + { + SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } } + } + }); + _client = new StreamsClient(_channel); return leader; } @@ -235,7 +260,7 @@ private async Task RestoreNode(int nodeIndex, int coldChunkNumber) private EndPoint[] GossipSeedsFor(int nodeIndex) => _nodeEndpoints .Where((_, index) => index != nodeIndex) - .Select(x => (EndPoint)x.HttpEndPoint) + .Select(x => (EndPoint)x.NodeEndPoint) .ToArray(); private async Task WaitForArchiveCheckpoint(long minimum) @@ -258,6 +283,7 @@ private async Task WaitForArchiveCheckpoint(long minimum) [OneTimeTearDown] public override async Task TestFixtureTearDown() { + _channel?.Dispose(); await base.TestFixtureTearDown(); if (_s3Client is null) { diff --git a/src/EventStore.Core.Tests/Integration/authenticated_requests_made_from_a_follower.cs b/src/EventStore.Core.Tests/Integration/authenticated_requests_made_from_a_follower.cs index e90fdf01ab..884b7614db 100644 --- a/src/EventStore.Core.Tests/Integration/authenticated_requests_made_from_a_follower.cs +++ b/src/EventStore.Core.Tests/Integration/authenticated_requests_made_from_a_follower.cs @@ -4,8 +4,6 @@ using System.Threading.Tasks; using EventStore.Client; using EventStore.Client.Streams; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; using EventStore.Core.Services.Transport.Grpc; using Google.Protobuf; using Grpc.Core; @@ -94,38 +92,4 @@ await call.RequestStream.WriteAsync(new AppendReq public void work() => Assert.AreEqual(StatusCode.OK, _status.StatusCode); } - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class via_tcp_should : authenticated_requests_made_from_a_follower - { - private Exception _caughtException; - - protected override async Task Given() - { - var node = GetFollowers()[0]; - await Task.WhenAll(node.AdminUserCreated, node.Started); - - using var connection = EventStoreConnection.Create(ConnectionSettings.Create() - .DisableServerCertificateValidation() - .PreferFollowerNode(), - node.ExternalTcpEndPoint); - await connection.ConnectAsync(); - - try - { - await connection.AppendToStreamAsync(ProtectedStream, ExpectedVersion.NoStream, - new UserCredentials("admin", "changeit"), - new EventData(Guid.NewGuid(), "-", false, Array.Empty(), Array.Empty())); - } - catch (Exception ex) - { - _caughtException = ex; - } - - await base.Given(); - } - - [Test] - [Retry(5)] - public void work() => Assert.Null(_caughtException); - } } diff --git a/src/EventStore.Core.Tests/Integration/specification_with_a_single_node.cs b/src/EventStore.Core.Tests/Integration/specification_with_a_single_node.cs index 7d264ab04c..f957746413 100644 --- a/src/EventStore.Core.Tests/Integration/specification_with_a_single_node.cs +++ b/src/EventStore.Core.Tests/Integration/specification_with_a_single_node.cs @@ -1,10 +1,7 @@ using System; using System.IO; -using System.Net; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; using EventStore.Core.Bus; using EventStore.Core.Tests.Helpers; using NUnit.Framework; diff --git a/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs b/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs index 6b8aa33884..b225f884aa 100644 --- a/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs +++ b/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs @@ -4,7 +4,6 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using EventStore.ClientAPI; using EventStore.Core.Data; using EventStore.Core.Tests.Helpers; using EventStore.Plugins.Subsystems; @@ -17,7 +16,6 @@ public abstract class specification_with_cluster : Specif { protected MiniClusterNode[] _nodes; protected Endpoints[] _nodeEndpoints; - protected IEventStoreConnection _conn; protected virtual TimeSpan GivenTimeout { get; } = TimeSpan.FromMinutes(2); protected virtual int NodeCount => 3; @@ -25,15 +23,11 @@ public abstract class specification_with_cluster : Specif protected class Endpoints { - public readonly IPEndPoint InternalTcp; - public readonly IPEndPoint ExternalTcp; - public readonly IPEndPoint HttpEndPoint; + public readonly IPEndPoint NodeEndPoint; public IEnumerable Ports() { - yield return InternalTcp.Port; - yield return ExternalTcp.Port; - yield return HttpEndPoint.Port; + yield return NodeEndPoint.Port; } private readonly List _sockets; @@ -44,21 +38,11 @@ public Endpoints() var defaultLoopBack = new IPEndPoint(IPAddress.Loopback, 0); - var internalTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - internalTcp.Bind(defaultLoopBack); - _sockets.Add(internalTcp); + var nodeEndpoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + nodeEndpoint.Bind(defaultLoopBack); + _sockets.Add(nodeEndpoint); - var externalTcp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - externalTcp.Bind(defaultLoopBack); - _sockets.Add(externalTcp); - - var httpEndPoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - httpEndPoint.Bind(defaultLoopBack); - _sockets.Add(httpEndPoint); - - InternalTcp = CopyEndpoint((IPEndPoint)internalTcp.LocalEndPoint); - ExternalTcp = CopyEndpoint((IPEndPoint)externalTcp.LocalEndPoint); - HttpEndPoint = CopyEndpoint((IPEndPoint)httpEndPoint.LocalEndPoint); + NodeEndPoint = CopyEndpoint((IPEndPoint)nodeEndpoint.LocalEndPoint); } public void DisposeSockets() @@ -102,7 +86,7 @@ public override async Task TestFixtureSetUp() nodeIndex, _nodeEndpoints[nodeIndex], _nodeEndpoints.Where((_, otherIndex) => otherIndex != nodeIndex) - .Select(x => (EndPoint)x.HttpEndPoint) + .Select(x => (EndPoint)x.NodeEndPoint) .ToArray(), wait)); _nodes[nodeIndex] = _nodeCreationFactory[nodeIndex](true); @@ -144,9 +128,6 @@ public override async Task TestFixtureSetUp() onFail: MiniNodeLogging.WriteLogs, msg: $"Waiting for followers timed out! States={string.Join(", ", _nodes.Select(n => n.NodeState))}"); - _conn = CreateConnection(); - await _conn.ConnectAsync(); - try { await Given().WithTimeout(GivenTimeout); @@ -158,9 +139,6 @@ public override async Task TestFixtureSetUp() } } - protected virtual IEventStoreConnection CreateConnection() => - EventStoreConnection.Create(_nodes[0].ExternalTcpEndPoint); - protected virtual void BeforeNodesStart() { } @@ -171,8 +149,7 @@ protected virtual void BeforeNodesStart() protected virtual MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds, bool wait = true) => new( - PathName, index, endpoints.InternalTcp, - endpoints.ExternalTcp, endpoints.HttpEndPoint, + PathName, index, endpoints.NodeEndPoint, subsystems: Array.Empty(), gossipSeeds: gossipSeeds); [TearDown] @@ -187,7 +164,6 @@ public void AfterEachTest() [OneTimeTearDown] public override async Task TestFixtureTearDown() { - _conn?.Close(); if (_nodes is not null) { await Task.WhenAll(_nodes.Where(node => node is not null).Select(node => node.Shutdown())); diff --git a/src/EventStore.Core.Tests/Integration/when_a_single_node_is_restarted_multiple_times.cs b/src/EventStore.Core.Tests/Integration/when_a_single_node_is_restarted_multiple_times.cs index b132166315..b7db44cba5 100644 --- a/src/EventStore.Core.Tests/Integration/when_a_single_node_is_restarted_multiple_times.cs +++ b/src/EventStore.Core.Tests/Integration/when_a_single_node_is_restarted_multiple_times.cs @@ -53,7 +53,6 @@ private async Task GetLastEpochId(Guid? previousEpochId) IndexDirectory = GetFilePathFor("epoch-index"), }); - await _node.WaitForTcpEndPoint().WaitAsync(RestartTimeout); var wait = Stopwatch.StartNew(); while (wait.Elapsed < RestartTimeout) { diff --git a/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs b/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs index 53791c1be1..ee99b6ff2d 100644 --- a/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs +++ b/src/EventStore.Core.Tests/Integration/when_cluster_nodes_are_restarted.cs @@ -102,7 +102,7 @@ private int SelectRestartNode(bool[] restartedNodes, bool restartLeader) private EndPoint[] GossipSeedsFor(int restartedNodeIndex) => _nodeEndpoints .Where((_, index) => index != restartedNodeIndex) - .Select(x => (EndPoint)x.HttpEndPoint) + .Select(x => (EndPoint)x.NodeEndPoint) .ToArray(); [Test] diff --git a/src/EventStore.Core.Tests/Integration/when_node_becomes_leader_with_unindexed_data.cs b/src/EventStore.Core.Tests/Integration/when_node_becomes_leader_with_unindexed_data.cs deleted file mode 100644 index f7e6400840..0000000000 --- a/src/EventStore.Core.Tests/Integration/when_node_becomes_leader_with_unindexed_data.cs +++ /dev/null @@ -1,370 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using EventStore.Client.Streams; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Grpc; -using EventStore.Core.Tests.Helpers; -using EventStore.Plugins.Subsystems; -using Google.Protobuf; -using Grpc.Core; -using Grpc.Net.Client; -using NUnit.Framework; -using Empty = EventStore.Client.Empty; -using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; -using RecordedEvent = EventStore.Client.Streams.ReadResp.Types.ReadEvent.Types.RecordedEvent; - -namespace EventStore.Core.Tests.Integration; - -[Explicit] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_node_becomes_leader_with_unindexed_data : specification_with_cluster -{ - private const string FakeHostAdvertiseAs = "192.168.123.123"; - private const string Username = "admin"; - private const string Password = "changeit"; - - private const string AuthenticationScheme = "Basic"; - private readonly string AuthenticationValue = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{Username}:{Password}")); - private static readonly Marshaller EmptyMarshaller = Marshallers.Create( - static message => message.ToByteArray(), - static bytes => Empty.Parser.ParseFrom(bytes)); - private static readonly Method ResignNodeMethod = new( - MethodType.Unary, - "event_store.client.operations.Operations", - "ResignNode", - EmptyMarshaller, - EmptyMarshaller); - private class CommitTimeoutException : Exception { } - private class WrongExpectedVersionException : Exception { } - - private EndPoint[][] _nodeGossipSeeds; - private HttpClient _httpClient; - - protected override async Task Given() - { - _nodeGossipSeeds = new[] { - new EndPoint[] {_nodeEndpoints[1].HttpEndPoint, _nodeEndpoints[2].HttpEndPoint}, - new EndPoint[] {_nodeEndpoints[0].HttpEndPoint, _nodeEndpoints[2].HttpEndPoint}, - new EndPoint[] {_nodeEndpoints[0].HttpEndPoint, _nodeEndpoints[1].HttpEndPoint} - }; - _httpClient = new HttpClient(new SocketsHttpHandler - { - SslOptions = { - RemoteCertificateValidationCallback = delegate { return true; } - } - }, true); - await base.Given(); - } - - private CallOptions GetCallOptions() - { - return new( - credentials: CallCredentials.FromInterceptor((_, metadata) => - { - metadata.Add("authorization", $"{AuthenticationScheme} {AuthenticationValue}"); - return Task.CompletedTask; - }), - deadline: DateTime.UtcNow.AddSeconds(5)); - } - - private async Task AppendEvent(IPEndPoint endpoint, string stream, long expectedVersion) - { - using var channel = GrpcChannel.ForAddress(new Uri($"https://{endpoint}"), - new GrpcChannelOptions { HttpClient = _httpClient }); - var streamClient = new Streams.StreamsClient(channel); - using var call = streamClient.Append(GetCallOptions()); - - var optionsAppendReq = new AppendReq - { - Options = new() - { - StreamIdentifier = new() - { - StreamName = ByteString.CopyFromUtf8(stream) - }, - } - }; - switch (expectedVersion) - { - case ExpectedVersion.Any: - optionsAppendReq.Options.Any = new Empty(); - break; - case ExpectedVersion.NoStream: - optionsAppendReq.Options.NoStream = new Empty(); - break; - default: - optionsAppendReq.Options.Revision = (ulong)expectedVersion; - break; - } - - await call.RequestStream.WriteAsync(optionsAppendReq); - await call.RequestStream.WriteAsync(new AppendReq - { - ProposedMessage = new() - { - Id = new() - { - String = Uuid.FromGuid(Guid.NewGuid()).ToString() - }, - Data = ByteString.Empty, - Metadata = { - [GrpcMetadata.Type] = "type", - [GrpcMetadata.ContentType] = GrpcMetadata.ContentTypes.ApplicationJson - } - } - }); - - await call.RequestStream.CompleteAsync(); - try - { - var appendResp = await call.ResponseAsync; - switch (appendResp.ResultCase) - { - case AppendResp.ResultOneofCase.Success: - return; - case AppendResp.ResultOneofCase.WrongExpectedVersion: - throw new WrongExpectedVersionException(); - } - } - catch (RpcException ex) when (ex.Status.StatusCode == StatusCode.DeadlineExceeded) - { - throw new CommitTimeoutException(); - } - } - - private async Task> ReadAllEvents(IPEndPoint endpoint) - { - using var channel = GrpcChannel.ForAddress(new Uri($"https://{endpoint}"), - new GrpcChannelOptions { HttpClient = _httpClient }); - var streamClient = new Streams.StreamsClient(channel); - - using var call = streamClient.Read(new ReadReq - { - Options = new() - { - All = new() - { - Start = new Empty() - }, - ReadDirection = ReadReq.Types.Options.Types.ReadDirection.Forwards, - Count = ulong.MaxValue, - NoFilter = new Empty(), - UuidOption = new() { Structured = new() } - } - }, GetCallOptions()); - - return await (from response in call.ResponseStream.ReadAllAsync() where response.Event != null select response.Event.Event).ToListAsync(); - } - - private MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds, - int nodePriority, string intHostAdvertiseAs) => new( - PathName, index, endpoints.InternalTcp, - endpoints.ExternalTcp, endpoints.HttpEndPoint, - subsystems: Array.Empty(), gossipSeeds: gossipSeeds, - nodePriority: nodePriority, intHostAdvertiseAs: intHostAdvertiseAs); - - private Task StartNode(int i, int priority, string intHostAdvertiseAs = null) - { - _nodes[i] = CreateNode(i, _nodeEndpoints[i], _nodeGossipSeeds[i], priority, intHostAdvertiseAs); - _nodes[i].Start(); - return Task.CompletedTask; - } - - private async Task IsNodeReady(IPEndPoint httpEndPoint) - { - var response = await _httpClient.GetAsync($"https://{httpEndPoint}/-/readiness"); - return response.IsSuccessStatusCode; - } - - private async Task WaitForAllNodesToBeLive() - { - for (int i = 0; i < 3; i++) - { - await WaitForNodeToBeLive(i); - } - } - - private async Task WaitForNodeToBeLive(int idx) - { - while (!await IsNodeReady(_nodes[idx].HttpEndPoint)) - { - await Task.Delay(100); - } - } - - private async Task WaitForAllNodesToBeCaughtUp(int maxIdx = 3) - { - while (true) - { - var prevWriter = long.MinValue; - var prevChaser = long.MinValue; - var caughtUp = true; - for (int i = 0; i < maxIdx; i++) - { - var writer = _nodes[i].Db.Config.WriterCheckpoint.ReadNonFlushed(); - var chaser = _nodes[i].Db.Config.ChaserCheckpoint.ReadNonFlushed(); - - if (prevWriter == long.MinValue) - { - prevWriter = writer; - } - - if (prevChaser == long.MinValue) - { - prevChaser = chaser; - } - - if (chaser != writer || writer != prevWriter) - { - caughtUp = false; - } - } - - if (caughtUp) - { - break; - } - - await Task.Delay(100); - } - } - - private async Task ShutdownNode(int i, bool keepDb) => await _nodes[i].Shutdown(keepDb: keepDb); - - private async Task ShutdownAllNodes(int maxIdx = 3, bool keepDb = false) - { - for (int i = 0; i < maxIdx; i++) - { - await ShutdownNode(i, keepDb); - } - } - - private async Task ResignLeader(int leaderIdx) - { - var httpEndPoint = _nodes[leaderIdx].HttpEndPoint; - using var channel = GrpcChannel.ForAddress(new Uri($"https://{httpEndPoint}"), - new GrpcChannelOptions { HttpClient = _httpClient }); - await channel.CreateCallInvoker().AsyncUnaryCall( - ResignNodeMethod, - null, - GetCallOptions(), - new Empty()); - - var start = DateTime.UtcNow; - while (_nodes[leaderIdx].NodeState != VNodeState.Unknown && DateTime.UtcNow - start < TimeSpan.FromSeconds(2)) - { - await Task.Delay(100); - } - } - - [SetUp] - public async Task SetUp() - { - // reset the node states between tests - for (int i = 0; i < 3; i++) - { - await _nodes[i].Shutdown(keepDb: false); - } - - for (int i = 0; i < 3; i++) - { - _nodes[i] = CreateNode(i, _nodeEndpoints[i], _nodeGossipSeeds[i], 0, null); - _nodes[i].Start(); - } - } - - [TestCase(true)] - [TestCase(false)] - [Explicit, Category("LongRunning"), Timeout(80000), NonParallelizable] - public async Task new_events_should_have_correct_event_numbers(bool appendInitialEvent) - { - await WaitForAllNodesToBeLive(); - - if (appendInitialEvent) - { - // append event 0@test - await AppendEvent(_nodes[0].HttpEndPoint, "test", ExpectedVersion.NoStream); - } - - await WaitForAllNodesToBeCaughtUp(); - await ShutdownAllNodes(keepDb: true); - - // make node 1 become the leader by setting its priority to 1 - // node 0 can't become a follower since it can't replicate over internal TCP due to the fake --int-host-advertise-as - await StartNode(0, priority: 0, intHostAdvertiseAs: FakeHostAdvertiseAs); - await StartNode(1, priority: 1, intHostAdvertiseAs: FakeHostAdvertiseAs); - - try - { - await WaitForNodeToBeLive(1).WithTimeout(TimeSpan.FromSeconds(10)); - Assert.AreEqual(VNodeState.Leader, _nodes[1].NodeState); - } - catch (TimeoutException) - { - // want to get stuck in preleader since replication isn't possible - Assert.AreEqual(VNodeState.PreLeader, _nodes[1].NodeState); - return; - } - - // append event 1@test. Expect a commit timeout since there is no quorum. - Assert.ThrowsAsync(async () => - { - await AppendEvent(_nodes[1].HttpEndPoint, "test", appendInitialEvent ? 0 : ExpectedVersion.NoStream); - }); - - // resign the leader node so that it goes into the Unknown state and to trigger new elections - await ResignLeader(1); - - // wait for the node to become Leader again - while (_nodes[1].NodeState != VNodeState.Leader) - { - await Task.Delay(100); - } - - // append event 1@test again. Expect a commit timeout since there is no quorum. - Assert.ThrowsAsync(async () => - { - await AppendEvent(_nodes[1].HttpEndPoint, "test", appendInitialEvent ? 0 : ExpectedVersion.NoStream); - }); - - // shut down both nodes - await ShutdownAllNodes(maxIdx: 2, keepDb: true); - - // start both nodes again without the fake --int-host-advertise-as so that they can form a cluster - await StartNode(0, priority: 0); - await StartNode(1, priority: 1); - try - { - await _nodes[0].Started.WithTimeout(TimeSpan.FromSeconds(10)); - await _nodes[1].Started.WithTimeout(TimeSpan.FromSeconds(10)); - } - catch (TimeoutException) - { - // this is expected in logv3 because by creating the duplicate events above we also - // created duplicate stream records which it will detect and complain about it - throw new Exception($"Couldn't start one or more nodes: {_nodes[0].NodeState} {_nodes[1].NodeState}"); - } - Assert.AreEqual(VNodeState.Follower, _nodes[0].NodeState); - Assert.AreEqual(VNodeState.Leader, _nodes[1].NodeState); - - // wait for data replication - await WaitForAllNodesToBeCaughtUp(maxIdx: 2); - - // read "test" events from $all - var events = - (await ReadAllEvents(_nodes[1].HttpEndPoint)) - .Where(x => x.StreamIdentifier!.StreamName.ToStringUtf8() == "test").ToArray(); - - Assert.AreEqual(appendInitialEvent ? 3 : 2, events.Length); - for (int i = 0; i < (appendInitialEvent ? 3 : 2); i++) - { - Assert.AreEqual(i, events[i].StreamRevision, $"i = {i}, revision = {events[i].StreamRevision}"); - } - } -} diff --git a/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs b/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs new file mode 100644 index 0000000000..e45a056858 --- /dev/null +++ b/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs @@ -0,0 +1,81 @@ +using System; +using System.IO.Pipelines; +using System.Net; +using System.Text.Json; +using System.Threading.Tasks; +using EventStore.ClusterNode.Components.Services; +using Microsoft.AspNetCore.Connections; +using NUnit.Framework; + +namespace EventStore.Core.Tests.Regression; + +[TestFixture] +public class GrpcOnlySurfaceParityTests +{ + [Test] + public void observability_payload_preserves_replication_visibility() + { + var page = QueueDashboardPage.Success(Array.Empty()); + using var payload = JsonDocument.Parse(page.ClientPayloadJson); + + Assert.That(payload.RootElement.TryGetProperty("replicationConnections", out _), Is.True); + Assert.That(payload.RootElement.TryGetProperty("nodeConnections", out _), Is.True); + } + + [Test] + public async Task shared_endpoint_connections_are_visible_only_while_active() + { + var tracker = new NodeConnectionTracker(); + var connection = new DefaultConnectionContext("grpc-connection") + { + LocalEndPoint = new IPEndPoint(IPAddress.Loopback, 2113), + RemoteEndPoint = new IPEndPoint(IPAddress.Loopback, 50123) + }; + var release = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var trafficObserved = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var incoming = new Pipe(); + var outgoing = new Pipe(); + connection.Transport = new TestDuplexPipe(incoming.Reader, outgoing.Writer); + var tracking = tracker.Track(connection, async trackedConnection => + { + tracker.ObserveRequest( + trackedConnection.ConnectionId, + "HTTP/2", + isGrpc: true, + connectionName: "projection-catchup", + userAgent: "grpc-dotnet"); + await trackedConnection.Transport.Output.WriteAsync(new byte[3]); + await incoming.Writer.WriteAsync(new byte[5]); + var read = await trackedConnection.Transport.Input.ReadAsync(); + trackedConnection.Transport.Input.AdvanceTo(read.Buffer.GetPosition(2), read.Buffer.End); + trafficObserved.SetResult(); + await release.Task; + }, isTls: true); + await trafficObserved.Task; + + Assert.That(tracker.Snapshot(), Has.One.Matches(x => + x.ConnectionId == "grpc-connection" && x.IsTls && + x.ClientName == "projection-catchup" && x.Application == "gRPC" && x.Protocol == "HTTP/2" && + x.TotalBytesSent == 3 && x.TotalBytesReceived == 2 && x.PendingReceivedBytes == 3)); + + tracker.ObserveRequest( + connection.ConnectionId, + "HTTP/2", + isGrpc: false, + connectionName: "", + userAgent: "browser"); + Assert.That(tracker.Snapshot(), Has.One.Matches(x => + x.ClientName == "projection-catchup" && x.Application == "HTTP and gRPC")); + + release.SetResult(); + await tracking; + + Assert.That(tracker.Snapshot(), Is.Empty); + } + + private sealed class TestDuplexPipe(PipeReader input, PipeWriter output) : IDuplexPipe + { + public PipeReader Input { get; } = input; + public PipeWriter Output { get; } = output; + } +} diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs index f63c8c9319..b072443420 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterSettingsFactory.cs @@ -1,9 +1,7 @@ using System; using System.Linq; using System.Net; -using System.Runtime.InteropServices; using EventStore.Core.Cluster.Settings; -using EventStore.Core.Tests.Services.Transport.Tcp; namespace EventStore.Core.Tests.Services.ElectionsService; @@ -14,13 +12,9 @@ public class ClusterSettingsFactory private static ClusterVNodeSettings CreateVNode(int nodeNumber, bool isReadOnlyReplica) { - int tcpIntPort = StartingPort + nodeNumber * 2, - tcpExtPort = tcpIntPort + 1, - httpPort = tcpIntPort + 11; + var httpPort = StartingPort + nodeNumber; return new ClusterVNodeSettings(Guid.NewGuid(), 0, - GetLoopbackForPort(tcpIntPort), null, - GetLoopbackForPort(tcpExtPort), null, GetLoopbackForPort(httpPort), 0, isReadOnlyReplica); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs index e1fb14badc..b9c4a888d9 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ClusterVNodeSettings.cs @@ -16,24 +16,14 @@ public class ClusterVNodeSettings public readonly bool ReadOnlyReplica; public ClusterVNodeSettings(Guid instanceId, int debugIndex, - IPEndPoint internalTcpEndPoint, - IPEndPoint internalSecureTcpEndPoint, - IPEndPoint externalTcpEndPoint, - IPEndPoint externalSecureTcpEndPoint, IPEndPoint httpEndPoint, int nodePriority, bool readOnlyReplica) { Ensure.NotEmptyGuid(instanceId, "instanceId"); - Ensure.Equal(false, internalTcpEndPoint == null && internalSecureTcpEndPoint == null, "Both internal TCP endpoints are null"); - Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); - NodeInfo = new VNodeInfo(instanceId, debugIndex, - internalTcpEndPoint, internalSecureTcpEndPoint, - externalTcpEndPoint, externalSecureTcpEndPoint, - httpEndPoint, - readOnlyReplica); + NodeInfo = new VNodeInfo(instanceId, debugIndex, httpEndPoint, readOnlyReplica); NodePriority = nodePriority; diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs index b24a1e875e..a07c837474 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionServiceUnit.cs @@ -47,11 +47,7 @@ public ElectionsServiceUnit(ClusterSettings clusterSettings) _bus = new(GetType().Name); var memberInfo = MemberInfo.Initial(clusterSettings.Self.NodeInfo.InstanceId, InitialDate, VNodeState.Unknown, true, - clusterSettings.Self.NodeInfo.InternalTcp, - clusterSettings.Self.NodeInfo.InternalSecureTcp, - clusterSettings.Self.NodeInfo.ExternalTcp, - clusterSettings.Self.NodeInfo.ExternalSecureTcp, - clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, 0, + clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, clusterSettings.Self.NodePriority, clusterSettings.Self.ReadOnlyReplica); ElectionsService = new Core.Services.ElectionsService(Publisher, @@ -83,11 +79,7 @@ private ClusterInfo BuildClusterInfo(ClusterSettings clusterSettings) InitialDate, VNodeState.Unknown, true, - clusterSettings.Self.NodeInfo.InternalTcp, - clusterSettings.Self.NodeInfo.InternalSecureTcp, - clusterSettings.Self.NodeInfo.ExternalTcp, - clusterSettings.Self.NodeInfo.ExternalSecureTcp, - clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, 0, + clusterSettings.Self.NodeInfo.HttpEndPoint, null, 0, LastCommitPosition, WriterCheckpoint, ChaserCheckpoint, -1, -1, @@ -98,11 +90,7 @@ private ClusterInfo BuildClusterInfo(ClusterSettings clusterSettings) InitialDate, VNodeState.Unknown, true, - x.NodeInfo.InternalTcp, - x.NodeInfo.InternalSecureTcp, - x.NodeInfo.ExternalTcp, - x.NodeInfo.ExternalSecureTcp, - x.NodeInfo.HttpEndPoint, null, 0, 0, + x.NodeInfo.HttpEndPoint, null, 0, LastCommitPosition, WriterCheckpoint, ChaserCheckpoint, -1, -1, @@ -196,9 +184,7 @@ public IEnumerable ListMembers(Func predicate = nu ? MemberInfo.ForManager(x.InstanceId, x.TimeStamp, x.IsAlive, x.HttpEndPoint) : MemberInfo.ForVNode(x.InstanceId, x.TimeStamp, x.State, x.IsAlive, - x.InternalTcpEndPoint, x.InternalSecureTcpEndPoint, - x.ExternalTcpEndPoint, x.ExternalSecureTcpEndPoint, - x.HttpEndPoint, null, 0, 0, + x.HttpEndPoint, null, 0, x.LastCommitPosition, x.WriterCheckpoint, x.ChaserCheckpoint, x.EpochPosition, x.EpochNumber, x.EpochId, x.NodePriority, x.IsReadOnlyReplica)); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs index bcf9e0938f..378cc8075c 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/ElectionsServiceTests.cs @@ -29,18 +29,12 @@ public abstract class ElectionsFixture protected static Func NodeFactory = (id) => new VNodeInfo( Guid.Parse($"00000000-0000-0000-0000-00000000000{id}"), id, - new IPEndPoint(IPAddress.Loopback, id), - new IPEndPoint(IPAddress.Loopback, id), - new IPEndPoint(IPAddress.Loopback, id), - new IPEndPoint(IPAddress.Loopback, id), new IPEndPoint(IPAddress.Loopback, id), false); protected static readonly Func MemberInfoFromVNode = (nodeInfo, timestamp, state, isAlive, epochNumber, epochId, priority) => MemberInfo.ForVNode( nodeInfo.InstanceId, timestamp, state, isAlive, - nodeInfo.InternalTcp, - nodeInfo.InternalSecureTcp, nodeInfo.ExternalTcp, nodeInfo.ExternalSecureTcp, - nodeInfo.HttpEndPoint, null, 0, 0, + nodeInfo.HttpEndPoint, null, 0, 0, 0, 0, 0, epochNumber, epochId, priority, nodeInfo.IsReadOnlyReplica); @@ -924,9 +918,7 @@ public void should_send_an_acceptance_to_other_members() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeThree.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeThree.InternalTcp, - _nodeThree.InternalSecureTcp, _nodeThree.ExternalTcp, _nodeThree.ExternalSecureTcp, - _nodeThree.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, 0, _epochId, 0, + _nodeThree.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeThree.IsReadOnlyReplica)), new GrpcMessage.SendOverGrpc(_nodeThree.HttpEndPoint, new ElectionMessage.Accept(_node.InstanceId, _node.HttpEndPoint, @@ -1077,9 +1069,7 @@ public void should_complete_elections() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, 0, _epochId, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; _publisher.Messages.Should().BeEquivalentTo(expected); @@ -1314,9 +1304,7 @@ public void should_attempt_not_to_elect_previously_elected_leader() new ElectionMessage.ElectionsDone(3,1, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, 0, _epochId, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; _publisher.Messages.Should().BeEquivalentTo(expected); @@ -1357,11 +1345,7 @@ public void should_throw_argument_exception() var endpoint = new IPEndPoint(IPAddress.Loopback, 1234); var nodeInfo = MemberInfo.Initial(Guid.NewGuid(), DateTime.UtcNow, VNodeState.ReadOnlyLeaderless, true, - endpoint, - endpoint, - endpoint, - endpoint, - endpoint, null, 0, 0, + endpoint, null, 0, 0, true); @@ -1405,9 +1389,7 @@ public void previous_leader_should_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeThree.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeThree.InternalTcp, - _nodeThree.InternalSecureTcp, _nodeThree.ExternalTcp, _nodeThree.ExternalSecureTcp, - _nodeThree.HttpEndPoint, null, 0, 0, + _nodeThree.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeThree.IsReadOnlyReplica)), }; @@ -1505,9 +1487,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; @@ -1541,9 +1521,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; @@ -1598,9 +1576,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; @@ -1634,9 +1610,7 @@ public void previous_leader_should_not_be_elected() new ElectionMessage.ElectionsDone(0,0, MemberInfo.ForVNode( _nodeTwo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - _nodeTwo.InternalTcp, - _nodeTwo.InternalSecureTcp, _nodeTwo.ExternalTcp, _nodeTwo.ExternalSecureTcp, - _nodeTwo.HttpEndPoint, null, 0, 0, + _nodeTwo.HttpEndPoint, null, 0, 0, 0, 0, 0, 0, _epochId, 0, _nodeTwo.IsReadOnlyReplica)), }; diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs b/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs index 11c8462e3d..6e3a013cf5 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/LeaderNode/ElectionsServiceUnitTests.cs @@ -40,7 +40,7 @@ public void Setup() seeds.Add(endPoint); var instanceId = Guid.Parse($"101EFD13-F9CD-49BE-9C6D-E6AF9AF5540{i}"); var memberInfo = MemberInfo.ForVNode(instanceId, DateTime.UtcNow, VNodeState.Unknown, true, - endPoint, null, endPoint, null, endPoint, null, 0, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false); + endPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false); members.Add(memberInfo); _fakeTimeProvider = new FakeTimeProvider(); _scheduler = new FakeScheduler(new FakeTimer(), _fakeTimeProvider); @@ -379,7 +379,7 @@ Func epochNumber { var id = IdForNode(i); var ep = EndpointForNode(i); - return MemberInfo.ForVNode(id, DateTime.Now, VNodeState.Follower, true, ep, ep, ep, ep, ep, null, 0, 0, + return MemberInfo.ForVNode(id, DateTime.Now, VNodeState.Follower, true, ep, null, 0, -1, writerCheckpoint(i), chaserCheckpoint(i), 1, epochNumber(i), epochId, nodePriority(i), false); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs index 68b6ce5fd9..168f65a466 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/RandomizedElectionsTestCase.cs @@ -74,7 +74,7 @@ public virtual void Init() var outputBus = new SynchronousScheduler($"ELECTIONS-OUTPUT-BUS-{i}"); var endPoint = new IPEndPoint(BaseEndPoint.Address, BaseEndPoint.Port + i); var memberInfo = MemberInfo.Initial(Guid.NewGuid(), DateTime.UtcNow, VNodeState.Unknown, true, - endPoint, endPoint, endPoint, endPoint, endPoint, null, 0, 0, 0, false); + endPoint, null, 0, 0, false); _instances.Add(new ElectionsInstance(memberInfo.InstanceId, endPoint, inputBus, outputBus)); sendOverHttpHandler.RegisterEndPoint(endPoint, inputBus); @@ -125,8 +125,7 @@ protected virtual GossipMessage.GossipUpdated GetInitialGossipFor(ElectionsInsta { var members = allInstances.Select( x => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - x.EndPoint, null, x.EndPoint, null, - x.EndPoint, null, 0, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)); + x.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)); var gossip = new GossipMessage.GossipUpdated(new ClusterInfo(members.ToArray())); return gossip; } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs index 7ed5cd5fc9..b9381ca8dc 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/UpdateGossipProcessor.cs @@ -63,7 +63,7 @@ public void Process(int iteration, RandTestQueueItem item) previousMembers[leaderIndex] = MemberInfo.ForVNode(previousLeaderInfo.InstanceId, DateTime.UtcNow, VNodeState.Leader, - previousLeaderInfo.IsAlive, leaderEndPoint, null, leaderEndPoint, null, leaderEndPoint, null, 0, 0, + previousLeaderInfo.IsAlive, leaderEndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false); } } @@ -82,7 +82,7 @@ public void Process(int iteration, RandTestQueueItem item) foreach (var memberInfo in updatedGossip) { - _sendOverGrpcProcessor.RegisterEndpointToSkip(memberInfo.ExternalTcpEndPoint, !memberInfo.IsAlive); + _sendOverGrpcProcessor.RegisterEndpointToSkip(memberInfo.HttpEndPoint, !memberInfo.IsAlive); } var updateGossipMessage = new GossipMessage.GossipUpdated(new ClusterInfo(updatedGossip)); diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs index 339ca873bd..6722010f0f 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_full_imediately.cs @@ -37,7 +37,7 @@ private MemberInfo[] CreateInitialGossip(ElectionsInstance instance, ElectionsIn { return new[] { MemberInfo.ForVNode(instance.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - instance.EndPoint, null, instance.EndPoint, null, instance.EndPoint, null, 0, 0, + instance.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false) }; } @@ -53,7 +53,7 @@ private MemberInfo[] CreateUpdatedGossip(int iteration, Console.WriteLine("Update item: {0} : {1}", iteration, item.EndPoint.GetPort()); return instances.Select((x, i) => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - x.EndPoint, null, x.EndPoint, null, x.EndPoint, null, 0, 0, + x.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)).ToArray(); } diff --git a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs index 05f165cfab..cdf621e6ab 100644 --- a/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs +++ b/src/EventStore.Core.Tests/Services/ElectionsService/Randomized/elections_service_5_nodes_with_1_known_when_started_and_set_to_full_later.cs @@ -37,7 +37,7 @@ private MemberInfo[] CreateInitialGossip(ElectionsInstance instance, ElectionsIn { return new[] { MemberInfo.ForVNode(instance.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - instance.EndPoint, null, instance.EndPoint, null, instance.EndPoint, null, 0, 0, + instance.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false) }; } @@ -57,7 +57,7 @@ private MemberInfo[] CreateUpdatedGossip(int iteration, { return instances.Select((x, i) => MemberInfo.ForVNode(x.InstanceId, DateTime.UtcNow, VNodeState.Unknown, true, - x.EndPoint, null, x.EndPoint, null, x.EndPoint, null, 0, 0, + x.EndPoint, null, 0, -1, 0, 0, -1, -1, Guid.Empty, 0, false)) .ToArray(); } diff --git a/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs b/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs index 11ebbfbf88..2c877c6645 100644 --- a/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/GossipService/NodeGossipServiceTests.cs @@ -43,31 +43,15 @@ public NodeGossipServiceTestFixture() _currentNode = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000001"), 1, - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1111), new IPEndPoint(IPAddress.Loopback, 1111), false); _nodeTwo = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000002"), 2, - new IPEndPoint(IPAddress.Loopback, 2222), - new IPEndPoint(IPAddress.Loopback, 2222), - new IPEndPoint(IPAddress.Loopback, 2222), - new IPEndPoint(IPAddress.Loopback, 2222), new IPEndPoint(IPAddress.Loopback, 2222), false); _nodeThree = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000003"), 3, - new IPEndPoint(IPAddress.Loopback, 3333), - new IPEndPoint(IPAddress.Loopback, 3333), - new IPEndPoint(IPAddress.Loopback, 3333), - new IPEndPoint(IPAddress.Loopback, 3333), new IPEndPoint(IPAddress.Loopback, 3333), false); _nodeFour = new VNodeInfo( Guid.Parse("00000000-0000-0000-0000-000000000004"), 4, - new IPEndPoint(IPAddress.Loopback, 4444), - new IPEndPoint(IPAddress.Loopback, 4444), - new IPEndPoint(IPAddress.Loopback, 4444), - new IPEndPoint(IPAddress.Loopback, 4444), new IPEndPoint(IPAddress.Loopback, 4444), false); _getNodeToGossipTo = infos => infos.First(x => Equals(x.HttpEndPoint, _nodeTwo.HttpEndPoint)); @@ -120,8 +104,7 @@ protected static MemberInfo MemberInfoForVNode(VNodeInfo nodeInfo, DateTime utcN VNodeState nodeState = VNodeState.Initializing, string esVersion = VersionInfo.DefaultVersion, bool isAlive = true) { return MemberInfo.ForVNode(nodeInfo.InstanceId, utcNow, nodeState, isAlive, - nodeInfo.InternalTcp, nodeInfo.InternalSecureTcp, nodeInfo.ExternalTcp, - nodeInfo.ExternalSecureTcp, nodeInfo.HttpEndPoint, null, 0, 0, + nodeInfo.HttpEndPoint, null, 0, 0, writerCheckpoint ?? 0, 0, -1, epochNumber ?? -1, Guid.Empty, nodePriority ?? 0, false, esVersion); } @@ -743,7 +726,7 @@ protected override Message[] Given() => _nodeTwo.HttpEndPoint); [Test] - public void should_ignore_message_and_wait_for_tcp_to_decide() + public void should_ignore_message_and_wait_for_connection_state_to_change() { ExpectNoMessages(); } @@ -1031,7 +1014,7 @@ private static MemberInfo TestNodeFor(int identifier, bool isAlive, DateTime tim { var ipEndpoint = new IPEndPoint(IPAddress.Loopback, identifier); return MemberInfo.ForVNode(Guid.NewGuid(), timeStamp, VNodeState.Initializing, isAlive, - ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, null, 0, 0, + ipEndpoint, null, 0, 0, 0, 0, -1, -1, Guid.Empty, 0, false); } @@ -1108,7 +1091,7 @@ private static MemberInfo TestNodeFor(int identifier, bool isAlive, DateTime tim { var ipEndpoint = new IPEndPoint(IPAddress.Loopback, identifier); return MemberInfo.ForVNode(Guid.NewGuid(), timeStamp, nodeState, isAlive, - ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, ipEndpoint, null, 0, 0, + ipEndpoint, null, 0, 0, 0, 0, -1, -1, Guid.Empty, 0, false); } diff --git a/src/EventStore.Core.Tests/Services/PersistentSubscription/PersistentSubscriptionTests.cs b/src/EventStore.Core.Tests/Services/PersistentSubscription/PersistentSubscriptionTests.cs index 9975fd3b17..cd37d16a0a 100644 --- a/src/EventStore.Core.Tests/Services/PersistentSubscription/PersistentSubscriptionTests.cs +++ b/src/EventStore.Core.Tests/Services/PersistentSubscription/PersistentSubscriptionTests.cs @@ -5,8 +5,6 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common; using EventStore.Core.Bus; using EventStore.Core.Data; using EventStore.Core.Helpers; @@ -15,10 +13,10 @@ using EventStore.Core.Messages; using EventStore.Core.Messaging; using EventStore.Core.Metrics; +using EventStore.Core.Services; using EventStore.Core.Services.PersistentSubscription; using EventStore.Core.Services.PersistentSubscription.ConsumerStrategy; using EventStore.Core.Services.Storage.ReaderIndex; -using EventStore.Core.Tests.ClientAPI; using EventStore.Core.Tests.Services.Replication; using EventStore.Core.Tests.TransactionLog; using EventStore.Core.TransactionLog.LogRecords; @@ -2606,58 +2604,6 @@ public void retrying_parked_messages_with_stop_at_replays_parkedEvents_until_tha } } -[Ignore("very long test")] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class DeadlockTest : SpecificationWithMiniNode -{ - protected override Task Given() - { - _conn = BuildConnection(_node); - return _conn.ConnectAsync(); - } - - protected override Task When() => Task.CompletedTask; - - [Test] - public async Task read_whilst_ack_doesnt_deadlock_with_request_response_dispatcher() - { - var persistentSubscriptionSettings = PersistentSubscriptionSettings.Create().Build(); - var userCredentials = DefaultData.AdminCredentials; - await _conn.CreatePersistentSubscriptionAsync("TestStream", "TestGroup", persistentSubscriptionSettings, - userCredentials); - - const int count = 5000; - await _conn.AppendToStreamAsync("TestStream", ExpectedVersion.Any, CreateEvent().Take(count)); - - - var received = 0; - var manualResetEventSlim = new ManualResetEventSlim(); - var sub1 = _conn.ConnectToPersistentSubscription("TestStream", "TestGroup", (sub, ev) => - { - received++; - if (received == count) - { - manualResetEventSlim.Set(); - } - - return Task.CompletedTask; - }, - (sub, reason, ex) => { }); - Assert.IsTrue(manualResetEventSlim.Wait(TimeSpan.FromSeconds(30)), - "Failed to receive all events in 2 minutes. Assume event store is deadlocked."); - sub1.Stop(TimeSpan.FromSeconds(10)); - _conn.Close(); - } - - private static IEnumerable CreateEvent() - { - while (true) - { - yield return new EventData(Guid.NewGuid(), "testtype", false, new byte[0], new byte[0]); - } - } -} - public class CheckpointingWithSkippedEvents { [Test] diff --git a/src/EventStore.Core.Tests/Services/Replication/LeaderReplication/when_non_tcp_replica_subscribes.cs b/src/EventStore.Core.Tests/Services/Replication/LeaderReplication/when_replica_subscribes.cs similarity index 96% rename from src/EventStore.Core.Tests/Services/Replication/LeaderReplication/when_non_tcp_replica_subscribes.cs rename to src/EventStore.Core.Tests/Services/Replication/LeaderReplication/when_replica_subscribes.cs index 40f010e3e7..b212493732 100644 --- a/src/EventStore.Core.Tests/Services/Replication/LeaderReplication/when_non_tcp_replica_subscribes.cs +++ b/src/EventStore.Core.Tests/Services/Replication/LeaderReplication/when_replica_subscribes.cs @@ -10,7 +10,7 @@ namespace EventStore.Core.Tests.Services.Replication.LeaderReplication; -public class when_non_tcp_replica_subscribes : WithReplicationService +public class when_replica_subscribes : WithReplicationService { private static readonly ReplicationSessionStatistics Statistics = new( SendQueueSize: 7, diff --git a/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs b/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs index a7514a3a51..1dfac124f9 100644 --- a/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs +++ b/src/EventStore.Core.Tests/Services/Replication/LogReplication/LogReplicationFixture.cs @@ -157,14 +157,9 @@ private async ValueTask> CreateLeader(TFChunkDb db, Cancel timeStamp: DateTime.Now, state: VNodeState.Leader, isAlive: true, - internalTcpEndPoint: FakeEndPoint, - internalSecureTcpEndPoint: null, - externalTcpEndPoint: null, - externalSecureTcpEndPoint: null, httpEndPoint: FakeEndPoint, advertiseHostToClientAs: null, advertiseHttpPortToClientAs: 0, - advertiseTcpPortToClientAs: 0, lastCommitPosition: 0, writerCheckpoint: 0, chaserCheckpoint: 0, diff --git a/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs b/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs index f8e76a11ab..b053aaf076 100644 --- a/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs +++ b/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs @@ -1,11 +1,18 @@ +using System; using System.Net; +using System.Net.Http; +using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.Core.Tests.ClientAPI.Helpers; +using EventStore.Client.Streams; +using EventStore.Core.Services.Transport.Grpc; using EventStore.Core.Tests.Helpers; using EventStore.Core.Tests.Integration; +using Google.Protobuf; +using Grpc.Core; +using Grpc.Net.Client; using NUnit.Framework; +using Empty = EventStore.Client.Empty; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; namespace EventStore.Core.Tests.Replication.ReadOnlyReplica; @@ -13,13 +20,15 @@ namespace EventStore.Core.Tests.Replication.ReadOnlyReplica; [TestFixture(typeof(LogFormat.V2), typeof(string))] public class connecting_to_read_only_replica : specification_with_cluster { + protected override async Task Given() => + await _nodes[2].AdminUserCreated.WithTimeout(TimeSpan.FromSeconds(30)); + protected override MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds, bool wait = true) { var isReadOnly = index == 2; var node = new MiniClusterNode( - PathName, index, endpoints.InternalTcp, - endpoints.ExternalTcp, endpoints.HttpEndPoint, gossipSeeds, + PathName, index, endpoints.NodeEndPoint, gossipSeeds, readOnlyReplica: isReadOnly); if (wait && !isReadOnly) { @@ -29,35 +38,82 @@ protected override MiniClusterNode CreateNode(int index, return node; } - protected override IEventStoreConnection CreateConnection() + private static CallOptions GetCallOptions() { - var settings = ConnectionSettings.Create() - .DisableServerCertificateValidation() - .PerformOnAnyNode(); - return EventStoreConnection.Create(settings, _nodes[2].ExternalTcpEndPoint); + var credentials = CallCredentials.FromInterceptor((_, metadata) => + { + metadata.Add("authorization", + $"Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:changeit"))}"); + return Task.CompletedTask; + }); + return new CallOptions(credentials: credentials, deadline: DateTime.UtcNow.AddSeconds(30)); } - [Test] - public async Task append_to_stream_should_fail_with_not_supported_exception() + private Streams.StreamsClient CreateClient(out GrpcChannel channel, out HttpClient httpClient) { - const string stream = "append_to_stream_should_fail_with_not_supported_exception"; - await AssertEx.ThrowsAsync( - () => _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, TestEvent.NewTestEvent())); + httpClient = new HttpClient(new SocketsHttpHandler + { + SslOptions = { RemoteCertificateValidationCallback = delegate { return true; } } + }); + channel = GrpcChannel.ForAddress(new Uri($"https://{_nodes[2].HttpEndPoint}"), + new GrpcChannelOptions { HttpClient = httpClient }); + return new Streams.StreamsClient(channel); } [Test] - public async Task delete_stream_should_fail_with_not_supported_exception() + public async Task append_to_stream_is_rejected() { - const string stream = "delete_stream_should_fail_with_not_supported_exception"; - await AssertEx.ThrowsAsync(() => - _conn.DeleteStreamAsync(stream, ExpectedVersion.Any)); + var client = CreateClient(out var channel, out var httpClient); + using (channel) + using (httpClient) + using (var call = client.Append(GetCallOptions())) + { + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(nameof(append_to_stream_is_rejected)) } + } + }); + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Uuid.NewUuid().ToDto(), + Data = ByteString.Empty, + CustomMetadata = ByteString.Empty, + Metadata = + { + [GrpcMetadata.Type] = "test", + [GrpcMetadata.ContentType] = GrpcMetadata.ContentTypes.ApplicationJson + } + } + }); + await call.RequestStream.CompleteAsync(); + + var exception = Assert.ThrowsAsync(async () => await call.ResponseAsync); + Assert.That(exception.StatusCode, Is.EqualTo(StatusCode.NotFound)); + } } [Test] - public async Task start_transaction_should_fail_with_not_supported_exception() + public async Task delete_stream_is_rejected() { - const string stream = "start_transaction_should_fail_with_not_supported_exception"; - await AssertEx.ThrowsAsync(() => - _conn.StartTransactionAsync(stream, ExpectedVersion.Any)); + var client = CreateClient(out var channel, out var httpClient); + using (channel) + using (httpClient) + using (var call = client.DeleteAsync(new DeleteReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(nameof(delete_stream_is_rejected)) } + } + }, GetCallOptions())) + { + var exception = Assert.ThrowsAsync(async () => await call.ResponseAsync); + Assert.That(exception.StatusCode, Is.EqualTo(StatusCode.NotFound)); + } } } diff --git a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs index e861b74159..70d7ef4a93 100644 --- a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingServiceTests.cs @@ -791,10 +791,6 @@ private static MemberInfo CreateLeader(Guid? instanceId = null, int httpPort = 2 DateTime.UtcNow, VNodeState.Leader, true, - new DnsEndPoint("leader-replication.internal", 1112), - new DnsEndPoint("leader-replication.internal", 1113), - null, - null, new DnsEndPoint("leader.internal", httpPort), null, 0, @@ -803,7 +799,6 @@ private static MemberInfo CreateLeader(Guid? instanceId = null, int httpPort = 2 0, 0, 0, - 0, Guid.NewGuid(), 0, false); diff --git a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs index 17fe60ab3f..50186afc83 100644 --- a/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs +++ b/src/EventStore.Core.Tests/Services/RequestForwarding/GrpcRequestForwardingTransportSecurityTests.cs @@ -53,7 +53,7 @@ public async Task cleartext_credential_rejection_completes_the_proxy_correlation supervisor.Handle(new ClientMessage.ForwardMessage(request)); - var response = publisher.Messages.OfType().Single(); + var response = publisher.Messages.OfType().Single(); Assert.That(response.CorrelationId, Is.EqualTo(request.InternalCorrId)); } @@ -119,10 +119,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( DateTime.UtcNow, VNodeState.Leader, true, - new DnsEndPoint("leader-replication.internal", 1112), - new DnsEndPoint("leader-replication.internal", 1113), - null, - null, new DnsEndPoint("leader.internal", 2113), null, 0, @@ -131,7 +127,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( 0, 0, 0, - 0, Guid.NewGuid(), 0, false); diff --git a/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs b/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs index d0ed4eb5e5..8eb4dccb3c 100644 --- a/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/RequestForwarding/RequestForwardingServiceTests.cs @@ -55,13 +55,13 @@ public void not_authenticated_survives_the_client_correlation_rewrite() clientCorrelationId, new CallbackEnvelope(message => response = message), TimeSpan.FromMinutes(1), - new TcpMessage.NotAuthenticated(clientCorrelationId, "timeout")); + new ClientMessage.NotAuthenticated(clientCorrelationId, "timeout")); var service = new RequestForwardingService( new NoopPublisher(), forwardingProxy, TimeSpan.FromSeconds(1)); - service.Handle(new TcpMessage.NotAuthenticated(internalCorrelationId, "not authenticated")); + service.Handle(new ClientMessage.NotAuthenticated(internalCorrelationId, "not authenticated")); - var completion = (TcpMessage.NotAuthenticated)response; + var completion = (ClientMessage.NotAuthenticated)response; Assert.Multiple(() => { Assert.That(completion.CorrelationId, Is.EqualTo(clientCorrelationId)); diff --git a/src/EventStore.Core.Tests/Services/RequestManagement/Service/RequestManagerServiceSpecification.cs b/src/EventStore.Core.Tests/Services/RequestManagement/Service/RequestManagerServiceSpecification.cs index 4a26eb87b9..4c04d8d10e 100644 --- a/src/EventStore.Core.Tests/Services/RequestManagement/Service/RequestManagerServiceSpecification.cs +++ b/src/EventStore.Core.Tests/Services/RequestManagement/Service/RequestManagerServiceSpecification.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using EventStore.ClientAPI.Common.Utils; +using EventStore.Common.Utils; using EventStore.Core.Bus; using EventStore.Core.Data; using EventStore.Core.Messages; diff --git a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs index 8ef3d8d03b..6f3bf4ff30 100644 --- a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs +++ b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader.cs @@ -42,10 +42,6 @@ private static MemberInfo FakeMemberInfo() return EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - null, 0, 0, 0, false); + null, 0, 0, false); } } diff --git a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs index 994a62f28d..4b7ba26312 100644 --- a/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs +++ b/src/EventStore.Core.Tests/Services/RequestManagement/Service/when_writing_and_deposed_as_leader_and_replica_moves_forward.cs @@ -37,10 +37,6 @@ private static MemberInfo FakeMemberInfo() return EventStore.Core.Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - new IPEndPoint(IPAddress.Parse(ipAddress), port), - null, 0, 0, 0, false); + null, 0, 0, false); } } diff --git a/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_disallowed_streams.cs b/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_disallowed_streams.cs index 4e4b337578..fad1650335 100644 --- a/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_disallowed_streams.cs +++ b/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_disallowed_streams.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using EventStore.Client.Messages; using EventStore.Core.Data; using EventStore.Core.Services.Storage.ReaderIndex; using NUnit.Framework; @@ -49,10 +48,7 @@ public async Task should_filter_out_disallowed_streams_when_reading_events_forwa [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_forward_with_event_type_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Prefix, new[] { "event-type" }); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Prefixes(true, new[] { "event-type" }); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); @@ -65,10 +61,7 @@ public async Task should_filter_out_disallowed_streams_when_reading_events_forwa [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_forward_with_event_type_regex() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Regex, new[] { @"^.*event-type-.*$" }); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Regex(true, @"^.*event-type-.*$"); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); @@ -81,10 +74,7 @@ public async Task should_filter_out_disallowed_streams_when_reading_events_forwa [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_forward_with_stream_id_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Prefix, new[] { "$persistentsubscripti" }); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Prefixes(true, new[] { "$persistentsubscripti" }); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); @@ -96,10 +86,7 @@ public async Task should_filter_out_disallowed_streams_when_reading_events_forwa [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_forward_with_stream_id_regex() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Regex, new[] { @"^.*istentsubsc.*$" }); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Regex(true, @"^.*istentsubsc.*$"); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); @@ -122,10 +109,7 @@ public async Task should_filter_out_disallowed_streams_when_reading_events_backw [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_backward_with_event_type_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Prefix, ["event-type"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Prefixes(true, ["event-type"]); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, @@ -139,10 +123,7 @@ await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFil [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_backward_with_event_type_regex() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Regex, [@"^.*event-type-.*$"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Regex(true, @"^.*event-type-.*$"); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, @@ -156,10 +137,7 @@ await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFil [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_backward_with_stream_id_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Prefix, ["$persistentsubscripti"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Prefixes(true, ["$persistentsubscripti"]); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, @@ -172,10 +150,7 @@ await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFil [Test] public async Task should_filter_out_disallowed_streams_when_reading_events_backward_with_stream_id_regex() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Regex, [@"^.*istentsubsc.*$"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Regex(true, @"^.*istentsubsc.*$"); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, diff --git a/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_filtering.cs b/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_filtering.cs index 1bec5d22c5..6ce9098f24 100644 --- a/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_filtering.cs +++ b/src/EventStore.Core.Tests/Services/Storage/AllReader/when_reading_all_with_filtering.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using EventStore.Client.Messages; using EventStore.Core.Data; using EventStore.Core.Services.Storage.ReaderIndex; using NUnit.Framework; @@ -35,10 +34,7 @@ protected override async ValueTask WriteTestScenario(CancellationToken token) [Test] public async Task should_read_only_events_forward_with_event_type_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Prefix, ["event-type"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Prefixes(true, ["event-type"]); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); Assert.AreEqual(2, result.Records.Count); @@ -47,10 +43,7 @@ public async Task should_read_only_events_forward_with_event_type_prefix() [Test] public async Task should_read_only_events_forward_with_event_type_regex() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Regex, [@"^.*other-event.*$"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Regex(true, @"^.*other-event.*$"); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); Assert.AreEqual(2, result.Records.Count); @@ -59,10 +52,7 @@ public async Task should_read_only_events_forward_with_event_type_regex() [Test] public async Task should_read_only_events_forward_with_stream_id_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Prefix, ["ES2"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Prefixes(true, ["ES2"]); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); Assert.AreEqual(1, result.Records.Count); @@ -71,10 +61,7 @@ public async Task should_read_only_events_forward_with_stream_id_prefix() [Test] public async Task should_read_only_events_forward_with_stream_id_regex() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Regex, [@"^.*ES2.*$"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Regex(true, @"^.*ES2.*$"); var result = await ReadIndex.ReadAllEventsForwardFiltered(_forwardReadPos, 10, 10, eventFilter, CancellationToken.None); Assert.AreEqual(1, result.Records.Count); @@ -83,10 +70,7 @@ public async Task should_read_only_events_forward_with_stream_id_regex() [Test] public async Task should_read_only_events_backward_with_event_type_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Prefix, ["event-type"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Prefixes(true, ["event-type"]); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, @@ -97,10 +81,7 @@ await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFil [Test] public async Task should_read_only_events_backward_with_event_type_regex() { - var filter = new Filter( - Filter.Types.FilterContext.EventType, - Filter.Types.FilterType.Regex, new[] { @"^.*other-event.*$" }); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.EventType.Regex(true, @"^.*other-event.*$"); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, @@ -111,10 +92,7 @@ await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFil [Test] public async Task should_read_only_events_backward_with_stream_id_prefix() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Prefix, ["ES2"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Prefixes(true, ["ES2"]); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, @@ -125,10 +103,7 @@ await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFil [Test] public async Task should_read_only_events_backward_with_stream_id_regex() { - var filter = new Filter( - Filter.Types.FilterContext.StreamId, - Filter.Types.FilterType.Regex, [@"^.*ES2.*$"]); - var eventFilter = EventFilter.Get(true, filter); + var eventFilter = EventFilter.StreamName.Regex(true, @"^.*ES2.*$"); var result = await ReadIndex.ReadAllEventsBackwardFiltered(_backwardReadPos, 10, 10, eventFilter, diff --git a/src/EventStore.Core.Tests/Services/Storage/HashCollisions/with_hash_collisions.cs b/src/EventStore.Core.Tests/Services/Storage/HashCollisions/with_hash_collisions.cs index e0428f32d8..46f66f7cd5 100644 --- a/src/EventStore.Core.Tests/Services/Storage/HashCollisions/with_hash_collisions.cs +++ b/src/EventStore.Core.Tests/Services/Storage/HashCollisions/with_hash_collisions.cs @@ -12,7 +12,7 @@ using EventStore.Core.TransactionLog; using EventStore.Core.TransactionLog.LogRecords; using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; +using ExpectedVersion = EventStore.Core.Data.ExpectedVersion; namespace EventStore.Core.Tests.Services.Storage.HashCollisions; diff --git a/src/EventStore.Core.Tests/Services/Storage/Scavenge/when_running_a_scavenge_from_storage_scavenger.cs b/src/EventStore.Core.Tests/Services/Storage/Scavenge/when_running_a_scavenge_from_storage_scavenger.cs index aacbdad527..d596e67c6e 100644 --- a/src/EventStore.Core.Tests/Services/Storage/Scavenge/when_running_a_scavenge_from_storage_scavenger.cs +++ b/src/EventStore.Core.Tests/Services/Storage/Scavenge/when_running_a_scavenge_from_storage_scavenger.cs @@ -1,17 +1,22 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; +using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; +using EventStore.Client.Streams; using EventStore.Core.Messages; using EventStore.Core.Messaging; using EventStore.Core.Services; using EventStore.Core.Services.UserManagement; -using EventStore.Core.Tests.ClientAPI.Helpers; using EventStore.Core.Tests.Helpers; +using Google.Protobuf; +using Grpc.Core; +using Grpc.Net.Client; using NUnit.Framework; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; using ILogger = Serilog.ILogger; +using ReadEvent = EventStore.Client.Streams.ReadResp.Types.ReadEvent; +using StreamsClient = EventStore.Client.Streams.Streams.StreamsClient; namespace EventStore.Core.Tests.Services.Storage.Scavenge; @@ -21,7 +26,7 @@ public class when_running_scavenge_from_storage_scavenger private static readonly ILogger Log = Serilog.Log.ForContext>(); private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(60); private MiniNode _node; - private List _result; + private List _result; public override async Task TestFixtureSetUp() { @@ -52,35 +57,54 @@ public async Task TearDown() public async Task When() { - using (var conn = TestConnection.Create(_node.TcpEndPoint, TcpType.Ssl, DefaultData.AdminCredentials)) + using var channel = GrpcChannel.ForAddress(new UriBuilder { Scheme = Uri.UriSchemeHttps }.Uri, + new GrpcChannelOptions { HttpClient = _node.HttpClient, DisposeHttpClient = false }); + var client = new StreamsClient(channel); + _result = new List(); + var deadline = DateTime.UtcNow + Timeout; + while (_result.Count < 2 && DateTime.UtcNow < deadline) { - await conn.ConnectAsync(); - var countdown = new CountdownEvent(2); - _result = new List(); - - conn.SubscribeToStreamFrom(SystemStreams.ScavengesStream, null, CatchUpSubscriptionSettings.Default, - (x, y) => + _result.Clear(); + using var call = client.Read(new ReadReq + { + Options = new() { - _result.Add(y); - countdown.Signal(); - return Task.CompletedTask; - }, - _ => Log.Information("Processing events started."), - (x, y, z) => { Log.Information("Subscription dropped: {0}, {1}.", y, z); } - ); + Stream = new() + { + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(SystemStreams.ScavengesStream) }, + Start = new() + }, + ReadDirection = ReadReq.Types.Options.Types.ReadDirection.Forwards, + ResolveLinks = true, + Count = 2, + NoFilter = new(), + UuidOption = new() { Structured = new() } + } + }, new CallOptions(credentials: CallCredentials.FromInterceptor((_, metadata) => + { + metadata.Add("authorization", + $"Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:changeit"))}"); + return Task.CompletedTask; + }))); + while (await call.ResponseStream.MoveNext(default)) + if (call.ResponseStream.Current.Event is { } resolvedEvent) + _result.Add(resolvedEvent); - if (!countdown.Wait(Timeout)) + if (_result.Count < 2) { - Assert.Fail("Timeout expired while waiting for events."); + Log.Information("Waiting for the scavenge event stream."); + await Task.Delay(100); } } + + Assert.That(_result.Count, Is.GreaterThanOrEqualTo(2), "Timeout expired while waiting for events."); } [Test] public void should_create_scavenge_started_event_on_index_stream() { var scavengeStartedEvent = - _result.FirstOrDefault(x => x.Event.EventType == SystemEventTypes.ScavengeStarted); + _result.FirstOrDefault(x => x.Event.Metadata[GrpcMetadata.Type] == SystemEventTypes.ScavengeStarted); Assert.IsNotNull(scavengeStartedEvent); } @@ -88,7 +112,7 @@ public void should_create_scavenge_started_event_on_index_stream() public void should_create_scavenge_completed_event_on_index_stream() { var scavengeCompletedEvent = - _result.FirstOrDefault(x => x.Event.EventType == SystemEventTypes.ScavengeCompleted); + _result.FirstOrDefault(x => x.Event.Metadata[GrpcMetadata.Type] == SystemEventTypes.ScavengeCompleted); Assert.IsNotNull(scavengeCompletedEvent); } @@ -96,9 +120,9 @@ public void should_create_scavenge_completed_event_on_index_stream() public void should_link_started_and_completed_events_to_the_same_stream() { var scavengeStartedEvent = - _result.FirstOrDefault(x => x.Event.EventType == SystemEventTypes.ScavengeStarted); + _result.FirstOrDefault(x => x.Event.Metadata[GrpcMetadata.Type] == SystemEventTypes.ScavengeStarted); var scavengeCompletedEvent = - _result.FirstOrDefault(x => x.Event.EventType == SystemEventTypes.ScavengeCompleted); - Assert.AreEqual(scavengeStartedEvent.Event.EventStreamId, scavengeCompletedEvent.Event.EventStreamId); + _result.FirstOrDefault(x => x.Event.Metadata[GrpcMetadata.Type] == SystemEventTypes.ScavengeCompleted); + Assert.AreEqual(scavengeStartedEvent.Event.StreamIdentifier, scavengeCompletedEvent.Event.StreamIdentifier); } } diff --git a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscription.CombinationTests.cs b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscription.CombinationTests.cs index 716711dc46..3919930028 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscription.CombinationTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscription.CombinationTests.cs @@ -2,15 +2,14 @@ using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common; +using EventStore.Client.Streams; using EventStore.Core.Data; using EventStore.Core.Services.Transport.Enumerators; using EventStore.Core.Services.UserManagement; using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; using Position = EventStore.Core.Services.Transport.Common.Position; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; +using RecordedEvent = EventStore.Client.Streams.ReadResp.Types.ReadEvent.Types.RecordedEvent; +using SystemStreams = EventStore.Core.Services.SystemStreams; namespace EventStore.Core.Tests.Services.Transport.Enumerators; @@ -120,7 +119,7 @@ public enum CheckpointType private readonly string _streamPrefix; private int _streamSuffix; - private List _events = new(); + private List _events = new(); private int _nextEventIndex; @@ -130,19 +129,18 @@ public AllSubscriptionCombinationTests(TestData testData) _streamPrefix = $"stream-{Guid.NewGuid()}-"; } - private Position GetPosition(ResolvedEvent @event) + private static Position GetPosition(RecordedEvent @event) { - var pos = @event.OriginalPosition!.Value; - return Position.FromInt64(pos.CommitPosition, pos.PreparePosition); + return new Position(@event.CommitPosition, @event.PreparePosition); } - private Position GetPositionPlusOneByte(ResolvedEvent @event) + private static Position GetPositionPlusOneByte(RecordedEvent @event) { var pos = GetPosition(@event); return new Position(pos.CommitPosition + 1, pos.PreparePosition + 1); } - private Position GetPositionMinusOneByte(ResolvedEvent @event) + private static Position GetPositionMinusOneByte(RecordedEvent @event) { var pos = GetPosition(@event); return new Position(pos.CommitPosition - 1, pos.PreparePosition - 1); @@ -161,23 +159,14 @@ private async Task PopulateExistingEvents() { _events.Clear(); - var result = await NodeConnection.ReadAllEventsForwardAsync( - position: EventStore.ClientAPI.Position.Start, - maxCount: 1000, - resolveLinkTos: false); - - foreach (var @event in result.Events) - { - _events.Add(@event); - } + _events.AddRange(await ReadAllEvents()); } private async Task WriteEvent(string stream, string eventType, string data, string metadata) { data ??= string.Empty; metadata ??= string.Empty; - var eventData = new EventData(Guid.NewGuid(), eventType, true, Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(metadata)); - await NodeConnection.AppendToStreamAsync(stream, ExpectedVersion.Any, eventData); + await AppendToStream(stream, eventType, data, metadata); } private Task WriteEvent() => WriteEvent(_streamPrefix + _streamSuffix++, "type", "{}", null); private Task RevokeAccessWithStreamAcl() => WriteEvent(SystemStreams.MetastreamOf(Core.Services.SystemStreams.AllStream), "$metadata", @"{ ""$acl"": { ""$r"": [] } }", null); @@ -275,8 +264,8 @@ private async Task ReadExpectedEvents(EnumeratorWrapper sub, int nextEventI switch (response) { case Event evt: - var evtPos = _events[nextEventIndex++].OriginalPosition!.Value; - var evtTfPos = new TFPos(evtPos.CommitPosition, evtPos.PreparePosition); + var expectedEvent = _events[nextEventIndex++]; + var evtTfPos = new TFPos((long)expectedEvent.CommitPosition, (long)expectedEvent.PreparePosition); Assert.AreEqual(evtTfPos, evt.EventPosition!.Value); break; case FellBehind: diff --git a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscriptionFiltered.CombinationTests.cs b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscriptionFiltered.CombinationTests.cs index 2ac02ecdd7..fadea91183 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscriptionFiltered.CombinationTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.AllSubscriptionFiltered.CombinationTests.cs @@ -1,18 +1,20 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; +using System.Text.RegularExpressions; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Common; -using EventStore.ClientAPI.Messages; +using EventStore.Client.Streams; using EventStore.Core.Data; using EventStore.Core.Services.Storage.ReaderIndex; using EventStore.Core.Services.Transport.Enumerators; +using EventStore.Core.Services.Transport.Grpc; using EventStore.Core.Services.UserManagement; using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; using Position = EventStore.Core.Services.Transport.Common.Position; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; +using RecordedEvent = EventStore.Client.Streams.ReadResp.Types.ReadEvent.Types.RecordedEvent; +using SystemStreams = EventStore.Core.Services.SystemStreams; namespace EventStore.Core.Tests.Services.Transport.Enumerators; @@ -379,7 +381,7 @@ public enum EventFilterType private readonly Guid _testGuid; private readonly string _streamPrefix; private int _streamSuffix; - private List _events = new(); + private List _events = new(); private int _nextEventIndex; @@ -390,19 +392,18 @@ public AllSubscriptionFilteredCombinationTests(TestData testData) _streamPrefix = $"stream-{_testGuid}-"; } - private Position GetPosition(ResolvedEvent @event) + private static Position GetPosition(RecordedEvent @event) { - var pos = @event.OriginalPosition!.Value; - return Position.FromInt64(pos.CommitPosition, pos.PreparePosition); + return new Position(@event.CommitPosition, @event.PreparePosition); } - private Position GetPositionPlusOneByte(ResolvedEvent @event) + private static Position GetPositionPlusOneByte(RecordedEvent @event) { var pos = GetPosition(@event); return new Position(pos.CommitPosition + 1, pos.PreparePosition + 1); } - private Position GetPositionMinusOneByte(ResolvedEvent @event) + private static Position GetPositionMinusOneByte(RecordedEvent @event) { var pos = GetPosition(@event); return new Position(pos.CommitPosition - 1, pos.PreparePosition - 1); @@ -421,34 +422,27 @@ private async Task PopulateExistingEvents() { _events.Clear(); - var filter = SubscriptionProps.EventFilterType switch + Func filter = SubscriptionProps.EventFilterType switch { - EventFilterType.None => null, - EventFilterType.StreamPrefix => new Filter(ClientMessage.Filter.FilterContext.StreamId, ClientMessage.Filter.FilterType.Prefix, new[] { $"stream-{_testGuid}" }), - EventFilterType.StreamRegex => new Filter(ClientMessage.Filter.FilterContext.StreamId, ClientMessage.Filter.FilterType.Regex, new[] { $"(.*?){_testGuid}(.*?)" }), - EventFilterType.EventTypePrefix => new Filter(ClientMessage.Filter.FilterContext.EventType, ClientMessage.Filter.FilterType.Prefix, new[] { $"type-{_testGuid}" }), - EventFilterType.EventTypeRegex => new Filter(ClientMessage.Filter.FilterContext.EventType, ClientMessage.Filter.FilterType.Regex, new[] { $"(.*?){_testGuid}(.*?)" }), + EventFilterType.None => _ => true, + EventFilterType.StreamPrefix => @event => StreamName(@event).StartsWith($"stream-{_testGuid}", StringComparison.Ordinal), + EventFilterType.StreamRegex => @event => Regex.IsMatch(StreamName(@event), $"(.*?){_testGuid}(.*?)"), + EventFilterType.EventTypePrefix => @event => EventType(@event).StartsWith($"type-{_testGuid}", StringComparison.Ordinal), + EventFilterType.EventTypeRegex => @event => Regex.IsMatch(EventType(@event), $"(.*?){_testGuid}(.*?)"), _ => throw new ArgumentOutOfRangeException() }; - var result = await NodeConnection.FilteredReadAllEventsForwardAsync( - position: EventStore.ClientAPI.Position.Start, - maxCount: 1000, - resolveLinkTos: false, - filter: filter); + _events.AddRange((await ReadAllEvents()).Where(filter)); - foreach (var @event in result.Events) - { - _events.Add(@event); - } + static string StreamName(RecordedEvent @event) => @event.StreamIdentifier.StreamName.ToStringUtf8(); + static string EventType(RecordedEvent @event) => @event.Metadata[GrpcMetadata.Type]; } private async Task WriteEvent(string stream, string eventType, string data, string metadata) { data ??= string.Empty; metadata ??= string.Empty; - var eventData = new EventData(Guid.NewGuid(), eventType, true, Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(metadata)); - await NodeConnection.AppendToStreamAsync(stream, ExpectedVersion.Any, eventData); + await AppendToStream(stream, eventType, data, metadata); } private Task WriteEvent() => WriteEvent(_streamPrefix + _streamSuffix++ + "-filtered", $"type-{_testGuid}-filtered", "{}", null); private Task RevokeAccessWithStreamAcl() => WriteEvent(SystemStreams.MetastreamOf(Core.Services.SystemStreams.AllStream), "$metadata", @"{ ""$acl"": { ""$r"": [] } }", null); @@ -564,8 +558,8 @@ private async Task ReadExpectedEvents(EnumeratorWrapper sub, int nextEventI switch (response) { case Event evt: - var evtPos = _events[nextEventIndex++].OriginalPosition!.Value; - var evtTfPos = new TFPos(evtPos.CommitPosition, evtPos.PreparePosition); + var expectedEvent = _events[nextEventIndex++]; + var evtTfPos = new TFPos((long)expectedEvent.CommitPosition, (long)expectedEvent.PreparePosition); lastEventOrCheckpointPos = evtTfPos; numEventsSinceLastCheckpoint++; Assert.AreEqual(evtTfPos, evt.EventPosition!.Value); diff --git a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.CombinationTests.cs b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.CombinationTests.cs index c65db64107..8f0e43bd56 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.CombinationTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.CombinationTests.cs @@ -1,9 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Tests.ClientAPI.Helpers; +using EventStore.Client; +using EventStore.Client.Streams; +using EventStore.Core.Services.Transport.Grpc; using EventStore.Core.Tests.Helpers; +using Google.Protobuf; +using Grpc.Core; +using Grpc.Net.Client; using NUnit.Framework; +using Empty = EventStore.Client.Empty; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; +using RecordedEvent = EventStore.Client.Streams.ReadResp.Types.ReadEvent.Types.RecordedEvent; namespace EventStore.Core.Tests.Services.Transport.Enumerators; @@ -12,8 +22,16 @@ public partial class EnumeratorTests [TestFixture] public class TestFixtureWithMiniNodeConnection : SpecificationWithDirectoryPerTestFixture { + private static readonly CallCredentials AdminCredentials = CallCredentials.FromInterceptor((_, metadata) => + { + metadata.Add("authorization", + $"Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:changeit"))}"); + return Task.CompletedTask; + }); + protected MiniNode Node { get; private set; } - protected IEventStoreConnection NodeConnection { get; private set; } + private GrpcChannel Channel { get; set; } + private Streams.StreamsClient StreamsClient { get; set; } [OneTimeSetUp] public override async Task TestFixtureSetUp() @@ -21,14 +39,135 @@ public override async Task TestFixtureSetUp() await base.TestFixtureSetUp(); Node = new MiniNode(PathName); await Node.Start(); - NodeConnection = TestConnection.To(Node, TcpType.Ssl, new UserCredentials("admin", "changeit")); - await NodeConnection.ConnectAsync(); + await Node.AdminUserCreated; + Channel = GrpcChannel.ForAddress(new Uri($"https://{Node.HttpEndPoint}"), + new GrpcChannelOptions + { + HttpClient = Node.HttpClient, + DisposeHttpClient = false, + }); + StreamsClient = new Streams.StreamsClient(Channel); } + protected async Task AppendToStream( + string stream, + IEnumerable<(string EventType, byte[] Data, byte[] Metadata)> events) + { + using var call = StreamsClient.Append(GetCallOptions()); + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) } + } + }); + + foreach (var @event in events) + { + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Uuid.NewUuid().ToDto(), + Data = ByteString.CopyFrom(@event.Data), + CustomMetadata = ByteString.CopyFrom(@event.Metadata), + Metadata = + { + [GrpcMetadata.Type] = @event.EventType, + [GrpcMetadata.ContentType] = GrpcMetadata.ContentTypes.ApplicationJson + } + } + }); + } + + await call.RequestStream.CompleteAsync(); + var response = await call.ResponseAsync; + Assert.That(response.ResultCase, Is.EqualTo(AppendResp.ResultOneofCase.Success)); + } + + protected Task AppendToStream(string stream, string eventType, string data, string metadata) => + AppendToStream(stream, + [(eventType, Encoding.UTF8.GetBytes(data ?? string.Empty), Encoding.UTF8.GetBytes(metadata ?? string.Empty))]); + + protected async Task> ReadAllEvents() + { + using var call = StreamsClient.Read(new ReadReq + { + Options = new() + { + All = new() { Start = new Empty() }, + ReadDirection = ReadReq.Types.Options.Types.ReadDirection.Forwards, + Count = ulong.MaxValue, + NoFilter = new Empty(), + UuidOption = new() { Structured = new Empty() } + } + }, GetCallOptions()); + + return await call.ResponseStream.ReadAllAsync() + .Where(response => response.Event is not null) + .Select(response => response.Event.Event) + .ToArrayAsync(); + } + + protected async Task DeleteStream(string stream, bool hardDelete) + { + if (hardDelete) + { + using var call = StreamsClient.TombstoneAsync(new TombstoneReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) } + } + }, GetCallOptions()); + await call.ResponseAsync; + return; + } + + using var deleteCall = StreamsClient.DeleteAsync(new DeleteReq + { + Options = new() + { + Any = new Empty(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) } + } + }, GetCallOptions()); + await deleteCall.ResponseAsync; + } + + protected async Task ReadLastStreamRevision(string stream) + { + using var call = StreamsClient.Read(new ReadReq + { + Options = new() + { + Stream = new() + { + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) }, + End = new Empty() + }, + ReadDirection = ReadReq.Types.Options.Types.ReadDirection.Backwards, + Count = 1, + NoFilter = new Empty(), + UuidOption = new() { Structured = new Empty() } + } + }, GetCallOptions()); + + var response = await call.ResponseStream.ReadAllAsync() + .FirstAsync(response => response.Event is not null); + return checked((long)response.Event.Event.StreamRevision); + } + + private static CallOptions GetCallOptions() => new( + credentials: AdminCredentials, + deadline: DateTime.UtcNow.AddSeconds(30)); + [OneTimeTearDown] public override async Task TestFixtureTearDown() { - NodeConnection?.Dispose(); + Channel?.Dispose(); await Node.Shutdown(); await base.TestFixtureTearDown(); } diff --git a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs index c070aac182..38fe6ab9ac 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Enumerators/Enumerator.StreamSubscription.CombinationTests.cs @@ -1,14 +1,13 @@ using System; +using System.Linq; using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; using EventStore.Core.Data; using EventStore.Core.Services; using EventStore.Core.Services.Transport.Common; using EventStore.Core.Services.Transport.Enumerators; using EventStore.Core.Services.UserManagement; using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; namespace EventStore.Core.Tests.Services.Transport.Enumerators; @@ -678,25 +677,20 @@ private async Task WriteExistingEvents() private async Task WriteEvents(int count) { - var events = new EventData[count]; - for (var i = 0; i < count; i++) - { - events[i] = new EventData(Guid.NewGuid(), "type", true, "{}"u8.ToArray(), Array.Empty()); - } - - await NodeConnection.AppendToStreamAsync(_stream, ExpectedVersion.Any, events); + await AppendToStream(_stream, + Enumerable.Range(0, count) + .Select(_ => ("type", "{}"u8.ToArray(), Array.Empty()))); } private async Task WriteEvent(string stream, string eventType, string data, string metadata) { data ??= string.Empty; metadata ??= string.Empty; - var eventData = new EventData(Guid.NewGuid(), eventType, true, Encoding.UTF8.GetBytes(data), Encoding.UTF8.GetBytes(metadata)); - await NodeConnection.AppendToStreamAsync(stream, ExpectedVersion.Any, eventData); + await AppendToStream(stream, eventType, data, metadata); } private Task WriteEvent() => WriteEvent(_stream, "type", "{}", null); - private async Task SoftDelete() => await NodeConnection.DeleteStreamAsync(_stream, Data.ExpectedVersion.Any, hardDelete: false); - private async Task Tombstone() => await NodeConnection.DeleteStreamAsync(_stream, Data.ExpectedVersion.Any, hardDelete: true); + private Task SoftDelete() => DeleteStream(_stream, hardDelete: false); + private Task Tombstone() => DeleteStream(_stream, hardDelete: true); private async Task RevokeAccessWithStreamAcl() => await WriteEvent(SystemStreams.MetastreamOf(_stream), "$metadata", @"{ ""$acl"": { ""$r"": [] } }", null); private async Task RevokeAccessWithDefaultAcl() => await WriteEvent(SystemStreams.SettingsStream, "update-default-acl", @"{ ""$userStreamAcl"" : { ""$r"" : [] } }", null); @@ -825,9 +819,7 @@ private async Task SetUpForEphemeralStream() return; } - var readResult = await NodeConnection.ReadStreamEventsBackwardAsync(_stream, -1, 1, resolveLinkTos: false); - - _ephemeralStreamLastEventNumber = readResult.LastEventNumber; + _ephemeralStreamLastEventNumber = await ReadLastStreamRevision(_stream); _nextEventNumber = CalculateNextEventNumberFromCheckpoint(); } diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs index 828810fab5..e4396f5693 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodecTests.cs @@ -415,8 +415,6 @@ public void not_handled_leader_info_round_trips() Guid.NewGuid(), ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, new ClientMessage.NotHandled.Types.LeaderInfo( - new DnsEndPoint("leader-tcp.internal", 1113), - true, new DnsEndPoint("leader-http.internal", 2113))); var decoded = RoundTripResponse(message); @@ -425,28 +423,6 @@ public void not_handled_leader_info_round_trips() { Assert.That(decoded.CorrelationId, Is.EqualTo(message.CorrelationId)); Assert.That(decoded.Reason, Is.EqualTo(message.Reason)); - Assert.That(decoded.LeaderInfo.IsSecure, Is.True); - Assert.That(decoded.LeaderInfo.ExternalTcp, Is.EqualTo(message.LeaderInfo.ExternalTcp)); - Assert.That(decoded.LeaderInfo.Http, Is.EqualTo(message.LeaderInfo.Http)); - }); - } - - [Test] - public void not_handled_leader_info_without_external_tcp_round_trips_as_null() - { - var message = new ClientMessage.NotHandled( - Guid.NewGuid(), - ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, - new ClientMessage.NotHandled.Types.LeaderInfo( - null, - false, - new DnsEndPoint("leader-http.internal", 2113))); - - var decoded = RoundTripResponse(message); - - Assert.Multiple(() => - { - Assert.That(decoded.LeaderInfo.ExternalTcp, Is.Null); Assert.That(decoded.LeaderInfo.Http, Is.EqualTo(message.LeaderInfo.Http)); }); } @@ -454,9 +430,9 @@ public void not_handled_leader_info_without_external_tcp_round_trips_as_null() [Test] public void not_authenticated_round_trips() { - var message = new TcpMessage.NotAuthenticated(Guid.NewGuid(), "not authenticated"); + var message = new ClientMessage.NotAuthenticated(Guid.NewGuid(), "not authenticated"); - var decoded = RoundTripResponse(message); + var decoded = RoundTripResponse(message); Assert.Multiple(() => { diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/MonitoringTests/TcpStatsTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/MonitoringTests/TcpStatsTests.cs deleted file mode 100644 index f46a618245..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/MonitoringTests/TcpStatsTests.cs +++ /dev/null @@ -1,136 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Client.Monitoring; -using EventStore.Core.Bus; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using Grpc.Core; -using NUnit.Framework; -using CoreTcpConnectionStats = EventStore.Core.Messages.MonitoringMessage.TcpConnectionStats; - -namespace EventStore.Core.Tests.Services.Transport.Grpc.MonitoringTests; - -[TestFixture] -public class TcpStatsTests -{ - private readonly Guid _connectionId = Guid.Parse("1e1c6d68-3c7c-446f-915e-8bdf8d35e122"); - private TcpStatsResp _response; - private CapturingPublisher _publisher; - - [SetUp] - public async Task SetUp() - { - _publisher = new CapturingPublisher(new List { - new() { - RemoteEndPoint = "127.0.0.1:1113", - LocalEndPoint = "127.0.0.1:2113", - ClientConnectionName = "test-connection", - ConnectionId = _connectionId, - TotalBytesSent = 123, - TotalBytesReceived = 456, - PendingSendBytes = 7, - PendingReceivedBytes = 8, - IsExternalConnection = true, - IsSslConnection = true - }, - new() { - ConnectionId = Guid.Empty - } - }); - var serviceType = typeof(MonitoringMessage).Assembly.GetType( - "EventStore.Core.Services.Transport.Grpc.Monitoring", - throwOnError: true); - var service = Activator.CreateInstance( - serviceType!, - BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, - binder: null, - args: [_publisher], - culture: null); - - var task = (Task)serviceType!.GetMethod(nameof(EventStore.Client.Monitoring.Monitoring.MonitoringBase.TcpStats))! - .Invoke(service, [new TcpStatsReq(), TestServerCallContext.Instance])!; - _response = await task; - } - - [Test] - public void should_request_fresh_tcp_connection_stats() - { - Assert.IsTrue(_publisher.RequestedTcpStats); - } - - [Test] - public void should_return_the_tcp_connection_stats() - { - Assert.AreEqual(2, _response.Connections.Count); - } - - [Test] - public void should_map_all_tcp_connection_fields() - { - var connection = _response.Connections[0]; - - Assert.AreEqual("127.0.0.1:1113", connection.RemoteEndpoint); - Assert.AreEqual("127.0.0.1:2113", connection.LocalEndpoint); - Assert.AreEqual("test-connection", connection.ClientConnectionName); - Assert.AreEqual(_connectionId.ToString("D"), connection.ConnectionId); - Assert.AreEqual(123, connection.TotalBytesSent); - Assert.AreEqual(456, connection.TotalBytesReceived); - Assert.AreEqual(7, connection.PendingSendBytes); - Assert.AreEqual(8, connection.PendingReceivedBytes); - Assert.IsTrue(connection.IsExternalConnection); - Assert.IsTrue(connection.IsSslConnection); - } - - [Test] - public void should_map_null_strings_to_empty_values() - { - var connection = _response.Connections[1]; - - Assert.AreEqual(string.Empty, connection.RemoteEndpoint); - Assert.AreEqual(string.Empty, connection.LocalEndpoint); - Assert.AreEqual(string.Empty, connection.ClientConnectionName); - } - - private sealed class CapturingPublisher(List connectionStats) : IPublisher - { - public bool RequestedTcpStats { get; private set; } - - public void Publish(Message message) - { - if (message is not MonitoringMessage.GetFreshTcpConnectionStats request) - { - throw new InvalidOperationException($"Unexpected message {message.GetType().Name}"); - } - - RequestedTcpStats = true; - request.Envelope.ReplyWith(new MonitoringMessage.GetFreshTcpConnectionStatsCompleted(connectionStats)); - } - } - - private sealed class TestServerCallContext : ServerCallContext - { - public static readonly TestServerCallContext Instance = new(); - - private TestServerCallContext() - { - } - - protected override string MethodCore => nameof(EventStore.Client.Monitoring.Monitoring.MonitoringBase.TcpStats); - protected override string HostCore => "localhost"; - protected override string PeerCore => "ipv4:127.0.0.1:0"; - protected override DateTime DeadlineCore => DateTime.MaxValue; - protected override Metadata RequestHeadersCore { get; } = new(); - protected override CancellationToken CancellationTokenCore => CancellationToken.None; - protected override Metadata ResponseTrailersCore { get; } = new(); - protected override Status StatusCore { get; set; } - protected override WriteOptions WriteOptionsCore { get; set; } - protected override AuthContext AuthContextCore => new(null, new Dictionary>()); - protected override IDictionary UserStateCore { get; } = new Dictionary(); - protected override ContextPropagationToken CreatePropagationTokenCore(ContextPropagationOptions options) => - throw new NotSupportedException(); - protected override Task WriteResponseHeadersAsyncCore(Metadata responseHeaders) => Task.CompletedTask; - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs index a6eaedf281..5d85c015b5 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs @@ -432,10 +432,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( DateTime.UtcNow, VNodeState.Leader, true, - new DnsEndPoint("leader-replication.internal", 1112), - null, - null, - null, new DnsEndPoint("leader.internal", 2113), null, 0, @@ -444,7 +440,6 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( 0, 0, 0, - 0, Guid.NewGuid(), 0, false); diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/DeleteTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/DeleteTests.cs index 78bc3f2059..efd40bfb12 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/DeleteTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/DeleteTests.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Threading.Tasks; using EventStore.Client.Streams; -using EventStore.ClientAPI; using EventStore.Core.Services; using EventStore.Core.Services.Transport.Common; using EventStore.Core.Services.Transport.Grpc; @@ -207,7 +206,7 @@ await call.RequestStream.WriteAsync(new() { Metadata.Type, SystemEventTypes.StreamMetadata }, { Metadata.ContentType, Metadata.ContentTypes.ApplicationJson } }, - Data = ByteString.CopyFromUtf8(StreamMetadata.Build().Build().AsJsonString()) + Data = ByteString.CopyFromUtf8("{}") } }); diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/ReadStreamsForwardTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/ReadStreamsForwardTests.cs index eee159ad67..0c7335aab5 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/ReadStreamsForwardTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/StreamsTests/ReadStreamsForwardTests.cs @@ -1,9 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using System.Threading.Tasks; using EventStore.Client.Streams; -using EventStore.ClientAPI; using EventStore.Core.Services.Transport.Grpc; using Google.Protobuf; using Grpc.Core; @@ -60,8 +60,7 @@ await call.RequestStream.WriteAsync(new() [GrpcMetadata.Type] = "$metadata", [GrpcMetadata.ContentType] = GrpcMetadata.ContentTypes.ApplicationJson }, - Data = ByteString.CopyFrom(StreamMetadata.Build().SetTruncateBefore(81).Build() - .AsJsonBytes()) + Data = ByteString.CopyFrom(Encoding.UTF8.GetBytes("{\"$tb\":81}")) } }); diff --git a/src/EventStore.Core.Tests/Services/Transport/Http/Authorization/authorization_tests.cs b/src/EventStore.Core.Tests/Services/Transport/Http/Authorization/authorization_tests.cs index 97d8bfd2f2..788c296bd3 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Http/Authorization/authorization_tests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Http/Authorization/authorization_tests.cs @@ -5,10 +5,8 @@ using System.Net.Http.Headers; using System.Threading.Tasks; using EventStore.Client.Users; -using EventStore.ClientAPI; using EventStore.Common.Utils; using EventStore.Core.Services; -using EventStore.Core.Tests.ClientAPI.Helpers; using EventStore.Core.Tests.Helpers; using Grpc.Core; using Grpc.Net.Client; @@ -146,12 +144,6 @@ public override async Task TestFixtureSetUp() await base.TestFixtureSetUp(); _node = new MiniNode(PathName); await _node.Start(); - await _node.WaitForTcpEndPoint().WithTimeout(ReadinessTimeout); - - using var connection = await TestConnectionLifecycle.ReconnectUntilReady( - () => TestConnection.CreateMiniNodeClient(_node.TcpEndPoint), - conn => conn.ReadAllEventsForwardAsync(Position.Start, 1, false, DefaultData.AdminCredentials), - ReadinessTimeout); _httpClients["Admin"] = CreateHttpClient("admin", "changeit"); _httpClients["Ops"] = CreateHttpClient("ops", "changeit"); diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpClientDispatcherTests.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpClientDispatcherTests.cs deleted file mode 100644 index b55b4d7d63..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpClientDispatcherTests.cs +++ /dev/null @@ -1,317 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using EventStore.Client.Messages; -using EventStore.Core.Authentication.InternalAuthentication; -using EventStore.Core.Bus; -using EventStore.Core.Data; -using EventStore.Core.LogV2; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Services; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Services.UserManagement; -using EventStore.Core.Tests.Authentication; -using EventStore.Core.Tests.Authorization; -using EventStore.Core.TransactionLog.LogRecords; -using EventStore.Core.Util; -using NUnit.Framework; -using EventRecord = EventStore.Core.Data.EventRecord; -using ResolvedEvent = EventStore.Core.Data.ResolvedEvent; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class TcpClientDispatcherTests -{ - private readonly NoopEnvelope _envelope = new NoopEnvelope(); - - private ClientTcpDispatcher _dispatcher; - private TcpConnectionManager _connection; - - [OneTimeSetUp] - public void Setup() - { - _dispatcher = new ClientTcpDispatcher(2000); - - var dummyConnection = new DummyTcpConnection(); - _connection = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), new InternalAuthenticationProvider( - InMemoryBus.CreateTest(), new Core.Helpers.IODispatcher(new SynchronousScheduler(), new NoopEnvelope()), - new StubPasswordHashAlgorithm(), 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { }, - Opts.ConnectionPendingSendBytesThresholdDefault, Opts.ConnectionQueueSizeThresholdDefault); - } - - [Test] - public void - when_wrapping_read_stream_events_forward_and_stream_was_deleted_should_not_downgrade_last_event_number_for_v2_clients() - { - var msg = new ClientMessage.ReadStreamEventsForwardCompleted(Guid.NewGuid(), "test-stream", 0, 100, - ReadStreamResult.StreamDeleted, new ResolvedEvent[0], new StreamMetadata(), - true, "", -1, long.MaxValue, true, 1000); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ReadStreamEventsForwardCompleted, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - - Assert.AreEqual(long.MaxValue, dto.LastEventNumber, "Last Event Number"); - } - - [Test] - public void - when_wrapping_read_stream_events_backward_and_stream_was_deleted_should_not_downgrade_last_event_number_for_v2_clients() - { - var msg = new ClientMessage.ReadStreamEventsBackwardCompleted(Guid.NewGuid(), "test-stream", 0, 100, - ReadStreamResult.StreamDeleted, new ResolvedEvent[0], new StreamMetadata(), - true, "", -1, long.MaxValue, true, 1000); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ReadStreamEventsBackwardCompleted, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - - Assert.AreEqual(long.MaxValue, dto.LastEventNumber, "Last Event Number"); - } - - [Test] - public void - when_wrapping_read_all_events_forward_completed_with_deleted_event_should_not_downgrade_last_event_number_for_v2_clients() - { - var events = new ResolvedEvent[] { - ResolvedEvent.ForUnresolvedEvent(CreateDeletedEventRecord(), 0), - }; - var msg = new ClientMessage.ReadAllEventsForwardCompleted(Guid.NewGuid(), ReadAllResult.Success, "", events, - new StreamMetadata(), true, 10, new TFPos(0, 0), - new TFPos(200, 200), new TFPos(0, 0), 100); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ReadAllEventsForwardCompleted, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(1, dto.Events.Count(), "Number of events"); - - Assert.AreEqual(long.MaxValue, dto.Events[0].Event.EventNumber, "Event Number"); - } - - [Test] - public void - when_wrapping_read_all_events_forward_completed_with_link_to_deleted_event_should_not_downgrade_version_for_v2_clients() - { - var events = new ResolvedEvent[] { - ResolvedEvent.ForResolvedLink(CreateLinkEventRecord(), CreateDeletedEventRecord(), 100) - }; - var msg = new ClientMessage.ReadAllEventsForwardCompleted(Guid.NewGuid(), ReadAllResult.Success, "", events, - new StreamMetadata(), true, 10, new TFPos(0, 0), - new TFPos(200, 200), new TFPos(0, 0), 100); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ReadAllEventsForwardCompleted, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(1, dto.Events.Count(), "Number of events"); - - Assert.AreEqual(0, dto.Events[0].Event.EventNumber, "Event Number"); - Assert.AreEqual(long.MaxValue, dto.Events[0].Link.EventNumber, "Link Event Number"); - } - - [Test] - public void - when_wrapping_read_all_events_backward_completed_with_deleted_event_should_not_downgrade_version_for_v2_clients() - { - var events = new ResolvedEvent[] { - ResolvedEvent.ForUnresolvedEvent(CreateDeletedEventRecord(), 0), - }; - var msg = new ClientMessage.ReadAllEventsBackwardCompleted(Guid.NewGuid(), ReadAllResult.Success, "", - events, - new StreamMetadata(), true, 10, new TFPos(0, 0), - new TFPos(200, 200), new TFPos(0, 0), 100); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ReadAllEventsBackwardCompleted, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(1, dto.Events.Count(), "Number of events"); - - Assert.AreEqual(long.MaxValue, dto.Events[0].Event.EventNumber, "Event Number"); - } - - [Test] - public void - when_wrapping_read_all_events_backward_completed_with_link_to_deleted_event_should_not_downgrade_version_for_v2_clients() - { - var events = new ResolvedEvent[] { - ResolvedEvent.ForResolvedLink(CreateLinkEventRecord(), CreateDeletedEventRecord(), 100) - }; - var msg = new ClientMessage.ReadAllEventsBackwardCompleted(Guid.NewGuid(), ReadAllResult.Success, "", - events, - new StreamMetadata(), true, 10, new TFPos(0, 0), - new TFPos(200, 200), new TFPos(0, 0), 100); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ReadAllEventsBackwardCompleted, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(1, dto.Events.Count(), "Number of events"); - - Assert.AreEqual(0, dto.Events[0].Event.EventNumber, "Event Number"); - Assert.AreEqual(long.MaxValue, dto.Events[0].Link.EventNumber, "Link Event Number"); - } - - [Test] - public void - when_wrapping_stream_event_appeared_with_deleted_event_should_not_downgrade_version_for_v2_clients() - { - var msg = new ClientMessage.StreamEventAppeared(Guid.NewGuid(), - ResolvedEvent.ForUnresolvedEvent(CreateDeletedEventRecord(), 0)); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.StreamEventAppeared, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(long.MaxValue, dto.Event.Event.EventNumber, "Event Number"); - } - - [Test] - public void - when_wrapping_subscribe_to_stream_confirmation_when_stream_deleted_should_not_downgrade_version_for_v2_clients() - { - var msg = new ClientMessage.SubscriptionConfirmation(Guid.NewGuid(), 100, long.MaxValue); - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.SubscriptionConfirmation, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(long.MaxValue, dto.LastEventNumber, "Last Event Number"); - } - - [Test] - public void - when_wrapping_subscribe_to_stream_confirmation_when_stream_deleted_should_not_downgrade_last_event_number_for_v2_clients() - { - var msg = new ClientMessage.SubscriptionConfirmation(Guid.NewGuid(), 100, long.MaxValue); - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.SubscriptionConfirmation, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(long.MaxValue, dto.LastEventNumber, "Last Event Number"); - } - - [Test] - public void - when_wrapping_stream_event_appeared_with_link_to_deleted_event_should_not_downgrade_version_for_v2_clients() - { - var msg = new ClientMessage.StreamEventAppeared(Guid.NewGuid(), - ResolvedEvent.ForResolvedLink(CreateLinkEventRecord(), CreateDeletedEventRecord(), 0)); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.StreamEventAppeared, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(0, dto.Event.Event.EventNumber, "Event Number"); - Assert.AreEqual(long.MaxValue, dto.Event.Link.EventNumber, "Link Event Number"); - } - - [Test] - public void - when_wrapping_persistent_subscription_confirmation_when_stream_deleted_should_not_downgrade_last_event_number_for_v2_clients() - { - var msg = new ClientMessage.PersistentSubscriptionConfirmation("subscription", Guid.NewGuid(), 100, - long.MaxValue); - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.PersistentSubscriptionConfirmation, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(long.MaxValue, dto.LastEventNumber, "Last event number"); - } - - [Test] - public void - when_wrapping_scavenge_started_response_should_return_result_and_scavengeId_for_v2_clients() - { - var scavengeId = Guid.NewGuid().ToString(); - var msg = new ClientMessage.ScavengeDatabaseStartedResponse(Guid.NewGuid(), scavengeId); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ScavengeDatabaseResponse, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(dto.Result, ScavengeDatabaseResponse.Types.ScavengeResult.Started); - Assert.AreEqual(dto.ScavengeId, scavengeId); - } - - [Test] - public void - when_wrapping_scavenge_inprogress_response_should_return_result_and_scavengeId_for_v2_clients() - { - var scavengeId = Guid.NewGuid().ToString(); - var msg = new ClientMessage.ScavengeDatabaseInProgressResponse(Guid.NewGuid(), scavengeId, reason: "In Progress"); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ScavengeDatabaseResponse, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(dto.Result, ScavengeDatabaseResponse.Types.ScavengeResult.InProgress); - Assert.AreEqual(dto.ScavengeId, scavengeId); - } - - [Test] - public void - when_wrapping_scavenge_unauthorized_response_should_return_result_and_scavengeId_for_v2_clients() - { - var scavengeId = Guid.NewGuid().ToString(); - var msg = new ClientMessage.ScavengeDatabaseUnauthorizedResponse(Guid.NewGuid(), scavengeId, "Unauthorized"); - - var package = _dispatcher.WrapMessage(msg, (byte)ClientVersion.V2); - Assert.IsNotNull(package, "Package is null"); - Assert.AreEqual(TcpCommand.ScavengeDatabaseResponse, package.Value.Command, "TcpCommand"); - - var dto = package.Value.Data.Deserialize(); - Assert.IsNotNull(dto, "DTO is null"); - Assert.AreEqual(dto.Result, ScavengeDatabaseResponse.Types.ScavengeResult.Unauthorized); - Assert.AreEqual(dto.ScavengeId, scavengeId); - } - - private EventRecord CreateDeletedEventRecord() - { - return new EventRecord(long.MaxValue, - LogRecord.DeleteTombstone(new LogV2RecordFactory(), 0, Guid.NewGuid(), Guid.NewGuid(), - "test-stream", "test-type", long.MaxValue), "test-stream", SystemEventTypes.StreamDeleted); - } - - private EventRecord CreateLinkEventRecord() - { - return new EventRecord(0, LogRecord.Prepare(new LogV2RecordFactory(), 100, Guid.NewGuid(), Guid.NewGuid(), 0, 0, - "link-stream", -1, PrepareFlags.SingleWrite | PrepareFlags.Data, SystemEventTypes.LinkTo, - Encoding.UTF8.GetBytes(string.Format("{0}@test-stream", long.MaxValue)), new byte[0]), "link-stream", SystemEventTypes.LinkTo); - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionManagerTests.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionManagerTests.cs deleted file mode 100644 index ff724c7917..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionManagerTests.cs +++ /dev/null @@ -1,348 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Core.Authentication.InternalAuthentication; -using EventStore.Core.Bus; -using EventStore.Core.Data; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Services; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Settings; -using EventStore.Core.Tests.Authentication; -using EventStore.Core.Tests.Authorization; -using EventStore.Core.TransactionLog.LogRecords; -using EventStore.Core.Util; -using EventStore.Transport.Tcp; -using NUnit.Framework; -using EventRecord = EventStore.Core.Data.EventRecord; -using ResolvedEvent = EventStore.Core.Data.ResolvedEvent; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class TcpConnectionManagerTests -{ - private int _connectionPendingSendBytesThreshold = 10 * 1024; - private int _connectionQueueSizeThreshold = 50000; - - [Test] - public void when_handling_trusted_write_on_external_service() - { - var package = new TcpPackage(TcpCommand.WriteEvents, TcpFlags.TrustedWrite, Guid.NewGuid(), null, null, - new byte[] { }); - - var dummyConnection = new DummyTcpConnection(); - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), - new InternalAuthenticationProvider( - InMemoryBus.CreateTest(), new Core.Helpers.IODispatcher(new SynchronousScheduler(), new NoopEnvelope()), - new StubPasswordHashAlgorithm(), 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { }, - _connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold); - - tcpConnectionManager.ProcessPackage(package); - - var data = dummyConnection.ReceivedData.Last(); - var receivedPackage = TcpPackage.FromArraySegment(data); - - Assert.AreEqual(receivedPackage.Command, TcpCommand.BadRequest, "Expected Bad Request but got {0}", - receivedPackage.Command); - } - - [Test] - public void when_handling_trusted_write_on_internal_service() - { - ManualResetEvent waiter = new ManualResetEvent(false); - ClientMessage.WriteEvents publishedWrite = null; - var evnt = new Event(Guid.NewGuid(), "TestEventType", true, new byte[] { }, new byte[] { }); - var write = new WriteEvents( - Guid.NewGuid().ToString(), - ExpectedVersion.Any, - new[] { - new NewEvent(evnt.EventId.ToByteArray(), evnt.EventType, evnt.IsJson ? 1 : 0, 0, - evnt.Data, evnt.Metadata) - }, - false); - - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), write.Serialize()); - var dummyConnection = new DummyTcpConnection(); - var publisher = new SynchronousScheduler(); - - publisher.Subscribe(new AdHocHandler(x => - { - publishedWrite = x; - waiter.Set(); - })); - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.Internal, new ClientTcpDispatcher(2000), - publisher, dummyConnection, publisher, - new InternalAuthenticationProvider(publisher, new Core.Helpers.IODispatcher(publisher, new NoopEnvelope()), - new StubPasswordHashAlgorithm(), 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { }, - _connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold); - - tcpConnectionManager.ProcessPackage(package); - - if (!waiter.WaitOne(TimeSpan.FromSeconds(5))) - { - throw new Exception("Timed out waiting for events."); - } - - Assert.AreEqual(evnt.EventId, publishedWrite.Events.First().EventId, - "Expected the published write to be the event that was sent through the tcp connection manager to be the event {0} but got {1}", - evnt.EventId, publishedWrite.Events.First().EventId); - } - - [Test] - public void - when_limit_pending_and_sending_message_smaller_than_threshold_and_pending_bytes_over_threshold_should_close_connection() - { - var mre = new ManualResetEventSlim(); - - var messageSize = _connectionPendingSendBytesThreshold / 2; - var evnt = new EventRecord(0, 0, Guid.NewGuid(), Guid.NewGuid(), 0, 0, "testStream", 0, DateTime.Now, - PrepareFlags.None, "eventType", new byte[messageSize], new byte[0]); - var record = ResolvedEvent.ForUnresolvedEvent(evnt, null); - var message = new ClientMessage.ReadEventCompleted(Guid.NewGuid(), "testStream", ReadEventResult.Success, - record, StreamMetadata.Empty, false, ""); - - var dummyConnection = new DummyTcpConnection(); - dummyConnection.PendingSendBytes = _connectionPendingSendBytesThreshold + 1000; - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), - new InternalAuthenticationProvider( - InMemoryBus.CreateTest(), new Core.Helpers.IODispatcher(new SynchronousScheduler(), - new NoopEnvelope()), null, 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); }, - _connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold); - - tcpConnectionManager.SendMessage(message); - - if (!mre.Wait(2000)) - { - Assert.Fail("Timed out waiting for connection to close"); - } - } - - [Test] - public void - when_limit_pending_and_sending_message_larger_than_pending_bytes_threshold_but_no_bytes_pending_should_not_close_connection() - { - var messageSize = _connectionPendingSendBytesThreshold + 1000; - var evnt = new EventRecord(0, 0, Guid.NewGuid(), Guid.NewGuid(), 0, 0, "testStream", 0, DateTime.Now, - PrepareFlags.None, "eventType", new byte[messageSize], new byte[0]); - var record = ResolvedEvent.ForUnresolvedEvent(evnt, null); - var message = new ClientMessage.ReadEventCompleted(Guid.NewGuid(), "testStream", ReadEventResult.Success, - record, StreamMetadata.Empty, false, ""); - - var dummyConnection = new DummyTcpConnection(); - dummyConnection.PendingSendBytes = 0; - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), - new InternalAuthenticationProvider(InMemoryBus.CreateTest(), - new Core.Helpers.IODispatcher(new SynchronousScheduler(), new NoopEnvelope()), null, 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { }, - _connectionPendingSendBytesThreshold, _connectionQueueSizeThreshold); - - tcpConnectionManager.SendMessage(message); - - var data = dummyConnection.ReceivedData.Last(); - var receivedPackage = TcpPackage.FromArraySegment(data); - - Assert.AreEqual(receivedPackage.Command, TcpCommand.ReadEventCompleted, - "Expected ReadEventCompleted but got {0}", receivedPackage.Command); - } - - [Test] - public void - when_not_limit_pending_and_sending_message_smaller_than_threshold_and_pending_bytes_over_threshold_should_not_close_connection() - { - var mre = new ManualResetEventSlim(); - - var messageSize = _connectionPendingSendBytesThreshold / 2; - var evnt = new EventRecord(0, 0, Guid.NewGuid(), Guid.NewGuid(), 0, 0, "testStream", 0, DateTime.Now, - PrepareFlags.None, "eventType", new byte[messageSize], new byte[0]); - var record = ResolvedEvent.ForUnresolvedEvent(evnt, null); - var message = new ClientMessage.ReadEventCompleted(Guid.NewGuid(), "testStream", ReadEventResult.Success, - record, StreamMetadata.Empty, false, ""); - - var dummyConnection = new DummyTcpConnection(); - dummyConnection.PendingSendBytes = _connectionPendingSendBytesThreshold + 1000; - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), - new InternalAuthenticationProvider(InMemoryBus.CreateTest(), - new Core.Helpers.IODispatcher(new SynchronousScheduler(), new NoopEnvelope()), null, 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); }, - ESConsts.UnrestrictedPendingSendBytes, ESConsts.MaxConnectionQueueSize); - - tcpConnectionManager.SendMessage(message); - - var data = dummyConnection.ReceivedData.Last(); - var receivedPackage = TcpPackage.FromArraySegment(data); - - Assert.AreEqual(receivedPackage.Command, TcpCommand.ReadEventCompleted, - "Expected ReadEventCompleted but got {0}", receivedPackage.Command); - } - - [Test] - public void - when_send_queue_size_is_smaller_than_threshold_should_not_close_connection() - { - var mre = new ManualResetEventSlim(); - - var messageSize = _connectionPendingSendBytesThreshold; - var evnt = new EventRecord(0, 0, Guid.NewGuid(), Guid.NewGuid(), 0, 0, "testStream", 0, DateTime.Now, - PrepareFlags.None, "eventType", new byte[messageSize], new byte[0]); - var record = ResolvedEvent.ForUnresolvedEvent(evnt, null); - var message = new ClientMessage.ReadEventCompleted(Guid.NewGuid(), "testStream", ReadEventResult.Success, - record, StreamMetadata.Empty, false, ""); - - var dummyConnection = new DummyTcpConnection(); - dummyConnection.SendQueueSize = ESConsts.MaxConnectionQueueSize - 1; - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), - new InternalAuthenticationProvider(InMemoryBus.CreateTest(), - new Core.Helpers.IODispatcher(new SynchronousScheduler(), new NoopEnvelope()), null, 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); }, - ESConsts.UnrestrictedPendingSendBytes, ESConsts.MaxConnectionQueueSize); - - tcpConnectionManager.SendMessage(message); - - var data = dummyConnection.ReceivedData.Last(); - var receivedPackage = TcpPackage.FromArraySegment(data); - - Assert.AreEqual(receivedPackage.Command, TcpCommand.ReadEventCompleted, - "Expected ReadEventCompleted but got {0}", receivedPackage.Command); - } - - [Test] - public void - when_send_queue_size_is_larger_than_threshold_should_close_connection() - { - var mre = new ManualResetEventSlim(); - - var messageSize = _connectionPendingSendBytesThreshold; - var evnt = new EventRecord(0, 0, Guid.NewGuid(), Guid.NewGuid(), 0, 0, "testStream", 0, DateTime.Now, - PrepareFlags.None, "eventType", new byte[messageSize], new byte[0]); - var record = ResolvedEvent.ForUnresolvedEvent(evnt, null); - var message = new ClientMessage.ReadEventCompleted(Guid.NewGuid(), "testStream", ReadEventResult.Success, - record, StreamMetadata.Empty, false, ""); - - var dummyConnection = new DummyTcpConnection(); - dummyConnection.SendQueueSize = ESConsts.MaxConnectionQueueSize + 1; - - var tcpConnectionManager = new TcpConnectionManager( - Guid.NewGuid().ToString(), TcpServiceType.External, new ClientTcpDispatcher(2000), - new SynchronousScheduler(), dummyConnection, new SynchronousScheduler(), - new InternalAuthenticationProvider(InMemoryBus.CreateTest(), - new Core.Helpers.IODispatcher(new SynchronousScheduler(), new NoopEnvelope()), null, 1, false, DefaultData.DefaultUserOptions), - new AuthorizationGateway(new TestAuthorizationProvider()), - TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10), (man, err) => { mre.Set(); }, - ESConsts.UnrestrictedPendingSendBytes, ESConsts.MaxConnectionQueueSize); - - tcpConnectionManager.SendMessage(message); - - if (!mre.Wait(2000)) - { - Assert.Fail("Timed out waiting for connection to close"); - } - } -} - -internal class DummyTcpConnection : ITcpConnection -{ - public Guid ConnectionId - { - get { return _connectionId; } - set { _connectionId = value; } - } - private Guid _connectionId = Guid.NewGuid(); - public string ClientConnectionName - { - get { return _clientConnectionName; } - } - - public long TotalBytesSent { get; } - public long TotalBytesReceived { get; } - - public bool IsClosed - { - get { return false; } - } - - public IPEndPoint LocalEndPoint - { - get { return new IPEndPoint(IPAddress.Loopback, 2); } - } - - public IPEndPoint RemoteEndPoint - { - get { return new IPEndPoint(IPAddress.Loopback, 1); } - } - - private int _sendQueueSize; - public int SendQueueSize - { - get { return _sendQueueSize; } - set { _sendQueueSize = value; } - } - - private int _pendingSendBytes; - - public int PendingSendBytes - { - get { return _pendingSendBytes; } - set { _pendingSendBytes = value; } - } - - public event Action ConnectionClosed; - private string _clientConnectionName; - - public void Close(string reason) - { - var handler = ConnectionClosed; - if (handler != null) - { - handler(this, SocketError.Shutdown); - } - } - - public IEnumerable> ReceivedData; - - public void EnqueueSend(IEnumerable> data) - { - ReceivedData = data; - } - - public void ReceiveAsync(Action>> callback) - { - throw new NotImplementedException(); - } - - public void SetClientConnectionName(string clientConnectionName) - { - _clientConnectionName = clientConnectionName; - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionSslTests.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionSslTests.cs deleted file mode 100644 index 486d0462de..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionSslTests.cs +++ /dev/null @@ -1,176 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Common.Utils; -using EventStore.Transport.Tcp; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class TcpConnectionSslTests -{ - protected static Socket CreateListeningSocket() - { - var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); - listener.Listen(1); - return listener; - } - - private IEnumerable> GenerateData() - { - var data = new List>(); - data.Add(new ArraySegment(new byte[100])); - return data; - } - - [Test, Timeout(120000)] - public async Task no_data_should_be_dispatched_after_tcp_connection_closed() - { - for (int i = 0; i < 1000; i++) - { - bool closed = false; - bool dataReceivedAfterClose = false; - var listeningSocket = CreateListeningSocket(); - - var mre = new ManualResetEventSlim(false); - var clientTcpConnection = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - listeningSocket.LocalEndPoint.GetHost(), - null, - (IPEndPoint)listeningSocket.LocalEndPoint, - delegate - { return (true, null); }, - null, - new TcpClientConnector(), - TimeSpan.FromSeconds(5), - (conn) => mre.Set(), - (conn, error) => - { - Assert.Fail($"Connection failed: {error}"); - }, - false); - - var serverSocket = listeningSocket.Accept(); - var serverTcpConnection = TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), - (IPEndPoint)serverSocket.RemoteEndPoint, serverSocket, ssl_connections.GetServerCertificate, - null, delegate - { return (true, null); }, false); - - mre.Wait(TimeSpan.FromSeconds(3)); - try - { - clientTcpConnection.ConnectionClosed += (connection, error) => - { - Volatile.Write(ref closed, true); - }; - - clientTcpConnection.ReceiveAsync((connection, data) => - { - if (Volatile.Read(ref closed)) - { - dataReceivedAfterClose = true; - } - }); - - using (var b = new Barrier(2)) - { - Task sendData = Task.Factory.StartNew(() => - { - b.SignalAndWait(); - for (int i = 0; i < 1000; i++) - { - serverTcpConnection.EnqueueSend(GenerateData()); - } - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - - Task closeConnection = Task.Factory.StartNew(() => - { - b.SignalAndWait(); - serverTcpConnection.Close("Intentional close"); - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - - await Task.WhenAll(sendData, closeConnection); - Assert.False(dataReceivedAfterClose); - } - } - finally - { - clientTcpConnection.Close("Shut down"); - serverTcpConnection.Close("Shut down"); - listeningSocket.Dispose(); - } - } - } - - [Test, Timeout(120000)] - public void when_connection_closed_quickly_socket_should_be_properly_disposed() - { - for (int i = 0; i < 1000; i++) - { - var listeningSocket = CreateListeningSocket(); - ITcpConnection clientTcpConnection = null; - ITcpConnection serverTcpConnection = null; - Socket serverSocket = null; - try - { - ManualResetEventSlim mre = new ManualResetEventSlim(false); - - clientTcpConnection = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - listeningSocket.LocalEndPoint.GetHost(), - null, - (IPEndPoint)listeningSocket.LocalEndPoint, - delegate - { return (true, null); }, - null, - new TcpClientConnector(), - TimeSpan.FromSeconds(5), - (conn) => { }, - (conn, error) => { }, - false); - - clientTcpConnection.ConnectionClosed += (conn, error) => - { - mre.Set(); - }; - - serverSocket = listeningSocket.Accept(); - clientTcpConnection.Close("Intentional close"); - serverTcpConnection = TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), - (IPEndPoint)serverSocket.RemoteEndPoint, serverSocket, ssl_connections.GetServerCertificate, - null, delegate - { return (true, null); }, false); - - mre.Wait(TimeSpan.FromSeconds(10)); - SpinWait.SpinUntil(() => serverTcpConnection.IsClosed, TimeSpan.FromSeconds(10)); - - var disposed = false; - try - { - int x = serverSocket.Available; - } - catch (ObjectDisposedException) - { - disposed = true; - } - - Assert.AreEqual(true, disposed); - } - finally - { - clientTcpConnection?.Close("Shut down"); - serverTcpConnection?.Close("Shut down"); - listeningSocket.Dispose(); - serverSocket?.Dispose(); - } - } - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionTests.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionTests.cs deleted file mode 100644 index fb40e56fdd..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/TcpConnectionTests.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Transport.Tcp; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class TcpConnectionTests -{ - protected static Socket CreateListeningSocket() - { - var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - listener.Bind(new IPEndPoint(IPAddress.Loopback, 0)); - listener.Listen(1); - return listener; - } - - private IEnumerable> GenerateData() - { - var data = new List>(); - data.Add(new ArraySegment(new byte[100])); - return data; - } - - [Test, Timeout(120000)] - public async Task no_data_should_be_dispatched_after_tcp_connection_closed() - { - for (int i = 0; i < 1000; i++) - { - bool closed = false; - bool dataReceivedAfterClose = false; - var listeningSocket = CreateListeningSocket(); - TaskCompletionSource connectionResult = new(TaskCreationOptions.RunContinuationsAsynchronously); - - var clientTcpConnection = TcpConnection.CreateConnectingTcpConnection( - Guid.NewGuid(), - (IPEndPoint)listeningSocket.LocalEndPoint, - new TcpClientConnector(), - TimeSpan.FromSeconds(5), - (conn) => connectionResult.TrySetResult(SocketError.Success), - (conn, error) => connectionResult.TrySetResult(error), - false); - - var serverSocket = listeningSocket.Accept(); - var serverTcpConnection = TcpConnection.CreateAcceptedTcpConnection(Guid.NewGuid(), - (IPEndPoint)serverSocket.RemoteEndPoint, serverSocket, false); - - SocketError error = await connectionResult.Task.WithTimeout(); - Assert.AreEqual(SocketError.Success, error); - try - { - clientTcpConnection.ConnectionClosed += (connection, error) => - { - Volatile.Write(ref closed, true); - }; - - clientTcpConnection.ReceiveAsync((connection, data) => - { - if (Volatile.Read(ref closed)) - { - dataReceivedAfterClose = true; - } - }); - - using (var b = new Barrier(2)) - { - Task sendData = Task.Factory.StartNew(() => - { - b.SignalAndWait(); - for (int i = 0; i < 1000; i++) - { - serverTcpConnection.EnqueueSend(GenerateData()); - } - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - - Task closeConnection = Task.Factory.StartNew(() => - { - b.SignalAndWait(); - serverTcpConnection.Close("Intentional close"); - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default); - - await Task.WhenAll(sendData, closeConnection); - Assert.False(dataReceivedAfterClose); - } - } - finally - { - clientTcpConnection.Close("Shut down"); - serverTcpConnection.Close("Shut down"); - listeningSocket.Dispose(); - } - } - } - - [Test, Timeout(120000)] - public void when_connection_closed_quickly_socket_should_be_properly_disposed() - { - for (int i = 0; i < 1000; i++) - { - var listeningSocket = CreateListeningSocket(); - ITcpConnection clientTcpConnection = null; - ITcpConnection serverTcpConnection = null; - Socket serverSocket = null; - try - { - ManualResetEventSlim mre = new ManualResetEventSlim(false); - - clientTcpConnection = TcpConnection.CreateConnectingTcpConnection( - Guid.NewGuid(), - (IPEndPoint)listeningSocket.LocalEndPoint, - new TcpClientConnector(), - TimeSpan.FromSeconds(5), - (conn) => { }, - (conn, error) => { }, - false); - - clientTcpConnection.ConnectionClosed += (conn, error) => - { - mre.Set(); - }; - - serverSocket = listeningSocket.Accept(); - clientTcpConnection.Close("Intentional close"); - serverTcpConnection = TcpConnection.CreateAcceptedTcpConnection(Guid.NewGuid(), - (IPEndPoint)serverSocket.RemoteEndPoint, serverSocket, false); - - mre.Wait(TimeSpan.FromSeconds(10)); - SpinWait.SpinUntil(() => serverTcpConnection.IsClosed, TimeSpan.FromSeconds(10)); - - var disposed = false; - try - { - int x = serverSocket.Available; - } - catch (ObjectDisposedException) - { - disposed = true; - } - - Assert.AreEqual(true, disposed); - } - finally - { - clientTcpConnection?.Close("Shut down"); - serverTcpConnection?.Close("Shut down"); - listeningSocket.Dispose(); - serverSocket?.Dispose(); - } - } - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs deleted file mode 100644 index ffcb22e037..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/core_tcp_package.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using EventStore.Core.Services.Transport.Tcp; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class core_tcp_package -{ - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_login_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), null, "pa$$", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_password_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), "login", null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_null_exception_when_created_as_authorized_but_token_not_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, Guid.NewGuid(), null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_login_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), "login", null, - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_password_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), null, "pa$$", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_created_as_not_authorized_but_token_is_provided() - { - Assert.Throws(() => - new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, Guid.NewGuid(), "token", - new byte[] { 1, 2, 3 })); - } - - [Test] - public void not_authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, corrId, null, null, new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.None, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void not_authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.None, corrId, null, null, new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.None, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "login", "pa$$", - new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("login", pkg.Tokens["uid"]); - Assert.AreEqual("pa$$", pkg.Tokens["pwd"]); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "login", "pa$$", - new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("login", pkg.Tokens["uid"]); - Assert.AreEqual("pa$$", pkg.Tokens["pwd"]); - Assert.False(pkg.Tokens.TryGetValue("jwt", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void token_authorized_with_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "token", - new byte[] { 1, 2, 3 }); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("token", pkg.Tokens["jwt"]); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - - Assert.AreEqual(3, pkg.Data.Count); - Assert.AreEqual(1, pkg.Data.Array[pkg.Data.Offset + 0]); - Assert.AreEqual(2, pkg.Data.Array[pkg.Data.Offset + 1]); - Assert.AreEqual(3, pkg.Data.Array[pkg.Data.Offset + 2]); - } - - [Test] - public void token_authorized_with_empty_data_should_serialize_and_deserialize_correctly() - { - var corrId = Guid.NewGuid(); - var refPkg = new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, corrId, "token", - new byte[0]); - var bytes = refPkg.AsArraySegment(); - - var pkg = TcpPackage.FromArraySegment(bytes); - Assert.AreEqual(TcpCommand.BadRequest, pkg.Command); - Assert.AreEqual(TcpFlags.Authenticated, pkg.Flags); - Assert.AreEqual(corrId, pkg.CorrelationId); - Assert.AreEqual("token", pkg.Tokens["jwt"]); - Assert.False(pkg.Tokens.TryGetValue("uid", out _)); - Assert.False(pkg.Tokens.TryGetValue("pwd", out _)); - - Assert.AreEqual(0, pkg.Data.Count); - } - - [Test] - public void should_throw_argument_exception_when_login_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), new string('*', TcpPackage.MaxLoginLength + 1), "pa$$", new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_password_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), "login", new string('*', TcpPackage.MaxPasswordLength + 1), new byte[] { 1, 2, 3 })); - } - - [Test] - public void should_throw_argument_exception_when_token_too_long() - { - Assert.Throws(() => new TcpPackage(TcpCommand.BadRequest, TcpFlags.Authenticated, - Guid.NewGuid(), new string('*', TcpPackage.MaxTokenLength + 1), new byte[] { 1, 2, 3 })); - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs deleted file mode 100644 index f6165d0f82..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connection.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Reflection; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Tests.Helpers; -using EventStore.Transport.Tcp; -using NUnit.Framework; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class ssl_connections -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - private IPAddress _ip; - private int _port; - - [SetUp] - public void SetUp() - { - _ip = IPAddress.Loopback; - _port = PortsHelper.GetAvailablePort(_ip); - } - - [Test] - public void should_connect_to_each_other_and_send_data() - { - var serverEndPoint = new IPEndPoint(_ip, _port); - X509Certificate2 cert = GetServerCertificate(); - - var sent = new byte[1000]; - new Random().NextBytes(sent); - - using var received = new MemoryStream(); - - var done = new ManualResetEventSlim(); - - var listener = new TcpServerListener(serverEndPoint); - listener.StartListening((endPoint, socket) => - { - var ssl = TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), endPoint, socket, - () => cert, null, delegate - { return (true, null); }, - verbose: true); - ssl.ConnectionClosed += (x, y) => done.Set(); - if (ssl.IsClosed) - { - done.Set(); - } - - Action>> callback = null; - callback = (x, y) => - { - foreach (var arraySegment in y) - { - received.Write(arraySegment.Array, arraySegment.Offset, arraySegment.Count); - Log.Information("Received: {0} bytes, total: {1}.", arraySegment.Count, received.Length); - } - - if (received.Length >= sent.Length) - { - Log.Information("Done receiving..."); - done.Set(); - } - else - { - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - } - }; - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - }, "Secure"); - - var clientSsl = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - serverEndPoint.GetHost(), - null, - serverEndPoint, - delegate - { return (true, null); }, - null, - new TcpClientConnector(), - TcpConnectionManager.ConnectionTimeout, - conn => - { - Log.Information("Sending bytes..."); - conn.EnqueueSend(new[] { new ArraySegment(sent) }); - }, - (conn, err) => - { - Log.Error("Connecting failed: {0}.", err); - done.Set(); - }, - verbose: true); - - Assert.IsTrue(done.Wait(20000), "Took too long to receive completion."); - - Log.Information("Stopping listener..."); - listener.Stop(); - Log.Information("Closing client TLS connection..."); - clientSsl.Close("Normal close."); - Log.Information("Checking received data..."); - Assert.AreEqual(sent, received.ToArray()); - } - - private static X509Certificate2 _root, _server, _otherServer, _untrusted; - public static X509Certificate2 GetRootCertificate() - { - _root ??= GetCertificate("ca", loadKey: false); - return new X509Certificate2(_root); - } - - public static X509Certificate2 GetServerCertificate() - { - _server ??= GetCertificate("node1"); - return new X509Certificate2(_server); - } - - public static X509Certificate2 GetOtherServerCertificate() - { - _otherServer ??= GetCertificate("node2"); - return new X509Certificate2(_otherServer); - } - - public static X509Certificate2 GetUntrustedCertificate() - { - _untrusted ??= GetCertificate("untrusted"); - return new X509Certificate2(_untrusted); - } - - private static X509Certificate2 GetCertificate(string name, bool loadKey = true) - { - const string resourcePath = "EventStore.Core.Tests.Services.Transport.Tcp.test_certificates"; - - var certBytes = LoadResource($"{resourcePath}.{name}.{name}.crt"); - var certificate = X509Certificate2.CreateFromPem(Encoding.UTF8.GetString(certBytes)); - - if (!loadKey) - { - return certificate; - } - - var keyBytes = LoadResource($"{resourcePath}.{name}.{name}.key"); - using var rsa = RSA.Create(); - rsa.ImportFromPem(Encoding.UTF8.GetString(keyBytes)); - - using X509Certificate2 certWithKey = certificate.CopyWithPrivateKey(rsa); - - // recreate the certificate from a PKCS #12 bundle to work around: https://github.com/dotnet/runtime/issues/23749 - return X509CertificateLoader.LoadPkcs12(certWithKey.ExportToPkcs12(), string.Empty, X509KeyStorageFlags.Exportable); - } - - private static byte[] LoadResource(string resource) - { - using var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resource); - if (resourceStream == null) - { - return null; - } - - using var memStream = new MemoryStream(); - resourceStream.CopyTo(memStream); - return memStream.ToArray(); - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs deleted file mode 100644 index 1a80ae435e..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/ssl_connections_mutual_auth.cs +++ /dev/null @@ -1,149 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Security; -using System.Reflection; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Tests.Helpers; -using EventStore.Transport.Tcp; -using NUnit.Framework; -using Serilog; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class ssl_connections_mutual_auth -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - private IPAddress _ip; - private int _port; - - [SetUp] - public void SetUp() - { - _ip = IPAddress.Loopback; - _port = PortsHelper.GetAvailablePort(_ip); - } - - [TestCase(true, true, true, true, true)] //require valid server and client certificate - [TestCase(true, false, true, true, false)] //require valid server and client certificate - [TestCase(false, true, true, true, false)] //require valid server and client certificate - [TestCase(false, false, true, true, false)] //require valid server and client certificate - [TestCase(true, true, true, false, true)] //require valid server certificate only - [TestCase(true, false, true, false, true)] //require valid server certificate only - [TestCase(false, true, true, false, false)] //require valid server certificate only - [TestCase(false, false, true, false, false)] //require valid server certificate only - [TestCase(true, true, false, true, true)] //require valid client certificate only - [TestCase(true, false, false, true, false)] //require valid client certificate only - [TestCase(false, true, false, true, true)] //require valid client certificate only - [TestCase(false, false, false, true, false)] //require valid client certificate only - [TestCase(true, true, false, false, true)] //do not require valid client or server certificate - [TestCase(true, false, false, false, true)] //do not require valid client or server certificate - [TestCase(false, true, false, false, true)] //do not require valid client or server certificate - [TestCase(false, false, false, false, true)] //do not require valid client or server certificate - public void should_connect_to_each_other_and_send_data_depending_on_certificate_validity_and_settings( - bool useValidServerCertificate, - bool useValidClientCertificate, - bool validateServerCertificate, - bool validateClientCertificate, - bool shouldConnectSuccessfully - ) - { - var serverEndPoint = new IPEndPoint(_ip, _port); - var serverCertificate = useValidServerCertificate - ? ssl_connections.GetServerCertificate() - : ssl_connections.GetUntrustedCertificate(); - var clientCertificate = useValidClientCertificate - ? ssl_connections.GetOtherServerCertificate() - : ssl_connections.GetUntrustedCertificate(); - var rootCertificates = new X509Certificate2Collection(ssl_connections.GetRootCertificate()); - - - var sent = new byte[1000]; - new Random().NextBytes(sent); - - var received = new MemoryStream(); - - var done = new ManualResetEventSlim(); - - var listener = new TcpServerListener(serverEndPoint); - listener.StartListening((endPoint, socket) => - { - var ssl = TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), endPoint, socket, () => serverCertificate, - null, (cert, chain, err) => validateClientCertificate ? - ClusterVNode.ValidateClientCertificate(cert, chain, err, () => null, () => rootCertificates) : (true, null), - verbose: true); - ssl.ConnectionClosed += (x, y) => done.Set(); - if (ssl.IsClosed) - { - done.Set(); - } - - Action>> callback = null; - callback = (x, y) => - { - foreach (var arraySegment in y) - { - received.Write(arraySegment.Array, arraySegment.Offset, arraySegment.Count); - Log.Information("Received: {0} bytes, total: {1}.", arraySegment.Count, received.Length); - } - - if (received.Length >= sent.Length) - { - Log.Information("Done receiving..."); - done.Set(); - } - else - { - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - } - }; - Log.Information("Receiving..."); - ssl.ReceiveAsync(callback); - }, "Secure"); - - var clientSsl = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - serverEndPoint.GetHost(), - null, - serverEndPoint, - (cert, chain, err, _) => validateServerCertificate ? ClusterVNode.ValidateServerCertificate(cert, chain, err, () => null, () => rootCertificates, null) : (true, null), - () => new X509CertificateCollection { clientCertificate }, - new TcpClientConnector(), - TcpConnectionManager.ConnectionTimeout, - conn => - { - Log.Information("Sending bytes..."); - conn.EnqueueSend(new[] { new ArraySegment(sent) }); - }, - (conn, err) => - { - Log.Error("Connecting failed: {0}.", err); - done.Set(); - }, - verbose: true); - - Assert.IsTrue(done.Wait(20000), "Took too long to receive completion."); - - Log.Information("Stopping listener..."); - listener.Stop(); - Log.Information("Closing client TLS connection..."); - clientSsl.Close("Normal close."); - Log.Information("Checking received data..."); - - if (shouldConnectSuccessfully) - { - Assert.AreEqual(sent, received.ToArray()); - } - else - { - Assert.AreEqual(new byte[0], received.ToArray()); - } - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.crt b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.crt deleted file mode 100644 index 20a26b37ae..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.crt +++ /dev/null @@ -1,33 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFyTCCA7GgAwIBAgIUPqHLTqi5UFbchjkFXb/QyEJG05YwDQYJKoZIhvcNAQEL -BQAwczELMAkGA1UEBhMCTVUxGDAWBgNVBAgMD1BsYWluZXMgV2lsaGVtczEWMBQG -A1UEBwwNUXVhdHJlLUJvcm5lczETMBEGA1UECgwKRXZlbnRTdG9yZTEQMA4GA1UE -CwwHVGVzdGluZzELMAkGA1UEAwwCY2EwIBcNMjMwNDI1MDYwNzI1WhgPMjA1MDA5 -MTAwNjA3MjVaMHMxCzAJBgNVBAYTAk1VMRgwFgYDVQQIDA9QbGFpbmVzIFdpbGhl -bXMxFjAUBgNVBAcMDVF1YXRyZS1Cb3JuZXMxEzARBgNVBAoMCkV2ZW50U3RvcmUx -EDAOBgNVBAsMB1Rlc3RpbmcxCzAJBgNVBAMMAmNhMIICIjANBgkqhkiG9w0BAQEF -AAOCAg8AMIICCgKCAgEA40ed3tkTUXI6BVnA64jOYtDCohIvO0OSlXFCcJOt5zjz -zlluFW5kSW1JQpP1PmH/rY3d2w6jelGTSEHAarji7Hw3p/NW67M3sWmF8KCoHO1l -MAhMdrUVkNl9ptxWHZSxDSK/MRFFWxMskjhi67Z2i1tkQhRuc8zEavYI6DmnPnx5 -usmUJMqNzV4G4Cndjk0yu/e6S7mle5wKOou5nSiRjORDs0TJh7j0/GcgYeB+kxBa -eaKmxr1pM9VU2i0ezzR6Ym15yY/ofW+mzQw9DWAZ6JnEiFWHzHwzJdslyN3upmUO -e21k8ejELL2HeU0DPB9yRlL9vMPPYaJqxhdKS/O2K8VrfrtQsrV5V3Hp78YrwzRN -qya6BzVhtDbGmWsYHN3aOsaU+sX7d/5dts+/UUxTGC5RYAFz2KzMx+0ZSc+ZlH75 -fsBkKgshUsGeJN3Yr2SitPz1tbU9q+d+eYME0oGCjVO2yGSY4vgKUmNZ++y9YHkd -hPis9cAGsdLMo97WN3NoL9X8oFGJJjrFXCdfTUXNiHej7xK4gb/qbVYjeSk70jfk -R7lOITkWCCqquzUUyIGm74JKKJE4ztSO4a96bEJKfIphVITMxDcF1N/zpNg+9MJl -DvW1aIcaJZ9P5ovnPbUmPZ9XI9LHf/YqRlm+t/zKU4ehjMC8JMyXmgQTHlI9zJcC -AwEAAaNTMFEwHQYDVR0OBBYEFIpy/ykFnAwP3+yw+ezO1miKBnfsMB8GA1UdIwQY -MBaAFIpy/ykFnAwP3+yw+ezO1miKBnfsMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI -hvcNAQELBQADggIBADfdRv5zlfBx4Diti8HbjQ7kmB5oPPuWoSzG5uT1nQY/Q5/T -fRWvUlRXmfmrwJEiBIGeL/VNgeGKteErs5ikesVUdaSXYO+WePPDZPP/aNVllI98 -WAtShpDLjv+LV5P3xraxytKRpv/7CMTBIgfqhl5BaEzLKit7bXsffgWN1qvzBtNE -Jh3iqSArXVMDOkxB6OOr+f/MR6BRzLhg89lVg5sezh+4pSpQ1q4+RlGU2Lsl37yh -Zj690GI3GyzOF51q75hdfcq7x5GZt2Ot5hHuqkxG6fQRFdkFveq/fhPimtwVLsvv -ie6AqRnRwgt/m2qvULlF3H1kIGibwEyuBWYNvA1rgOpbFHHsqE+puXHf1bIK1p6l -d0AXrmhyGvoqadOdVpu5NJC/5SOCcVe/JOmp8EAJP4rmoMMHeKIsJvWsb+uykwIW -z5Q6WJW28Lo86L7Bd0Ak51SCtkg1ovfi/Gof5KkoakiwibxKT5wXWIEPcVu9y/yk -5sj32ZMxygIgIjFOLYvegHyLgNGNJRDrL6a/2xydQwurfgKbAgmMNi362QDztPS5 -806VS0u0SqDNX61WtLLnFv3tVDa0C0ED93uk+me90/3jWih6ibob7vjWURRMj8Oo -Uo+RGOH9Ktfh8AAY8MlaroknWjOPbDgKzNxRURtIG+EOCszmIMkIl8KtOpeq ------END CERTIFICATE----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.key b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.key deleted file mode 100644 index aabb8c635f..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca/ca.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDjR53e2RNRcjoF -WcDriM5i0MKiEi87Q5KVcUJwk63nOPPOWW4VbmRJbUlCk/U+Yf+tjd3bDqN6UZNI -QcBquOLsfDen81brszexaYXwoKgc7WUwCEx2tRWQ2X2m3FYdlLENIr8xEUVbEyyS -OGLrtnaLW2RCFG5zzMRq9gjoOac+fHm6yZQkyo3NXgbgKd2OTTK797pLuaV7nAo6 -i7mdKJGM5EOzRMmHuPT8ZyBh4H6TEFp5oqbGvWkz1VTaLR7PNHpibXnJj+h9b6bN -DD0NYBnomcSIVYfMfDMl2yXI3e6mZQ57bWTx6MQsvYd5TQM8H3JGUv28w89homrG -F0pL87YrxWt+u1CytXlXcenvxivDNE2rJroHNWG0NsaZaxgc3do6xpT6xft3/l22 -z79RTFMYLlFgAXPYrMzH7RlJz5mUfvl+wGQqCyFSwZ4k3divZKK0/PW1tT2r5355 -gwTSgYKNU7bIZJji+ApSY1n77L1geR2E+Kz1wAax0syj3tY3c2gv1fygUYkmOsVc -J19NRc2Id6PvEriBv+ptViN5KTvSN+RHuU4hORYIKqq7NRTIgabvgkookTjO1I7h -r3psQkp8imFUhMzENwXU3/Ok2D70wmUO9bVohxoln0/mi+c9tSY9n1cj0sd/9ipG -Wb63/MpTh6GMwLwkzJeaBBMeUj3MlwIDAQABAoICAE+b2MDjgUBAhfE02H3H3B7B -p8MUIoYcK5573v1GxR2eFcv8CDKqiacsiAIxiC/zLJbyf7jInltscm3XVhorq5SR -B/+knRgPHN1gLGG2LFyBjLVv2rSjxiuH+tq1B9bxq7iBqL17orL9etm2tXOB5HD4 -sJvywyN9oHkkt8b71shs2kiibekbJ+M84aSoFWpQQQqVAaSfoMa0x+Js6bWSLbVP -iSUkXYDUOL+mMbIG698AVeiQH+GyG8TjnwaS0l1asNmEJioDZbh2INNmy4XBWSHf -pnbompn2EQZcBhafIgYXlKSIE1kmonxXYLoMzyrCiwj17dmuLpgwxzyekNAcy/xM -0Higee2wVH07azPk/hjMJ4Jl5XpxFpooww+sLE61gcd/oFNcHV848rt6aOqVFMtZ -MLJbE0lbOmVZ6CHcGVJWIY0hbleo5Bj5DpYiu4IDsB80RP/FhlHHLV+hfvSmimX7 -gi/SLvu+E1+QUrL83X+xGsZrbxWoXnl98wCRiSoozMkSWg8iHb5FLkzwBmXxOq4Z -+XrXoHuDHRB+7XXyurTCvOkSiYu9aCrjhBEyiYDi5HkDFfQeq2m/atz0zjO4UkFm -nUzCfI/0XeY5yPmv0BEN18PpSv/ry1ymH+LGX1g4ixcsPSw3C/y3jh0QpgBYFiWe -jGVTs6gNaCOPZ3iFtYlZAoIBAQDmbMHW5kt+URSyLcWFOj+voR6vyCIQXSHl9mFp -N3YQiw4C9+zSdD45f3RmKzLYJJS4fGpIbSXnYkncLK8+Iq6yS0GXRo+F2bnKeByw -BebfVbfrTSMEYTOdqZ6e2dtqNgAsIXLf1xJQROJhFhzKrSAswjVyDHeotBRbP7iP -0W79d5dedHmlNR3UAVRc5ip8SEDrRtAxVr3J/EISfgr0jVkAZcThofy7aXE042mU -B0tclr2goPSKq8YCdTfivSc3VX1nnTf7AmHT5DK9eSfjBWEUu6eVHEziidIU6bZG -UPGiFOr9s1LDMEbFkpk5D52ip4XVVCKsGp4fGagt+DCNNMTvAoIBAQD8gX7wFeno -mE3m6arW+5BlYjVzvXXL7fVE3IxNgr3mXjO/D1LggBQ7vO1ILfMsQ4Tku4hiqdSp -de2OEoGpx4WXdufAzM5nGuQAAKo8iW8KVOE6rF6sCuMB8xFnXV12AFl0dqJsnuj7 -mYCsX/5XW+2WlbgS25u70akOBk2Ua640JMFFRhp7Z9+ejvfEdWDGM5fHm5/GLyDS -iHNF2tPkrkz+fR/yb5nLO1wqJLKmg9LdqgEkFpongqRGq8XUFjTqrvFadmlHEe+w -iJZyAFAEhcv5h+fqLnKgD49tVMDmPdxVI84JMCSWRuGhG2Wcy0MInWadxAuYFeIi -eAdl9dDSlwLZAoIBAQC/Tm5EyCDHd/Ox1vSVnv2/CArdMFCvu1LohPztCSHZrElu -x330YJRE0n/GT3lAIGblIE5xYUVF7MGbrfG0qcDWwEptmSSRoWlyIvTWY6ABrj4r -enhnK3haHrhV9FQJw1mzSfYAx6QpxncY2e3/i2saHgeXb6htBU2dULMfnGcEw6SF -xDg2pHnmSVBMbL+7PZXAgKNbQ0QBDZGhnsnjrfLFtLiYCBA3XNyjZ2eBgejcrz4y -vU8qTmDwK3fBpP+JHk4ml04h9SFZda12x78I6a9nMkF5N+zRbFFiEYPGr29d/PX4 -ukgtS8iGQic6S6+5mlIwAlFjCBpdgcSynctZgQU7AoIBAQDFA0zFinOM9Xii2GJh -hrptQCkwevyz9qBN8D7QI1iDq7B4PD6Px58B1TDWBnQbbzr7NI7/XD7/UXHeTfY5 -P+GuAipktee9bq/CiMJKpXir+Poay266kWv5k2ta2PAZJ8CkLAQi+AIUfPp2hBdw -ldtUiNCsdgiWjMl4ldZmHXY4XQ0nPcFf0/YYQrhy4pp1Sr/8ECKjgRX87lXyhQCb -BpwwW/RXNlgqx8/rWeLCJUybgw+M1s3EUhxWv6OFDSaQjZrm2EjgGNCJTqkDndbR -EIvL0AxVHZR16m4gAebYPh/xlhQl+xgw4HEfQL8RjU5qJFVydwoMydY8yqYLGbaq -+QRRAoIBABAAf+K0lkAqexS9t8cAJr4pXbfSskeO8D2yG1VuhUJKhfnEJbtYdGFd -ZzQrDSyBe1V8C3fXsCaWNNLYKBfmnzEZId9baisxKC8e0kfG2Qe5elvKYTCwJyyJ -ifbd+WO77liQ6fWz4g4IxcY2174e8lVLSAAkax8Wq+z1PEM7klchYgKAjKysvSqj -1Q88ae5wV9foFV9AHHmJ0tlx8ow2R2RyHYzpo6NMKPDsUdZJP2vWUQVb1Bc9sBss -HABiIRi36LNlswRdX0IaMMX5LDU5DRSdrd7sDtHg20XeOuhicgB0Ok7vdhYPnVLQ -ScM70S4i08cZPDlXr8Z+nF+ZKcKkUYo= ------END PRIVATE KEY----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.crt b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.crt deleted file mode 100644 index 76fe93fe37..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.crt +++ /dev/null @@ -1,34 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIF3TCCA8WgAwIBAgIUOKdLLfMki+A4K1wkFWfubBhsT9YwDQYJKoZIhvcNAQEL -BQAwfTELMAkGA1UEBhMCTVUxGDAWBgNVBAgMD1BsYWluZXMgV2lsaGVtczEWMBQG -A1UEBwwNUXVhdHJlLUJvcm5lczETMBEGA1UECgwKRXZlbnRTdG9yZTEQMA4GA1UE -CwwHVGVzdGluZzEVMBMGA1UEAwwMY2EtdW50cnVzdGVkMCAXDTIzMDQyNTA2MDgy -MFoYDzIwNTAwOTEwMDYwODIwWjB9MQswCQYDVQQGEwJNVTEYMBYGA1UECAwPUGxh -aW5lcyBXaWxoZW1zMRYwFAYDVQQHDA1RdWF0cmUtQm9ybmVzMRMwEQYDVQQKDApF -dmVudFN0b3JlMRAwDgYDVQQLDAdUZXN0aW5nMRUwEwYDVQQDDAxjYS11bnRydXN0 -ZWQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCffq88kHePXRL70jOc -sc+wLs4ya/vdVgcD3Q4/+AMX5LySWvt/ayaM6yfZzDjfzmPYQjVGuYTSr3pVN6oQ -Xyn7znAze8Lg9wHLGdabanqeDZWr/CIKfj6y8cb/uBp2m2BMVT27vQOLvIJSqMNJ -PJYVfyDIUA6a9h/fUt6D2n9rqHmK6VGbwzZt8A69IPX96UYKL+yUh9C1k2ZmYIIf -gjtsG6XtnkbkGEtbV25Jbte0Ic3H6J57grLBUPhXFYhWV1yqk8Vj2MbCARTrI7qn -QhXNvZy02/q6H92US+gBU5qnVXY1ZcrkpIVBEapRcyjCBmx8EcFiweemKymsCHJL -VQUtCq08W1jxlWopbLUynL6jGmU+3P/ojwamCqx4LjffaofrvQkVTkKfQs8c7zum -rGVNemPi0maSDJ51lzdxLN9pE+D8t1UUetZ/Itb96EQICk7tx3oPkCoMCiKralUG -NS1KUA4DKugKOjnlBRNsPdCnCELtwHXsiDFSDK5XiWH8Fd/qbFssafPSKYuJWibh -+HBC9HmxB9zzdw20SWtcojqd6P+xvZw5jzXyR6AKXWvmXQ6mOyS0OIq48zvhpKH+ -aFdESklD30YtXR7FPHPcQsguwKbb5sHp045cQBJvJytTIqHBbL4yZkG96FZuUsmu -4KxMbhl4tV1Nd4OtWwWBtYiflwIDAQABo1MwUTAdBgNVHQ4EFgQUe8AgLxr9tylK -dOW3SLG1BjnVYqEwHwYDVR0jBBgwFoAUe8AgLxr9tylKdOW3SLG1BjnVYqEwDwYD -VR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAjZ8oHcTBhsqljPA8tvj/ -GJVARvOhBi1TQYfbjq/gVqvS/hxABY/8n4tyuwcw/NvU56AMplj7lyZ7WFp7S/mu -UpIViuVfKo/+mRAEPR1chIbkc+34gg1EAWR15Zhgdm8dqleBdtqEJooEtEsPtnnn -6Pd5PdYuQ7/gMlVnrjMeHN+d6Ey3ZxB84mrbpk1jsFjbK/W4F4cRozeAiPdIAHuR -JAzBpzFrcL/NP6inZGe+gEVhBl/xci2gdpzPwJP/7gfaop82t5U61UYXuLXHX2p/ -zC9vU5kZ/8wsH+PkZa0MxCSeTYAecAWJNpXo/Y8fmcz8ty6pFujgiHSKTJNW6FwE -x773NfQwEEj/VwFKbiMTSvhBSQE4tZr+w+n6Xj3gjP8Nmg+E4BH3K1v9xHZjb2tb -w3e3ElBMpHq3w+Y5SdLAxrAowDXkf56IqVhg2W8Vochz9GVlq9bAUZ08kVj/KJul -XlcL36dB71vSXzWTHVraGR2w/iWr9ymJ2y7ziCvycpvLJx+bZQ7/64VrsunLDrYk -AnJC50aYD0RRK058BmnTvTHj3R9IlWGajzX7jx47cf4hz6yaqa3Ero8YwtVQUJ5r -b4pg/IWTgubQvYvf7SRw+89l/zncTiklsH0PNi+F+Gmxr4sGJXj1rBnlBSwRNWi3 -vuQ358hUhqfB76+BqCnvD7o= ------END CERTIFICATE----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.key b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.key deleted file mode 100644 index 9832ebac2d..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/ca_untrusted/ca.key +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIJRAIBADANBgkqhkiG9w0BAQEFAASCCS4wggkqAgEAAoICAQCffq88kHePXRL7 -0jOcsc+wLs4ya/vdVgcD3Q4/+AMX5LySWvt/ayaM6yfZzDjfzmPYQjVGuYTSr3pV -N6oQXyn7znAze8Lg9wHLGdabanqeDZWr/CIKfj6y8cb/uBp2m2BMVT27vQOLvIJS -qMNJPJYVfyDIUA6a9h/fUt6D2n9rqHmK6VGbwzZt8A69IPX96UYKL+yUh9C1k2Zm -YIIfgjtsG6XtnkbkGEtbV25Jbte0Ic3H6J57grLBUPhXFYhWV1yqk8Vj2MbCARTr -I7qnQhXNvZy02/q6H92US+gBU5qnVXY1ZcrkpIVBEapRcyjCBmx8EcFiweemKyms -CHJLVQUtCq08W1jxlWopbLUynL6jGmU+3P/ojwamCqx4LjffaofrvQkVTkKfQs8c -7zumrGVNemPi0maSDJ51lzdxLN9pE+D8t1UUetZ/Itb96EQICk7tx3oPkCoMCiKr -alUGNS1KUA4DKugKOjnlBRNsPdCnCELtwHXsiDFSDK5XiWH8Fd/qbFssafPSKYuJ -Wibh+HBC9HmxB9zzdw20SWtcojqd6P+xvZw5jzXyR6AKXWvmXQ6mOyS0OIq48zvh -pKH+aFdESklD30YtXR7FPHPcQsguwKbb5sHp045cQBJvJytTIqHBbL4yZkG96FZu -Usmu4KxMbhl4tV1Nd4OtWwWBtYiflwIDAQABAoICACp39pw3UYacf2ILQg/ursEY -bXIym6F/dyW5tkIlPVme3/FOhZoTg3PRZ86QGPLLB339fyciXfIlpHrNdByg4lJ4 -0Rp8UVNKojEc466FZznvnxNo8v19SR+nzleoA2/ihn0800/KkAekvcTZyAoXtlTq -AWYlHwZ3E9yIudQ9GWGmvhpVbxP9ImCao6oLfUTLWQl8Yk/YbCP1KDaJawyNxlnO -cNu5IeI5b9NWD/lAqPH/cEZTM+PqVMOx1jaF9sQaxgNrjos8HQxqclH8NQa1S435 -d6EW1Ae8VWlX3uJ/ivYUhQHJ5YEFJ7BATmwasFcdk8Rji+1KeW6fHpz+sMRLUrGU -fc+V/SYd1KShShEOFrY1CQYTx+ix3M1+v+3MwalKzH5PKgZ9hvNxav2+JpP6amq2 -wNhlLRekfQo8OAgo3r53JIepQsqXaso2qy8AZgXhkTS85Xx6wBe9UBWkrQwXYOvK -fz/t84LfBv2mhB+1Tl/bIyNXR9p81gQpjeySbxnOSPAV3KxjHdrGtH9v8zDLM8n1 -fSJ6uZMIc2bB4i5yfcuwSdYhnXwiqmaA9e+5K0hfF3GlaPNSm78ez3ssO7oputGg -wOjeIVlyQDDUmBh3IqdFz4t7Co+TIGQAG2tkKyHT/bgklaqcbNqZ8K9AS1tBvFCs -VGS4lo2PMT/3JMVVpe65AoIBAQDN4gooESixrLcL85UqULewUY3xECJKnwGQCfGS -BxJ2pfYqKSrAQf3HGbE5MnpRd7cixP4cPIqUja6yeMPcO0O96CloqpACOtJ+COlt -s+zDDhPypxz9mFJ4xfc4RHZSGuvkAY6D1sZl3B+zdqU2rwgNSFjGQsGnhgFI2Y21 -r1eysdeEfoCZmPWhku3CSUmOSvAgQnBDFx7UPBeM5pw/3jV6lwrhfdP7mS68Oeua -ibsxpxhBLjY3/SnNZFzbMoALxaXyr2vWQJGGBtvemIq7yInNCRM2jdJni2ltX58A -0Z+soHkHTMnyu+tjig40OBoUF0Qw0LjK/r4WdTdso7sRlJS/AoIBAQDGUeNfp2cV -pq/PUlwJyKvu8LktKPGvxm2J85eFoO43tn2C96hPrXOzK3nK0ZCCANrA7ctPXwcH -GJP/0ISBGuyo4ULGBb7VOrQNgbwGPlxgHEgCZo9Asc0WdXmVtzXI12wJfprDMrrg -Z7UHD/xLv9XWxA+bCiVjYwObUYxRQ4NLHhP6xJYp+IeRjdKBCX/SY9wT+lfl54Jh -bY1QYQoqL4tSo1Nuj5v+Nj1WY7YpggF4wzHshC37wT0Lw6ubqLc5MTV8oK2pAM2d -QTEkoT8CpL4cf0X0aU4ye71wTroBCWeKo0SImGQOZSi3OtaYXYYVbsJiwCQp5sMc -CWALhUNd1XMpAoIBAQCO9ykAW7H7dykA9pudb8GugWNkg0Sctr+6Cv2h3qFWnLZM -3pteODUGvfOP+VpxGgWk8lBWwT+mGqC2NCAl3U2UIRQoJ1CrC7fBZ2TZFfkSFcOK -W7tI6i5SyX17qCbVqvYlNWOGb20DrJCi6X+ecnrP5m4m9pJNR0O7F79M676lStOS -oNmiT1I6abIKmSOn2KfYbupP3E0/KZ9c64KS4zYVs68NmHfMnbmTYVEW1g+UUikI -khN6W6VciBXm7LF3+z7aQDBM7jdfb4Hcl+a2sXHnXCboWiFCX8/M8HqUcfxEPobs -p8kQcqfpTiPA5ThOvT1P/JBl/fF33Nuo6CVt9tQ3AoIBAQC3wu0ClyUnpLpHSrNJ -xY2664dKVyQLkbuyFeqSwZm6Ofh2wHBO1BPfMyu5SRGzxILuPekjdJQYeSe55EbD -MmWT3pZ20qAPCEQKN+o3htUloU8AGnQmqBm288djeM33edT7vzo8IOeUQ8Qz9RX8 -QpcsV6N2L6KRQw/2fUudiQdBNW+h70KNRWKo9lbVhi3QjwM/57pGp4viJxbIIgK/ -MCrSuc/3JE6Q8UVIpUAHmhUvBv9dfuT04AtmuTpsE5si/XvoLSJ89OeRDPUyUc8G -7gvScAQsAt+YW5wfUnN7vnEYv0wjncEorYdasMRjjlVNysGHqy5XPSIQ+obqZeMU -MVJxAoIBAQCSWSfxGEfmsp4qINA1lx7hCT8sBR1VSMQw9D5lGXiTs7GX/+yPsf15 -LDIpKpFxGOaUIC2MnDEkG9/zWKWavd/JRj/jCVnl3e844BZw9k2gulEfv5hdu0ct -bY8UKAC5Wv6iyHmq2YcrnvN8sMfnPlqK4OY6PVjPFAUCSTS5XPjGoyiNd2CFhbKD -4B14sqaTviyFeJKtxNnu6BNoQhjj4nvZvzktFlRJLvRwTVOVvELhjkYcoue9G5i8 -XAaSbAnUwCBc7cblSrk6djB0LQhZ1ruldsVNAUFkQ1o+9ERgFqPnUc6Rb5kWB8Fh -tKSXzb/URtW5aTqTJsCXd2hi9odLLkAU ------END PRIVATE KEY----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_ca.sh b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_ca.sh deleted file mode 100755 index 9086f7ddde..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_ca.sh +++ /dev/null @@ -1,2 +0,0 @@ -openssl genrsa -out ca.key 4096 -openssl req -x509 -sha256 -new -nodes -key ca.key -days 10000 -out ca.crt diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_cert.sh b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_cert.sh deleted file mode 100755 index bc34b6f169..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/generate_cert.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -if [ "$#" -ne 1 ]; then - echo "generate_cert.sh " - echo "generate_cert.sh node1" - exit -fi - -certificate_name=$1 - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -pushd DIR &>/dev/null - -echo "Creating certificate directory: $certificate_name" -mkdir "$certificate_name" -pushd "$certificate_name" &>/dev/null - -echo "Generating key: $certificate_name.key" -openssl genrsa -out "$certificate_name".key 2048 &>/dev/null - -echo "Generating CSR: $certificate_name.csr" -openssl req -new -sha256 -key "$certificate_name".key -subj "/CN=eventstoredb-node" -out "$certificate_name".csr &>/dev/null - -echo "Generating Certificate: $certificate_name.crt" -openssl x509 \ - -req \ - -sha256 \ - -days 10000 \ - -in "$certificate_name".csr \ - -CA ../ca/ca.crt -CAkey ../ca/ca.key \ - -passin pass:password \ - -CAcreateserial \ - -extfile <(\ - printf " - authorityKeyIdentifier=keyid,issuer - basicConstraints=CA:FALSE - keyUsage = digitalSignature, nonRepudiation, keyEncipherment - extendedKeyUsage = serverAuth, clientAuth - subjectAltName = IP:127.0.0.1,DNS:localhost" - ) \ - -out "$certificate_name".crt &>/dev/null - -echo "Deleting CSR file" -rm "$certificate_name".csr &>/dev/null -rm ../ca/ca.srl &>/dev/null - -popd &>/dev/null - -popd &>/dev/null - -echo "Done!" diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.crt b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.crt deleted file mode 100644 index 4c3aa61525..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.crt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIElTCCAn2gAwIBAgIUGmJbCD3Le5HWkxqi4LbaiYjE6XgwDQYJKoZIhvcNAQEL -BQAwczELMAkGA1UEBhMCTVUxGDAWBgNVBAgMD1BsYWluZXMgV2lsaGVtczEWMBQG -A1UEBwwNUXVhdHJlLUJvcm5lczETMBEGA1UECgwKRXZlbnRTdG9yZTEQMA4GA1UE -CwwHVGVzdGluZzELMAkGA1UEAwwCY2EwIBcNMjQwMjA5MTYxMjI2WhgPMjA1MTA2 -MjcxNjEyMjZaMBwxGjAYBgNVBAMMEWV2ZW50c3RvcmVkYi1ub2RlMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArqXOfwJmN61y8kkBb373DJ+JJ4Vt5WLa -mkatFPTsEzXnGN+UWNxzQfH3cqgUMQVj0JBS0BT5hQfHU3zXt6A3jyRplqzG5ONr -TgZBFgadTWtvOjH3yCOeAGwJ1gLT8bg+3BNWS2zq4J6PjMFFsLdyABPb7cVS2DhJ -SHFVNqJGMpSsMmx0PUaDLAoIvKmyMIKaCQluQ9mKY2qnxA7hJw7PGPT3zsWGXtxj -YZVOiclWtrc3b02uaUMdxu17hC6utULtMzIpI7XJsmarS3/KTff3YbOb/IJF4/M0 -XH7kABCTHirdPGGzMT7fHi/2mEN232qkAN8+rQRxpXNxXT8mba5IvwIDAQABo3Yw -dDAfBgNVHSMEGDAWgBSKcv8pBZwMD9/ssPnsztZoigZ37DAJBgNVHRMEAjAAMAsG -A1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGgYDVR0R -BBMwEYcEfwAAAYIJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQCMGdOPgBiK -4t0T+GRlP+XIh8A1ecc182IO6lPPi9Hr4B7leCiZ8tlDa68Ykt2U6rZ/f89h33E4 -THQUeHxMPDtO0FI+VPF/gvpzuxJIAPNgHFbgVddSgzhPGY33SMEjDcMv2cpnzUPr -317UnvJoZ0bez2ogCVHWqg73JH2AVcmVHSPdED1sNUNqXGahvJ+yKx7iM1m5wdV8 -OPVjMWm9bK7ysGRRBNkkuhDAb3qSy+8MTz+puvDJ08yISvh+au4so4Yl3jh3Wu2C -xUSd4RAJdneK+3DrPU2YU3eNE+n8eX2ijxtAoDOSM6JyjWf2fkTOfH3rDhq3bSqG -6poSaebEPTRuq3bl0D+pz6uCyjtBfTfmKlhG1BH1kxNb8vFt3AO702Fa+uORf1uf -SP6srKYsNRriwl3yI6+NEoYaAcOEjuJ2itlP5vUneltm8Ndpk//5PSf7DK/kE5zR -12nvMU5wez7P1LjrnbEX1841QhsnZHX7HNZ6wxAwnDXAoaXm+Z20RJTD+7HKRhls -jk83BiAWMRsQDR0RmauyJw6U5BNLeR7s1jUMx/viZxwDXKz5VAGHn+1lqdl5aQP3 -Rw041Zu75ZuiIO7LOWCrgdMp3W8pCJ56CmpzazupisNLuZvIro4BSferwJ9Xmk9v -vOb+MTeB+xEmQY85QF/TXIrdoYMtWsMzlA== ------END CERTIFICATE----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.key b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.key deleted file mode 100644 index 7d7c8f6142..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node1/node1.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEArqXOfwJmN61y8kkBb373DJ+JJ4Vt5WLamkatFPTsEzXnGN+U -WNxzQfH3cqgUMQVj0JBS0BT5hQfHU3zXt6A3jyRplqzG5ONrTgZBFgadTWtvOjH3 -yCOeAGwJ1gLT8bg+3BNWS2zq4J6PjMFFsLdyABPb7cVS2DhJSHFVNqJGMpSsMmx0 -PUaDLAoIvKmyMIKaCQluQ9mKY2qnxA7hJw7PGPT3zsWGXtxjYZVOiclWtrc3b02u -aUMdxu17hC6utULtMzIpI7XJsmarS3/KTff3YbOb/IJF4/M0XH7kABCTHirdPGGz -MT7fHi/2mEN232qkAN8+rQRxpXNxXT8mba5IvwIDAQABAoIBADScIe5ErMIM79lo -gJQzrPwSeuB64zX8Z1md/LmZZhwLWsg1YZy7G8U7vIy/cQWrV/wV3bxs9RVL2JGf -w+O+0b2+/Ee1WFzO3R4t6CFYcL5HpMPlFX0mrYv2/6eGRo5EMDtfX9lsv/xWB2wh -NHsdZ6v6T3/6txAHNJwr971zeK33d8y3Fvb6lnMKEW9eIKP+/hzqMkOIpzTmz2Q3 -M7uTcR5ItN+Ik7X1W4lbAJUAUvflCAkJy6fwMDcIB9c0t2tEK6X96iyng8H9A7l2 -8adNatiosNlLRI1hEMBat0/04m6RC6s01fQaPqgIn2IQRYrl4UX+gS0v/Ki0Pol1 -zOgVALECgYEA2eifns3XSu5BFt+D5gJ/kujrkb4YU9h4hNoHbCZlucf2viFtD7dy -qFNPxipO5B8Szi1M5seBl/HtJ2uyQ50UvH+Yn+1Iy1QOdk+uaoJto7GPPhfIfhRb -2lIZGtQWnCXuGWj3ClNZ8GH7P/b4u+yqlbHADbWKITbHlj9mBVOlbIMCgYEAzS1C -td8+fv7jDgKaUkkk5Z6EhWLIdxLeXtXsEiFFWxwdYjWwzm97+B9UXjqw/Qc2cxYL -0aVjiLHonO4GH2rqv5/yb/8wblxCgONNEUm5WCXWzYq7NXrQ7LvmjiLRqEqWGz96 -uVlXFi2TQJ5xjzUZKjC6u0eZuRG8cB1Mlc8IdhUCgYAYnWFig+f869roZYk1szSL -+MmbZfjDh+FdSG+sNM4q5T6nDYmPS+6kuvx46xCMTkJGjfFw5DpsWoSqfkzdvGG6 -RUONVTVy2mTj0m5mU6Nk6Uq//dnE/DHAAKQbVrfTHam+rw+w1+eB8t95w0EnoT1H -uZNzOP2IcPKssy6oDWHTVwKBgEXjyPmS1gMEZ+t5z3+AEuV0nceh4o9NChpb3FJA -qxNh/FBb4aIwL5ZRdbezXl6SemWxeM0s/T6u+4GZQtSo4tXrcfXcg27UVoL46mOd -LVltakIRo7uP3mMFjn+UNL6SyZb2hKNmX1fapnI4ho41lCDfAHyS4zY7aGj4UgIl -x8gRAoGAD2T5Se0Q8zAUK8dYoa4dSS7wRn33alCR4FvJHm8KiL95Cn/nhic3u3St -6poLD11p1Twlk/aXKQd2LVVXw7nQt6izywJPrbv6gxdx7tmKIgYLyc94bqcVjl/i -ufe49i09WJ4qNxIuKZDc7JidAZKsJiQMArJANXFvVwj1CLN/A5A= ------END RSA PRIVATE KEY----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.crt b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.crt deleted file mode 100644 index d6c203789b..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.crt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIElTCCAn2gAwIBAgIUW+7IruzFkAcTBpY03iGey70VKzAwDQYJKoZIhvcNAQEL -BQAwczELMAkGA1UEBhMCTVUxGDAWBgNVBAgMD1BsYWluZXMgV2lsaGVtczEWMBQG -A1UEBwwNUXVhdHJlLUJvcm5lczETMBEGA1UECgwKRXZlbnRTdG9yZTEQMA4GA1UE -CwwHVGVzdGluZzELMAkGA1UEAwwCY2EwIBcNMjQwMjA5MTYxMjM0WhgPMjA1MTA2 -MjcxNjEyMzRaMBwxGjAYBgNVBAMMEWV2ZW50c3RvcmVkYi1ub2RlMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4N01FVHAofXzLnidQk3CsPthhE7JnuwH -wqpvZIkq7FUem6zFzrsUarfrG052tThCTgYlqUwTHJqYeBPiRSU8IlgmGcbU0YGf -ztYOEboEK90JCFb21weFkxMAcmf5iwbZryNbzXBB6izeWZ3US1ApFfstxr8dlPzy -BvyEw3MNMrDAnha64L+4JLiZbIao948wDNfTT6kopftAJoNHy+EauK0tJz5Ns8qN -geW8lIzsOvR/hmzdcEmtEPwYYmeVuVp6/YZwfjfRZET0CqIzMPx62myJuh4otXxV -3P3shh2+xR/oxzF/wuGrSLovDFneFrxjhd+Tv6VuTGgkJLBuzwGQsQIDAQABo3Yw -dDAfBgNVHSMEGDAWgBSKcv8pBZwMD9/ssPnsztZoigZ37DAJBgNVHRMEAjAAMAsG -A1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGgYDVR0R -BBMwEYcEfwAAAYIJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQCITjv8cPa/ -p/EyJXr1o87CmnCj1bKOVvdZGv8HnJWJrtWK3oL24I8lyPtlSTMkpv0f1GWNBZ6b -MiOrutSYhe5leC5H6hvJM2WslsMdNeYbOhFYXblD6/1A/4l7FyAlAgFgO7r1QoEK -+uVF1DdeSnfMhBreB00mnKHAIl35cTqYq05BU4/QEIdKhZpRYmbIdmGaV9iGTKpP -FQVuq0TenVWuEk0UXUo1+TyTdEa7BxHkBbOxkMxo2kLmoz1y9ixb3xZ8nFvpuOah -1/KSM0sHc3opiE/egPJOOelkjnp3LryFq0H1GXpfiSx9bqieyub4DmH6+fwd0Q1F -i9htDJE2XvR4ZOxc1zZUd3/95d6VWCD+Q3drqqLuDXVHYLJ08atAHFU4QYs28rh9 -ngjI4dFhnU8SUQN7TEqZYi+DWY9VgtFBTB108WH/d9ccqSes2zbF9L/JdyRhu2Fn -GINDa9XZgdLASp1cbxFPTKzSG3zPdSqMHm87fvranjc/6CMD0wCZ2zeep58GzVnx -Y6/XNN2qgcPND5ouQhgB39Q+cXS+RI9uRuAVnfJBlSz8kl39B/SfBB21Koxycjmg -xDtd6RV0Ha9sInSClzK4U0pRTfjbidomLUemjwgC3lneq4GF6CaWRohmqXFeHzAu -dM3Q0YkzSmYanm59A5soH79UBlB1WLk1xw== ------END CERTIFICATE----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.key b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.key deleted file mode 100644 index 06ab14d413..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node2/node2.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEA4N01FVHAofXzLnidQk3CsPthhE7JnuwHwqpvZIkq7FUem6zF -zrsUarfrG052tThCTgYlqUwTHJqYeBPiRSU8IlgmGcbU0YGfztYOEboEK90JCFb2 -1weFkxMAcmf5iwbZryNbzXBB6izeWZ3US1ApFfstxr8dlPzyBvyEw3MNMrDAnha6 -4L+4JLiZbIao948wDNfTT6kopftAJoNHy+EauK0tJz5Ns8qNgeW8lIzsOvR/hmzd -cEmtEPwYYmeVuVp6/YZwfjfRZET0CqIzMPx62myJuh4otXxV3P3shh2+xR/oxzF/ -wuGrSLovDFneFrxjhd+Tv6VuTGgkJLBuzwGQsQIDAQABAoIBAQCzMjWyZrMPntQM -DZytERD9P2doK+1rE4ffrzkUMmedbPv2bYo5hEbyAN2AplfddRcJ3MY4k2KLofut -fZMNLW4tpHfAhP5mc1lqWJVftRDL79rZ5YvSsrDVEwiBI7DvBSeG9bOcFUDCIL2V -Rzy+RARp+wBw6S7m42jw/l3C8DXtVQV/lzk/QtIHCqnZdZDPqjin7aW6/MTjqk+1 -TRRzCAWWWvY8mBw0wZyEgebcT1ROwajUVLC+7z0RA6DiUUClA81fBJaetvzpwmG/ -9QZS2/EbNNvmV5UmOM62bWZdW5D9jAmM9QOzVjdWdkGFhznGtJuAxjzOwNe+Z4/4 -1NYzFDIBAoGBAPoMcbABIlwQneDMP8xOyrwzaIZkJPSaPZm/Rog0mZU2kABx28yM -DWRNYAUQ8AFOhNb58gs+uGu2AN9n1XghPsj7ZH+jiibWD8ox2awXDBkKy2obc/tI -KtEfvWhvEiWPmAv9W4N7stVK3N5/SwbivBkwJrrbjBq/QieaFCoezAbRAoGBAOY3 -UCGotezsgBVjxzsPPyPWqK+kP5rbQJ79Kn7q6hesqusi0+kmYIwbE15hhVZqv2km -hbUTF/9fZjUF/bKl5PiaawvXZCrZL+0Ms/f+EldQ2j9GGo+Mv4q7nUL8svlyt954 -P+NOo4ljyYCMn3pXVeCoXZrB4w1zhBjNdyIO8SPhAoGBAL5ZbspPcrqnI6iXlBGS -AZ+XewCjh8Z1DGPYSncxo6EFckYEZxClpSQwCuKQbERyY4nLqEiLLJVCbhOj1cun -c0ZIqc7IzkiWOFrx7ASnXtxJbWSPFZG69G1SPjwcIbJaR6MI8ft71T2tycnPGWxa -elqqXzFsJJHP+ypujt8ppqOhAoGAXl3uCt4NNMD8/8eWsy7cwuQK4XSCXgfhxUoh -UUUaD/pHVXtBQiDpEoVGuzleXr4DGCciR8c73wJL8xAxnjlCUmV527C+7pF4g8Lh -QpZzDjemPqmN2anoGgYtwHe1JWtQ03CXP6XMg1TUFE2QUaXbHevl8rnnSGxBiakv -XFajogECgYB2LxjHqrM3PBxlne+RHerhTBPfkUm4uLwllby6kSPCXXy9NQm/NVP4 -tKmh7NdnaBr1w2/qf9qHEAFOD8TjRALpj/ykQy3ep5sQ/fgJZBHs4nhxs37yyzIo -7/DrKczuJnRAYcsOtTrASjsleZ1f2DHHJIkOS0F71WGnwR8a/UfdFw== ------END RSA PRIVATE KEY----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.crt b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.crt deleted file mode 100644 index 4cbfe52423..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.crt +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIElTCCAn2gAwIBAgIUObXdVNzmq5Hx995l4Rq+4GvF5E0wDQYJKoZIhvcNAQEL -BQAwczELMAkGA1UEBhMCTVUxGDAWBgNVBAgMD1BsYWluZXMgV2lsaGVtczEWMBQG -A1UEBwwNUXVhdHJlLUJvcm5lczETMBEGA1UECgwKRXZlbnRTdG9yZTEQMA4GA1UE -CwwHVGVzdGluZzELMAkGA1UEAwwCY2EwIBcNMjQwMjA5MTYxMjM2WhgPMjA1MTA2 -MjcxNjEyMzZaMBwxGjAYBgNVBAMMEWV2ZW50c3RvcmVkYi1ub2RlMIIBIjANBgkq -hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtRnWJxkP1ftS1qTiuDkXGOddi+2J72Zd -8GFv1t7a/8dgqq/gjBykhEm1KRlg0rbBaea04TXla2OcJ5KLBGgQfHYN99lyizTx -e1zD9XWahsO71uBoMwfmZSomoiEgGN08FMSrdoaD82m7Il8bE7osw6GFM1PiExei -0buoLUZ4aNAc4C1nMHrIIMUTa66LUVBXg35VAwNPHOyPH35uccI5B9/NXyBTHkhq -/WC+FLcRDC3lo1Ez10rVlrsRs6LFFL9JtYrpAu0MnhubsRlvVvehzWeNK736g5bp -CrFlVLhx2ErL7GGgYviu34CT+o9f7TCUSYjzk+ArOwXvJdQ37OCE/wIDAQABo3Yw -dDAfBgNVHSMEGDAWgBSKcv8pBZwMD9/ssPnsztZoigZ37DAJBgNVHRMEAjAAMAsG -A1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwGgYDVR0R -BBMwEYcEfwAAAYIJbG9jYWxob3N0MA0GCSqGSIb3DQEBCwUAA4ICAQB4I0f8/AbA -V6Ml9Tai4V/ArLwAtcHoEJnwEs8YMf27iCQwYvDF/OQZkMk75bHbNycKdluJbmHJ -bHZPGuKQHKvOfNTdr3ciz5RqsccgOQ+3rJ/q40z83TsnWtAxyiZEzkbn1X8JqBmX -AFcl28jV1EwaQtqJ0XRsf0+JTzhj5WhMXh3viPJ1kpbQE/HzKzZ/sghNxVwqxUq9 -EASrxZiJYYjz8hCppGVo8P9t+2tsrdlvgK/+ljWNxcDlTeQrZZO/ds0AuVpWbtFP -2TRJapwPTOBkl17bvobs9hzWxjDNli4hnFgzeRMGJ/gDd4Cit48UgCHoWxWonqo3 -FAv/Y6cgdAzyQZI0/siAIc13MQl+xiYT+WurcS5NVRpYZYAY+m473HJ/rzhy8dJk -Z/ofLrtAlQ9k6DZU8XSzHIrgs3AtiJEaTlmNTCR5CFWuUM5VcIvOljAqoMTe1dIo -/HhqZpZ9cmklhNd4F9BZKa8QaJ789mhrOIvihx/vBMYXIbwiNrILZWZ3E/MDg0ho -rvjnKxxHWcAH+bLnvEk1WV7ItfuDQIJr3MqunChSA05mltjo/r7g6N6MG3s67OeH -2qvCv78vs29dPpfah7rC+dXM6yH5AtsP26nindJ7LVu3eduWmAHeqcJd21NEnGUA -ar/DNwXKDcozLEXD4PCdSskhzrrXO6e7Nw== ------END CERTIFICATE----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.key b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.key deleted file mode 100644 index eac058ebe4..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/node3/node3.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAtRnWJxkP1ftS1qTiuDkXGOddi+2J72Zd8GFv1t7a/8dgqq/g -jBykhEm1KRlg0rbBaea04TXla2OcJ5KLBGgQfHYN99lyizTxe1zD9XWahsO71uBo -MwfmZSomoiEgGN08FMSrdoaD82m7Il8bE7osw6GFM1PiExei0buoLUZ4aNAc4C1n -MHrIIMUTa66LUVBXg35VAwNPHOyPH35uccI5B9/NXyBTHkhq/WC+FLcRDC3lo1Ez -10rVlrsRs6LFFL9JtYrpAu0MnhubsRlvVvehzWeNK736g5bpCrFlVLhx2ErL7GGg -Yviu34CT+o9f7TCUSYjzk+ArOwXvJdQ37OCE/wIDAQABAoIBAQCpRs8fmJNuxg42 -R8ULwYFPa+PKyWum9B5WdB0f0QsPDgPTR7YyAQveAy9sjSYtj1nWbSkhYUliSadK -bfW3VrYiQA7qXrxsVj5putoH87dOxGIthXCgUoAOi4m0/hWMaC6ikvpaPX3U5j1e -8VrT46+35KAmy/+DLRKpzCh0uWwPOvTSDYnDO7S5TeAprdlLOxY0wrvAiYr2R9/f -bVpydCj7morGJaPP7OKBnRb8bj+XH3Gf3DL9I09XhakrlJhXD0ynjLGK7wKwcVu/ -sMO1aGqxjaQerp9qojcp1vkxaYedfQNdjY/0472LiQ7B3UzmKvJ/KGm8SGAuw0ia -R6kNBFlBAoGBAOLCgn9b8SSFSGgr4ScXDQxW2Ievh9vDrqkjg5YRGsZQ4tY8XObF -1i2la0oaXAiWU0dPrBGQK2QHWt93LrB9vfgH4XDl+GWzljZ6xTvOZJnV84uqqau5 -fLDbbNhalZzY5b0E4xik5ZuLUnLD5pCbkyrenC6joh3XuvzYfiJP5gxxAoGBAMx0 -GN2L6pn7pPLlixDklR0Fi2nVnMBV8fkLLZiZBuIWXUQHASGVjKH27nAm5ilpHKcS -fHFyzQwqIUgwCutT0OKWYQqwxkqPvDtkbhSZdyxVMjQ4QrHcbMXwCcn7L6hLV5TX -IA+PQPWpHveJRUMDhHw0y0h1Xp8wpRueMyAVQyBvAoGAMniuySAyxTkxS5Zwr92u -ls+BtHm6WIzuhFEWScGW3wtD4gFfgDIKoPBPuBez4mlrMdUm7GXVcpc8f76sUrY6 -IUnHwPQ+3IwjZA7hdZVTyPXZFn4u87z+ZvMAAAvxfw67292JmoOhY4SYHllCKW+n -SwRvaOYdAF1zDjI1qPd1sdECgYEAofxJxx4X3bA23AtpxxST386Iym1oKCrv7/Uf -RwvXHEHD2yYBi25FYf2WpFztdlnFTcmjtu7R7pVikSMfQ5vAoAWtFR9Bu8ltxVCo -tsFwXjuR7zW0Ad82fwzTdKgC9j/bQB3BVRKW0QwRcOXdbl7XFOFvPhVfxphv+BW7 -Q+wUpxkCgYAMSQRtXVOAelCej2e7I3Ufd4wWg+4wkP7sPblHccNnSZ+ZtxFac/6L -enSNP5NwgMq45KCjHFYIeMfxokVWYjTxrtbm2NJvem2nXPSLhmyBbdjfj6rLfA9K -xGiGW7bU3qRQUpiDJ7HZOUGUjUe9crirIuZN8Hv0AAazQDrpYP+tYA== ------END RSA PRIVATE KEY----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.crt b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.crt deleted file mode 100644 index d1107be6e5..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.crt +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEwDCCAqigAwIBAgIUcY3ZNOQsWA95vhCh9IlrHdRS3/UwDQYJKoZIhvcNAQEL -BQAwfTELMAkGA1UEBhMCTVUxGDAWBgNVBAgMD1BsYWluZXMgV2lsaGVtczEWMBQG -A1UEBwwNUXVhdHJlLUJvcm5lczETMBEGA1UECgwKRXZlbnRTdG9yZTEQMA4GA1UE -CwwHVGVzdGluZzEVMBMGA1UEAwwMY2EtdW50cnVzdGVkMCAXDTI0MDIxMjEyMjM0 -OFoYDzIwNTEwNjMwMTIyMzQ4WjAcMRowGAYDVQQDDBFldmVudHN0b3JlZGItbm9k -ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALPgRI7yp/BiAtn+ClWK -E9R7s/RE+q8cc5vxXTNM8/SVZnyjFVkyrRNnS9wUpruf9n68/hqD+1a8mNA6dYDW -tjVnq7LAFRnHV377yN3cLj3G0l7SPpVZbchvCyf9kaC863+J7QpkWI1jel+vdsDa -wh1XW9ld1WeEXVN1O/JCrLHlLBv1Th436LvG0oxh7LPZ+XGUXux/mgs06ng3lO9N -snoowyHlVKuAxokq+akgtmEfxyf+SuhqIs3FhmW6g5adk3Fy0EBAXJUGbeFrDO8r -E6oiQShOPNj4M4qn1bEGpQ/yvZ8Gjf7tW9s7jFPBYbFlnv/td/7YGKlnyQIGWnq0 -G98CAwEAAaOBljCBkzAfBgNVHSMEGDAWgBR7wCAvGv23KUp05bdIsbUGOdVioTAJ -BgNVHRMEAjAAMAsGA1UdDwQEAwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYB -BQUHAwIwGgYDVR0RBBMwEYcEfwAAAYIJbG9jYWxob3N0MB0GA1UdDgQWBBSSgCxV -o7LRR14LUWHwUszgNlO1YzANBgkqhkiG9w0BAQsFAAOCAgEAhN5vF0a/GTM5w/39 -fXD7/G7Ty/6VDDgwiZgfPiTuUfPrNjV79PpxAtXF9k8lfN093I0AwEAmAx5YqkUN -iFkHyT1XTpRX3gOALJonHbWOfXqq7wZLuc+KUj+g4soK+fQ1/pBqXG76YdFETTWV -sF0MJLqzjq+CqgXZcyhtoDkSlaT90HaXwvb6Hqt1+Hz+8zlFO5aoPKHYFDT4/mXY -cQzdMynkjjZJQLM4ZllpAqXnUl0qc5FZ98JQXtYhOvJlRfr1fudB1F743lfAVAn/ -wp1ih6TC/L8Nfb3qm6ljY/8ZxgQiS0CCQB70urp1f1PBE75WQ7iyZMPC1B9xZ2gy -ASuTM/1ySm/Iw91R/tPhaSXQAkt2jMoUbaQzydk+Jb2e9IMnuJc2wU6G47WOHZHp -r+uXjnB02p7tmW+AyOpoXEnFvLjOSUfcGXsYQ8+bjVShp5uStZjuuNj5UxCwQ8Sr -ojC1apGCNyixfWBV1g0DGoHrpP4wYFn4gEVn4s0sHkeHBz82JEhDRKtUquZGBgyw -9+7GgMNzN99e9ahBUV0igt5OuvQ91DVDOZf/WzwzmUu2CfK40HQxWpjxCIgNzstO -ynIfM2X/4CrtK8h5qeGEjY8a0V0vcwHe3e/xVOmuJWCPnEjyh6HUyjcQo8OTL/WB -NmHGXqYUx6c89kiFmBDR1iy/NZA= ------END CERTIFICATE----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.key b/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.key deleted file mode 100644 index 19eaebcaca..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/test_certificates/untrusted/untrusted.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCz4ESO8qfwYgLZ -/gpVihPUe7P0RPqvHHOb8V0zTPP0lWZ8oxVZMq0TZ0vcFKa7n/Z+vP4ag/tWvJjQ -OnWA1rY1Z6uywBUZx1d++8jd3C49xtJe0j6VWW3Ibwsn/ZGgvOt/ie0KZFiNY3pf -r3bA2sIdV1vZXdVnhF1TdTvyQqyx5Swb9U4eN+i7xtKMYeyz2flxlF7sf5oLNOp4 -N5TvTbJ6KMMh5VSrgMaJKvmpILZhH8cn/kroaiLNxYZluoOWnZNxctBAQFyVBm3h -awzvKxOqIkEoTjzY+DOKp9WxBqUP8r2fBo3+7VvbO4xTwWGxZZ7/7Xf+2BipZ8kC -Blp6tBvfAgMBAAECggEAAQuMJ94l53K05VW180zsOA2IgW8/AbTv/iRhSb+Ou7UB -eWpahLNWLRmgAU9ym1rAHibV4UijyfJx+eSMGbWtVlgO9UTwZfdyCesE87GwBwNC -svgPZKrzxUgE2OpBY4jgWVQsziTztlzVBe3mwm9WJqJAfE5zPmRjyi9yiASh1cxH -w6DefFV+7eOPPdH9wYDMzfVHxKqfKTWiMaEKSybYWoxJ7boA5YrkCNpUjhUSObmB -KeLtCuK91ltwInKJ6ejYmZ58sqqNmbmhS0cvwrvSbyoY+nJkley8Ju+uZOExDNLd -p5Xlsp4fhjMFw3sApgmcGSbUzBhgLUXaDXkdJDoEpQKBgQDUbUJCIElUCyYsiEJU -4Lv6MP5/YQSuMCkC3aBSd2JAtAh+8p/BIhs1gUG+j47x707bFT3x/P2Po+kEaQTs -Areld6tsr8c0fJp0ApzdfSzkyigufh2pexIbzCektq4/T4//AnY0kSdNqSZCXLFo -MoaE49TA9LCRgiRC3WiGZBdpOwKBgQDYxbyxEUQ0DL2hmx1IbelIhXCHd+jPptNZ -JA3dpEQJei5Oa5UwO7ooZBuKc0AoZ9UVvDuaARiyNh0mDXe51TFGZMcpj/tWTB1p -QJJMHLdNsGIaeViVMnDN/46xVWltVpg79jbgfBRnjoHMAApOxUa72gsB5zMHmRsg -1kMH2v4NrQKBgQC7fO91E7AEyI5Z0a3R4kSnDuUgog2SfBlGWTvubUY/22aAPT55 -JNQlBj9zpJtCt7e+r+HrmiJlzAKXDOX2NHPITHX1yLMmhS+BYuj2qEueZzrNxiwB -8QjLlgYLX90tLdjm91JDFTY+WiPVgKBoWefWlIur/ot08gt1JJvjuaXcDwKBgQCj -RXx0Gb83PQlvuXd64F4/Y1J4lpZiIRM7Ju7SrDEauk46fsmlxLbYSV59D4WZaWuT -rW1u97h0MlIOIUK2U5+Dl9Byu6HhQ/87EYmfjll6SIn7yr4v0ggpAH2nj9+SnRCY -yUBYINz1it60mqso3WoDf52e11cWN/m45+kg9483GQKBgQCUZtAQ9Ozi4b2lxj/q -WZTLmeLlG9Q8RnUSfj05RY7nMdZ8OKaT4spWtjTUvv3g7uWb/77EnGSbpBaBOFS9 -hAYSavGBYTSCkCTxMF6g5XSKBcpq57uKuAwURap0bNndsXmSAf2zZcLlms9XzFcC -2NXP7cyUm13HL2gT5RKvxsuWnw== ------END PRIVATE KEY----- diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/when_invalid_data_is_sent_over_tcp.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/when_invalid_data_is_sent_over_tcp.cs deleted file mode 100644 index 66ca036d1f..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/when_invalid_data_is_sent_over_tcp.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Common.Utils; -using EventStore.Core.Tests.Integration; -using EventStore.Transport.Tcp; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_invalid_data_is_sent_over_tcp : specification_with_cluster -{ - - [Timeout(15000)] - [TestCase("ExternalTcpEndPoint", false)] - public async Task connection_should_be_closed_by_remote_party(string endpointProperty, bool secure) - { - IPEndPoint endpoint = (IPEndPoint)_nodes[0].GetType().GetProperty(endpointProperty).GetValue(_nodes[0], null); - await WaitForEndpoint(endpoint); - - var closedEvent = new ManualResetEventSlim(); - TaskCompletionSource connectionResult = new(TaskCreationOptions.RunContinuationsAsynchronously); - - ITcpConnection connection; - if (!secure) - { - connection = TcpConnection.CreateConnectingTcpConnection( - Guid.NewGuid(), - endpoint, - new TcpClientConnector(), - TimeSpan.FromSeconds(5), - (conn) => connectionResult.TrySetResult(SocketError.Success), - (conn, error) => connectionResult.TrySetResult(error), - false); - } - else - { - connection = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - endpoint.GetHost(), - null, - endpoint, - delegate - { return (true, null); }, - null, - new TcpClientConnector(), - TimeSpan.FromSeconds(5), - (conn) => connectionResult.TrySetResult(SocketError.Success), - (conn, error) => connectionResult.TrySetResult(error), - false); - } - - connection.ConnectionClosed += (conn, error) => closedEvent.Set(); - - SocketError result = await connectionResult.Task.WithTimeout(); - Assert.AreEqual(SocketError.Success, result); - var data = new List> { - new ArraySegment(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) - }; - connection.EnqueueSend(data); - Assert.True(closedEvent.Wait(10000)); - connection.Close("intentional close"); - } - - private static async Task WaitForEndpoint(IPEndPoint endpoint) - { - using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5)); - - while (!timeout.IsCancellationRequested) - { - using var client = new TcpClient(); - - try - { - await client.ConnectAsync(endpoint.Address, endpoint.Port, timeout.Token); - return; - } - catch (Exception ex) when (ex is SocketException or OperationCanceledException) - { - await Task.Delay(100, CancellationToken.None); - } - } - - throw new TimeoutException($"TCP endpoint {endpoint} did not accept connections before the test timeout."); - } -} diff --git a/src/EventStore.Core.Tests/Services/Transport/Tcp/with_intermediate_certificates.cs b/src/EventStore.Core.Tests/Services/Transport/Tcp/with_intermediate_certificates.cs deleted file mode 100644 index 2b1c2f1540..0000000000 --- a/src/EventStore.Core.Tests/Services/Transport/Tcp/with_intermediate_certificates.cs +++ /dev/null @@ -1,156 +0,0 @@ -using System; -using System.Net; -using System.Net.Security; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Core.Tests.Certificates; -using EventStore.Core.Tests.Helpers; -using EventStore.Transport.Tcp; -using NUnit.Framework; - -namespace EventStore.Core.Tests.Services.Transport.Tcp; - -[TestFixture] -public class with_intermediate_certificates : with_certificate_chain_of_length_3 -{ - private TcpServerListener _listener; - private IPEndPoint _serverEndPoint; - private ITcpConnection _client; - private Func _clientCertValidator; - private X509Certificate2 _cert; - - [SetUp] - public void SetUp() - { - // certificate exported to PKCS #12 due to this issue on Windows: https://github.com/dotnet/runtime/issues/45680 - _cert = X509CertificateLoader.LoadPkcs12(_leaf.ExportToPkcs12(), null); - - _clientCertValidator = (_, _, _) => (true, null); - _serverEndPoint = new IPEndPoint(IPAddress.Loopback, PortsHelper.GetAvailablePort(IPAddress.Loopback)); - _listener = new TcpServerListener(_serverEndPoint); - _listener.StartListening((endPoint, socket) => - { - TcpConnectionSsl.CreateServerFromSocket( - Guid.NewGuid(), - endPoint, - socket, - () => _cert, - () => new X509Certificate2Collection(_intermediate), - (cert, chain, errors) => _clientCertValidator(cert, chain, errors), - verbose: true); - }, "Secure"); - } - - [Test] - public void server_should_send_intermediate_certificate_during_handshake() - { - var done = new ManualResetEventSlim(false); - - bool gotLeaf = false; - bool gotIntermediate = false; - - _client = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - _serverEndPoint.GetHost(), - null, - _serverEndPoint, - (certificate, chain, _, _) => - { - gotLeaf = _leaf.Equals(certificate); - foreach (var chainElement in chain.ChainElements) - { - if (chainElement.Certificate.Equals(_intermediate)) - { - gotIntermediate = true; - } - } - - done.Set(); - return (true, null); - }, - null, - new TcpClientConnector(), - TcpConnectionManager.ConnectionTimeout, - conn => { }, - (conn, err) => { }, - verbose: true); - - Assert.True(done.Wait(20000), "Took too long to receive completion."); - Assert.True(gotLeaf); - Assert.True(gotIntermediate); - } - - [Test, Ignore("Skipped since it adds an intermediate certificate to the current user's store")] - public void client_should_send_intermediate_certificate_during_handshake() - { - try - { - // see: https://github.com/dotnet/runtime/issues/47680#issuecomment-771093045 - AddIntermediateCertificateToStore(); - - var done = new ManualResetEventSlim(false); - - bool gotLeaf = false; - bool gotIntermediate = false; - - _clientCertValidator = (certificate, chain, _) => - { - gotLeaf = _leaf.Equals(certificate); - foreach (var chainElement in chain.ChainElements) - { - if (chainElement.Certificate.Equals(_intermediate)) - { - gotIntermediate = true; - } - } - - done.Set(); - return (true, null); - }; - - _client = TcpConnectionSsl.CreateConnectingConnection( - Guid.NewGuid(), - _serverEndPoint.GetHost(), - null, - _serverEndPoint, - (_, _, _, _) => (true, null), - () => new X509Certificate2Collection(_cert), - new TcpClientConnector(), - TcpConnectionManager.ConnectionTimeout, - conn => { }, - (conn, err) => { }, - verbose: true); - - Assert.True(done.Wait(20000), "Took too long to receive completion."); - Assert.True(gotLeaf); - Assert.True(gotIntermediate); - } - finally - { - RemoveIntermediateCertificateFromStore(); - } - } - - private void AddIntermediateCertificateToStore() - { - using var intermediateStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser); - intermediateStore.Open(OpenFlags.ReadWrite); - intermediateStore.Add(_intermediate); - } - - private void RemoveIntermediateCertificateFromStore() - { - using var intermediateStore = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser); - intermediateStore.Open(OpenFlags.ReadWrite); - intermediateStore.Remove(_intermediate); - } - - [TearDown] - public void TearDown() - { - _listener.Stop(); - _client.Close("Normal close."); - } -} diff --git a/src/EventStore.Core.Tests/Services/UserManagementService/user_management_service.cs b/src/EventStore.Core.Tests/Services/UserManagementService/user_management_service.cs index f933cb9cfc..f6c400ee33 100644 --- a/src/EventStore.Core.Tests/Services/UserManagementService/user_management_service.cs +++ b/src/EventStore.Core.Tests/Services/UserManagementService/user_management_service.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; -using EventStore.ClientAPI.Common.Utils; +using EventStore.Common.Utils; using EventStore.Core.Messages; using EventStore.Core.Services; using EventStore.Core.Services.UserManagement; diff --git a/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs b/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs index 8ae7429f0c..fc9ce572a8 100644 --- a/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs +++ b/src/EventStore.Core.Tests/Services/VNode/InaugurationManager/InaugurationManagerTests.cs @@ -20,8 +20,7 @@ public abstract class InaugurationManagerTests protected readonly MemberInfo _leader = MemberInfo.ForVNode( default, default, default, default, - new DnsEndPoint("localhost", default), default, default, default, - new DnsEndPoint("localhost", default), default, default, default, + new DnsEndPoint("localhost", default), default, default, default, default, default, default, default, default, default, default); protected readonly long _replicationTarget = 400; protected readonly long _indexTarget = 400; diff --git a/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs b/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs index f2adb06c98..d1011fa934 100644 --- a/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs +++ b/src/EventStore.Core.Tests/Services/VNode/ShutdownServiceTests.cs @@ -1,5 +1,4 @@ using System; -using System.Net; using DotNext.Net.Http; using EventStore.Core.Data; using EventStore.Core.Messages; @@ -16,10 +15,6 @@ public class ShutdownServiceTests = new( Guid.NewGuid(), 0, - new IPEndPoint(0, 0), - new IPEndPoint(IPAddress.Loopback, 1), - new IPEndPoint(IPAddress.Loopback, 2), - new IPEndPoint(IPAddress.Loopback, 3), new HttpEndPoint(new Uri("http://www.trogondb.com")), true); [Test] diff --git a/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs b/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs index 68de1f0112..9a34d143df 100644 --- a/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs +++ b/src/EventStore.Core.Tests/Services/VNode/leader_info_provider.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; -using EventStore.Common.Utils; using EventStore.Core.Cluster; using EventStore.Core.Data; using EventStore.Core.Services.VNode; @@ -13,252 +11,45 @@ namespace EventStore.Core.Tests.Services.VNode; [TestFixture] public class leader_info_provider { - private const string DefaultHttpEndPoint = "9.9.9.9:9"; - - public static IEnumerable TestCases() - { - var leaderInfoCases = new TestCase[] { - new ("Leader: NoEndPoints", - Given: new (Leader: new()), - Expected: new (TcpEndPoint: null)), - new ("Leader: TcpEndPoint", - Given: new (Leader: new(TcpEndPoint: "1.1.1.1:1")), - Expected: new (TcpEndPoint: "1.1.1.1:1", IsTcpSecure: false)), - new ("Leader: SecureTcpEndPoint", - Given: new (Leader: new(SecureTcpEndPoint: "2.2.2.2:2")), - Expected: new (TcpEndPoint: "2.2.2.2:2", IsTcpSecure: true)), - new ("Leader: TcpEndPoint + SecureTcpEndPoint", - Given: new (Leader: new(TcpEndPoint: "1.1.1.1:1", SecureTcpEndPoint: "2.2.2.2:2")), - Expected: new (TcpEndPoint: "1.1.1.1:1", IsTcpSecure: true)), - // AdvertiseTcpPort - new ("Leader: AdvertiseTcpPort & NoEndPoints", - Given: new (Leader: new(AdvertiseTcpPort: 9)), - Expected: new (TcpEndPoint: null, IsTcpSecure: false)), - new ("Leader: AdvertiseTcpPort & TcpEndPoint", - Given: new (Leader: new(AdvertiseTcpPort: 9, TcpEndPoint: "1.1.1.1:1")), - Expected: new (TcpEndPoint: "1.1.1.1:9")), - new ("Leader: AdvertiseTcpPort & SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseTcpPort: 9, SecureTcpEndPoint: "2.2.2.2:2")), - Expected: new (TcpEndPoint: "2.2.2.2:9", IsTcpSecure: true)), - new ("Leader: AdvertiseTcpPort & TcpEndPoint + SecureTcpEndPoint", - Given: new (Leader: new(TcpEndPoint: "1.1.1.1:1", SecureTcpEndPoint: "2.2.2.2:2", AdvertiseTcpPort: 9)), - Expected: new (TcpEndPoint: "1.1.1.1:9", IsTcpSecure: true)), - // AdvertiseHost - new ("Leader: AdvertiseHost & NoEndPoints", - Given: new (Leader: new(AdvertiseHost: "host", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: null, IsTcpSecure: false, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost & TcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", TcpEndPoint: "1.1.1.1:1", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:1", HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost & SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", SecureTcpEndPoint: "2.2.2.2:2", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:2", IsTcpSecure: true, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost & TcpEndPoint + SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", TcpEndPoint: "1.1.1.1:1", SecureTcpEndPoint: "2.2.2.2:2", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:1", IsTcpSecure: true, HttpEndPoint: "host:3")), //?? is this intended, secure with port of insecure - // AdvertiseHost + AdvertisePort - new ("Leader: AdvertiseHost + Port & NoEndPoints", - Given: new (Leader: new(AdvertiseHost: "host", AdvertiseTcpPort: 9, HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: null, IsTcpSecure: false, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost + Port & TcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", AdvertiseTcpPort: 9, TcpEndPoint: "1.1.1.1:1", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:9", IsTcpSecure: false, HttpEndPoint: "host:3")), - new ("Leader: AdvertiseHost + Port & SecureTcpEndPoint", - Given: new (Leader: new(AdvertiseHost: "host", AdvertiseTcpPort: 9, SecureTcpEndPoint: "2.2.2.2:2", HttpEndPoint: "3.3.3.3:3")), - Expected: new (TcpEndPoint: "host:9", IsTcpSecure: true, HttpEndPoint: "host:3")), - // AdvertiseHttpPort - new ("Leader: AdvertiseHttpPort & NoEndPoints", - Given: new (Leader: new(AdvertiseHttpPort: 8, HttpEndPoint: "3.3.3.3:3")), - Expected: new (HttpEndPoint: "3.3.3.3:8")), - new ("Leader: AdvertiseHttpPort & AdvertiseHost", - Given: new (Leader: new(AdvertiseHttpPort: 8, AdvertiseHost: "host", HttpEndPoint: "3.3.3.3:3")), - Expected: new (HttpEndPoint: "host:8")), - }; - - var nodeInfoCases = new TestCase[] { - new ("Gossip: NoEndPoints", - Given: new (Gossip: new()), - Expected: new (TcpEndPoint: null)), - new ("Gossip: TcpEndPoint", - Given: new (Gossip: new(TcpEndPoint: "4.4.4.4:4")), - Expected: new (TcpEndPoint: "4.4.4.4:4", IsTcpSecure: false)), - new ("Gossip: SecureTcpEndPoint", - Given: new (Gossip: new(SecureTcpEndPoint: "5.5.5.5:5")), - Expected: new (TcpEndPoint: "5.5.5.5:5", IsTcpSecure: true)), - // AnyIP SecureTcpEndPoint - new ("Gossip: TcpEndPoint + SecureTcpEndPoint", - Given: new (Gossip: new(TcpEndPoint: "4.4.4.4:4", SecureTcpEndPoint: "5.5.5.5:5")), - Expected: new (TcpEndPoint: "4.4.4.4:4", IsTcpSecure: true)), - // AnyIP & HttpEndPoint - new ("Gossip: HttpEndPoint", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8")), - Expected: new (HttpEndPoint: "8.8.8.8:8")), - new ("Gossip: HttpEndPoint & AdvertiseHost", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8", AdvertiseHost: "host")), - Expected: new (HttpEndPoint: "host:8")), - new ("Gossip: HttpEndPoint & AdvertisePort", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8", AdvertiseHttpPort: 9)), - Expected: new (HttpEndPoint: "8.8.8.8:9")), - new ("Gossip: HttpEndPoint & AdvertiseHost + Port", - Given: new (Gossip: new(HttpEndPoint: "8.8.8.8:8", AdvertiseHost: "host", AdvertiseHttpPort: 9)), - Expected: new (HttpEndPoint: "host:9")), - }; - - foreach (var testCase in leaderInfoCases.Concat(nodeInfoCases)) - { - yield return new object[] { testCase }; - } - } - - [TestCaseSource(nameof(TestCases))] - public void should_provide_as_expected(TestCase t) - { - - var given = t.BuildGiven(); - var expected = t.BuildExpected(); - - LeaderInfoProvider leaderInfoProvider = new LeaderInfoProvider( - given.GossipInfo, - given.LeaderInfo); - - var result = leaderInfoProvider.GetLeaderInfoEndPoints(); - - AssertAreEqual(expected.TcpEndPoint, result.AdvertisedTcpEndPoint, "TcpEndPoint"); - AssertAreEqual(expected.HttpEndPoint, result.AdvertisedHttpEndPoint, "HttpEndPoint"); - Assert.AreEqual(expected.IsSecure, result.IsTcpEndPointSecure); - } - - private void AssertAreEqual(EndPoint expected, EndPoint actual, string msg) - { - - if (expected == null) - { - Assert.IsNull(actual); - return; - } - - Assert.AreEqual(expected.GetHost(), actual.GetHost(), $"{msg} host"); - Assert.AreEqual(expected.GetPort(), actual.GetPort(), $"{msg} port"); - } - - public record TestCase(string Test, GivenInput Given, ExpectedInput Expected) - { - public override string ToString() => Test; - - public Given BuildGiven() => Given.Build(); - public Expected BuildExpected() => Expected.Build(); - } - - public record Given(MemberInfo LeaderInfo, GossipAdvertiseInfo GossipInfo); - - public record Expected(EndPoint TcpEndPoint, bool IsSecure, EndPoint HttpEndPoint); - - public record LeaderInput( - string TcpEndPoint = null, - string SecureTcpEndPoint = null, - string HttpEndPoint = null, - int AdvertiseTcpPort = 0, - int AdvertiseHttpPort = 0, - string AdvertiseHost = null); - - public record GossipInput( - string TcpEndPoint = null, - string SecureTcpEndPoint = null, - string HttpEndPoint = null, - int AdvertiseTcpPort = 0, - int AdvertiseHttpPort = 0, - string AdvertiseHost = null); - - public record GivenInput( - LeaderInput Leader = null, - GossipInput Gossip = null) - { - - public MemberInfo BuildLeaderInfo() - { - if (Leader == null) - { - return null; - } - - return MemberInfo.Initial( - instanceId: Guid.NewGuid(), - timeStamp: DateTime.Now, - state: VNodeState.Initializing, - isAlive: false, - internalTcpEndPoint: IPEndPoint.Parse("1.1.2.2:3"), - internalSecureTcpEndPoint: IPEndPoint.Parse("4.4.5.5:6"), - externalTcpEndPoint: Leader.TcpEndPoint == null ? null : IPEndPoint.Parse(Leader.TcpEndPoint), - externalSecureTcpEndPoint: Leader.SecureTcpEndPoint == null ? null : IPEndPoint.Parse(Leader.SecureTcpEndPoint), - httpEndPoint: IPEndPoint.Parse(Leader.HttpEndPoint ?? DefaultHttpEndPoint), - advertiseHostToClientAs: Leader.AdvertiseHost, - advertiseHttpPortToClientAs: Leader.AdvertiseHttpPort, - advertiseTcpPortToClientAs: Leader.AdvertiseTcpPort, - nodePriority: 1, - isReadOnlyReplica: false); - } - - public GossipAdvertiseInfo BuildGossipInfo() - { - return new GossipAdvertiseInfo( - externalTcp: ParseDnsEndPoint(Gossip?.TcpEndPoint), - externalSecureTcp: ParseDnsEndPoint(Gossip?.SecureTcpEndPoint), - httpEndPoint: ParseDnsEndPoint(Gossip?.HttpEndPoint ?? DefaultHttpEndPoint), - advertiseHostToClientAs: Gossip?.AdvertiseHost, - advertiseHttpPortToClientAs: Gossip?.AdvertiseHttpPort ?? 8, - advertiseTcpPortToClientAs: Gossip?.AdvertiseTcpPort ?? 9, - internalTcp: new DnsEndPoint("internal.tcp", 1), - internalSecureTcp: new DnsEndPoint("secure.tcp", 2), - advertiseInternalHostAs: null, - advertiseExternalHostAs: null, - advertiseHttpPortAs: 0); - } - - public Given Build() - { - return new Given( - BuildLeaderInfo(), - BuildGossipInfo()); - } - } - - public record ExpectedInput(string TcpEndPoint = null, string HttpEndPoint = null, bool IsTcpSecure = false) - { - public Expected Build() - { - return new Expected( - ParseEndPoint(TcpEndPoint), - IsTcpSecure, - ParseEndPoint(HttpEndPoint ?? DefaultHttpEndPoint)); - } - } - - private static EndPoint ParseEndPoint(string s) - { - if (s == null) - { - return null; - } - - if (IPEndPoint.TryParse(s, out var ipEndPoint)) - { - return ipEndPoint; - } - - return ParseDnsEndPoint(s); - } - - private static DnsEndPoint ParseDnsEndPoint(string s) - { - if (s == null) - { - return null; - } - - var parts = s.Split(":"); - var host = parts[0]; - var port = int.Parse(parts[1]); - - return new DnsEndPoint(host, port); - } + public static IEnumerable Cases() + { + yield return Case("leader endpoint", Leader("1.1.1.1", 2113), Gossip("2.2.2.2", 2113), "1.1.1.1", 2113); + yield return Case("leader advertised host", Leader("1.1.1.1", 2113, "leader.example"), + Gossip("2.2.2.2", 2113), "leader.example", 2113); + yield return Case("leader advertised port", Leader("1.1.1.1", 2113, advertisePort: 3113), + Gossip("2.2.2.2", 2113), "1.1.1.1", 3113); + yield return Case("leader advertised endpoint", Leader("1.1.1.1", 2113, "leader.example", 3113), + Gossip("2.2.2.2", 2113), "leader.example", 3113); + yield return Case("local gossip endpoint", null, Gossip("2.2.2.2", 2113), "2.2.2.2", 2113); + yield return Case("local advertised endpoint", null, Gossip("2.2.2.2", 2113, "node.example", 3113), + "node.example", 3113); + } + + [TestCaseSource(nameof(Cases))] + public void returns_the_advertised_http_endpoint(MemberInfo leader, GossipAdvertiseInfo gossip, EndPoint expected) + { + var result = new LeaderInfoProvider(gossip, leader).GetLeaderInfoEndPoint(); + + Assert.AreEqual(expected, result); + } + + private static TestCaseData Case(string name, MemberInfo leader, GossipAdvertiseInfo gossip, + string expectedHost, int expectedPort) => + new TestCaseData(leader, gossip, new DnsEndPoint(expectedHost, expectedPort)).SetName(name); + + private static MemberInfo Leader(string host, int port, string advertiseHost = null, int advertisePort = 0) => + MemberInfo.Initial( + Guid.NewGuid(), + DateTime.UtcNow, + VNodeState.Leader, + true, + new DnsEndPoint(host, port), + advertiseHost, + advertisePort, + 0, + false); + + private static GossipAdvertiseInfo Gossip(string host, int port, string advertiseHost = null, + int advertisePort = 0) => + new(new DnsEndPoint(host, port), advertiseHost, advertisePort); } diff --git a/src/EventStore.Core.Tests/Services/VNode/startup_should.cs b/src/EventStore.Core.Tests/Services/VNode/startup_should.cs index 32c20dac8e..8092cda91f 100644 --- a/src/EventStore.Core.Tests/Services/VNode/startup_should.cs +++ b/src/EventStore.Core.Tests/Services/VNode/startup_should.cs @@ -12,7 +12,6 @@ using EventStore.Core.Configuration.Sources; using EventStore.Core.Services.Monitoring; using EventStore.Core.Tests.Helpers; -using EventStore.Core.Tests.Services.Transport.Tcp; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.Configuration; @@ -31,8 +30,6 @@ public async Task propagate_cancellation_into_startup_tasks() var startupTaskStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var blockingStartupTask = new BlockingStartupTask(startupTaskStarted); var ip = IPAddress.Loopback; - var tcpEndPoint = new IPEndPoint(ip, PortsHelper.GetAvailablePort(ip)); - var internalEndPoint = new IPEndPoint(ip, PortsHelper.GetAvailablePort(ip)); var httpEndPoint = new IPEndPoint(ip, PortsHelper.GetAvailablePort(ip)); var options = new ClusterVNodeOptions @@ -44,11 +41,6 @@ public async Task propagate_cancellation_into_startup_tasks() StatsPeriodSec = 60 * 60, WorkerThreads = 1 }, - Interface = new() - { - ReplicationHeartbeatInterval = 10_000, - ReplicationHeartbeatTimeout = 10_000 - }, Cluster = new() { DiscoverViaDns = false, @@ -68,21 +60,12 @@ public async Task propagate_cancellation_into_startup_tasks() LoadedOptions = ClusterVNodeOptions.GetLoadedOptions(new ConfigurationBuilder() .AddEventStoreDefaultValues() .Build()), - }.Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate()) - .WithReplicationEndpointOn(internalEndPoint) - .WithExternalTcpOn(tcpEndPoint) + }.Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate()) .WithNodeEndpointOn(httpEndPoint) .RunOnDisk(System.IO.Path.Combine(PathName, "db")); - var configuration = new ConfigurationBuilder() - .AddInMemoryCollection(new KeyValuePair[] { - new("EventStore:TcpUnitTestPlugin:NodeTcpPort", tcpEndPoint.Port.ToString()), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatInterval", "10000"), - new("EventStore:TcpUnitTestPlugin:NodeHeartbeatTimeout", "10000"), - new("EventStore:TcpUnitTestPlugin:Insecure", options.Application.Insecure.ToString()), - }) - .Build(); + var configuration = new ConfigurationBuilder().Build(); var node = new ClusterVNode( options, diff --git a/src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs b/src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs deleted file mode 100644 index 2cc5eb14dd..0000000000 --- a/src/EventStore.Core.Tests/TcpApiTestPlugin/PublicTcpApiTestService.cs +++ /dev/null @@ -1,123 +0,0 @@ -#nullable enable - -using System; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Core; -using EventStore.Core.Bus; -using EventStore.Core.Certificates; -using EventStore.Core.Messages; -using EventStore.Core.Services; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Plugins.Authentication; -using Microsoft.Extensions.Hosting; -using Serilog; - -namespace EventStore.TcpUnitTestPlugin; - -public class PublicTcpApiTestService : IHostedService -{ - static readonly ILogger Logger = Log.ForContext(); - private readonly TcpService _tcpService; - private int _systemInitialized; - - PublicTcpApiTestService(TcpService tcpService, ISubscriber bus) - { - _tcpService = tcpService; - - bus.Subscribe(new AdHocHandler(_ => StartTcpService())); - bus.Subscribe(new AdHocHandler(_ => StartTcpService())); - bus.Subscribe(tcpService); - - _ = Task.Run(async () => - { - await Task.Delay(TimeSpan.FromHours(1)); - Logger.Warning("Shutting down TCP unit tests"); - tcpService.Handle(new SystemMessage.BecomeShuttingDown(Guid.NewGuid(), true, true)); - }); - } - - public static PublicTcpApiTestService Insecure( - TcpApiTestOptions options, - IAuthenticationProvider authProvider, - AuthorizationGateway authGateway, - StandardComponents components - ) - { - var endpoint = new IPEndPoint(IPAddress.Loopback, options.NodeTcpPort); - - var tcpService = new TcpService( - publisher: components.MainQueue, - serverEndPoint: endpoint, - networkSendQueue: components.NetworkSendService, - serviceType: TcpServiceType.External, securityType: TcpSecurityType.Normal, - dispatcher: new ClientTcpDispatcher(options.WriteTimeoutMs), - heartbeatInterval: TimeSpan.FromMilliseconds(options.NodeHeartbeatInterval), - heartbeatTimeout: TimeSpan.FromMilliseconds(options.NodeHeartbeatTimeout), - authProvider: authProvider, - authorizationGateway: authGateway, - certificateSelector: null, - intermediatesSelector: null, - sslClientCertValidator: null, - connectionPendingSendBytesThreshold: options.ConnectionPendingSendBytesThreshold, - connectionQueueSizeThreshold: options.ConnectionQueueSizeThreshold - ); - - return new(tcpService, components.MainBus); - } - - public static PublicTcpApiTestService Secure( - TcpApiTestOptions options, - IAuthenticationProvider authProvider, - AuthorizationGateway authGateway, - StandardComponents components, - CertificateProvider? certificateProvider - ) - { - var endpoint = new IPEndPoint(IPAddress.Loopback, options.NodeTcpPort); - - var tcpService = new TcpService( - publisher: components.MainQueue, - serverEndPoint: endpoint, - networkSendQueue: components.NetworkSendService, - serviceType: TcpServiceType.External, securityType: TcpSecurityType.Secure, - dispatcher: new ClientTcpDispatcher(options.WriteTimeoutMs), - heartbeatInterval: TimeSpan.FromMilliseconds(options.NodeHeartbeatInterval), - heartbeatTimeout: TimeSpan.FromMilliseconds(options.NodeHeartbeatTimeout), - authProvider: authProvider, - authorizationGateway: authGateway, - certificateSelector: () => certificateProvider?.Certificate, - intermediatesSelector: () => - { - var intermediates = certificateProvider?.IntermediateCerts; - return intermediates == null ? null : new X509Certificate2Collection(intermediates); - }, - sslClientCertValidator: delegate - { return (true, null); }, - connectionPendingSendBytesThreshold: options.ConnectionPendingSendBytesThreshold, - connectionQueueSizeThreshold: options.ConnectionQueueSizeThreshold - ); - - return new(tcpService, components.MainBus); - } - - public Task StartAsync(CancellationToken cancellationToken) - { - StartTcpService(); - return Task.CompletedTask; - } - - public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - - private void StartTcpService() - { - if (Interlocked.Exchange(ref _systemInitialized, 1) == 1) - { - return; - } - - _tcpService.Handle(new SystemMessage.SystemInit()); - } -} diff --git a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs b/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs deleted file mode 100644 index a2be50f225..0000000000 --- a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestOptions.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace EventStore.TcpUnitTestPlugin; - -public class TcpApiTestOptions -{ - public int NodeTcpPort { get; init; } = 1113; - public int NodeHeartbeatInterval { get; init; } = 2_000; - public int NodeHeartbeatTimeout { get; init; } = 1_000; - public int ConnectionPendingSendBytesThreshold { get; set; } = 10 * 1_024 * 1_024; - public int ConnectionQueueSizeThreshold { get; set; } = 50_000; - public int WriteTimeoutMs { get; set; } = 2_000; - public bool Insecure { get; init; } = false; -} diff --git a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs b/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs deleted file mode 100644 index 543fe02ca7..0000000000 --- a/src/EventStore.Core.Tests/TcpApiTestPlugin/TcpApiTestPlugin.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core; -using EventStore.Core.Certificates; -using EventStore.Core.Services; -using EventStore.Plugins; -using EventStore.Plugins.Authentication; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Serilog; - -namespace EventStore.TcpUnitTestPlugin; - -public class TcpApiTestPlugin() : SubsystemsPlugin(name: "TcpTestApi") -{ - static readonly ILogger Logger = Log.ForContext(); - - public override void ConfigureServices(IServiceCollection services, IConfiguration configuration) - { - var options = configuration.GetSection("EventStore:TcpUnitTestPlugin").Get() ?? new(); - - services.AddHostedService(serviceProvider => - { - var components = serviceProvider.GetRequiredService(); - var authGateway = serviceProvider.GetRequiredService(); - var authProvider = serviceProvider.GetRequiredService(); - - return options.Insecure - ? PublicTcpApiTestService.Insecure(options, authProvider, authGateway, components) - : PublicTcpApiTestService.Secure(options, authProvider, authGateway, components, serviceProvider.GetService()); - }); - } - - public override Task Start() - { - Logger.Debug("{Name}-{Version} test plugin is loaded", Name, Version); - return Task.CompletedTask; - } -} diff --git a/src/EventStore.Core.Tests/TransactionLog/Truncation/when_truncating_database.cs b/src/EventStore.Core.Tests/TransactionLog/Truncation/when_truncating_database.cs index a21d8f3546..8047641bf1 100644 --- a/src/EventStore.Core.Tests/TransactionLog/Truncation/when_truncating_database.cs +++ b/src/EventStore.Core.Tests/TransactionLog/Truncation/when_truncating_database.cs @@ -20,7 +20,6 @@ public async Task everything_should_go_fine() var miniNode = new MiniNode(PathName); await miniNode.Start(); - var tcpPort = miniNode.TcpEndPoint.Port; var httpPort = miniNode.HttpEndPoint.Port; const int cnt = 50; var countdown = new CountdownEvent(cnt); @@ -43,12 +42,12 @@ public async Task everything_should_go_fine() await miniNode.Shutdown(keepDb: true); // --- first restart and truncation - miniNode = new MiniNode(PathName, tcpPort, httpPort); + miniNode = new MiniNode(PathName, httpPort); await miniNode.Start(); await miniNode.Shutdown(keepDb: true); // --- second restart after truncation - miniNode = new MiniNode(PathName, tcpPort, httpPort); + miniNode = new MiniNode(PathName, httpPort); await miniNode.Start(); Assert.AreEqual(-1, miniNode.Db.Config.TruncateCheckpoint.Read()); Assert.That(miniNode.Db.Config.WriterCheckpoint.Read(), Is.GreaterThanOrEqualTo(truncatePosition)); @@ -61,7 +60,7 @@ public async Task everything_should_go_fine() await miniNode.Shutdown(keepDb: true); // -- third restart - miniNode = new MiniNode(PathName, tcpPort, httpPort); + miniNode = new MiniNode(PathName, httpPort); Assert.AreEqual(-1, miniNode.Db.Config.TruncateCheckpoint.Read()); await miniNode.Start(); @@ -99,16 +98,14 @@ public async Task with_truncate_position_in_completed_chunk_everything_should_go await miniNode.Shutdown(keepDb: true); - var tcpPort = miniNode.TcpEndPoint.Port; - // --- first restart and truncation - miniNode = new MiniNode(PathName, tcpPort, httpPort, chunkSize: chunkSize, + miniNode = new MiniNode(PathName, httpPort, chunkSize: chunkSize, cachedChunkSize: cachedSize); await miniNode.Start(); await miniNode.Shutdown(keepDb: true); // --- second restart after truncation - miniNode = new MiniNode(PathName, tcpPort, httpPort, chunkSize: chunkSize, + miniNode = new MiniNode(PathName, httpPort, chunkSize: chunkSize, cachedChunkSize: cachedSize); await miniNode.Start(); Assert.AreEqual(-1, miniNode.Db.Config.TruncateCheckpoint.Read()); diff --git a/src/EventStore.Core.Tests/Transforms/TransformTests.cs b/src/EventStore.Core.Tests/Transforms/TransformTests.cs index b760c89303..0f93e821af 100644 --- a/src/EventStore.Core.Tests/Transforms/TransformTests.cs +++ b/src/EventStore.Core.Tests/Transforms/TransformTests.cs @@ -4,8 +4,8 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Tests.ClientAPI.Helpers; +using EventStore.Client.Streams; +using EventStore.Core.Services.Transport.Grpc; using EventStore.Core.Tests.Helpers; using EventStore.Core.Tests.Transforms.BitFlip; using EventStore.Core.Tests.Transforms.ByteDup; @@ -14,7 +14,11 @@ using EventStore.Core.TransactionLog.Chunks.TFChunk; using EventStore.Core.Transforms.Identity; using EventStore.Plugins.Transforms; +using Google.Protobuf; +using Grpc.Net.Client; using NUnit.Framework; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; +using StreamsClient = EventStore.Client.Streams.Streams.StreamsClient; namespace EventStore.Core.Tests.Transforms; @@ -33,7 +37,7 @@ public class TransformTests : SpecificationWithDirectoryP public async Task transform_works(string transform) { MiniNode node = null; - IEventStoreConnection connection = null; + GrpcChannel connection = null; var dbPath = Path.Combine(PathName, $"node-{Guid.NewGuid()}"); try { @@ -82,7 +86,7 @@ private async ValueTask VerifyChecksums(MiniNode node, Ca } } - private async Task<(MiniNode, IEventStoreConnection)> CreateNode(string dbPath, string transform) + private async Task<(MiniNode, GrpcChannel)> CreateNode(string dbPath, string transform) { IDbTransform dbTransform = transform switch { @@ -103,14 +107,13 @@ private async ValueTask VerifyChecksums(MiniNode node, Ca await node.Start(StartupTimeout); var connection = BuildConnection(node); - await connection.ConnectAsync(); return (node, connection); } private static async Task ShutdownNode( MiniNode node, - IEventStoreConnection connection, + GrpcChannel connection, bool keepDb = false) { if (node is not null) @@ -121,52 +124,82 @@ private static async Task ShutdownNode( connection?.Dispose(); } - private static IEventStoreConnection BuildConnection(MiniNode node) + private static GrpcChannel BuildConnection(MiniNode node) { - return TestConnection.Create(node.TcpEndPoint); + return GrpcChannel.ForAddress(new UriBuilder { Scheme = Uri.UriSchemeHttps }.Uri, + new GrpcChannelOptions { HttpClient = node.HttpClient, DisposeHttpClient = false }); } - private static async Task WriteEvents(IEventStoreConnection connection) + private static async Task WriteEvents(GrpcChannel connection) { var writtenIds = new List(); + var client = new StreamsClient(connection); for (var i = 0; i < NumEvents / BatchSize; i++) { var events = CreateEventBatch(BatchSize); - await connection.AppendToStreamAsync("test", ExpectedVersion.Any, events); - writtenIds.AddRange(events.Select(x => x.EventId)); + using var call = client.Append(); + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8("test") } + } + }); + foreach (var @event in events) + await call.RequestStream.WriteAsync(new AppendReq { ProposedMessage = @event }); + await call.RequestStream.CompleteAsync(); + await call.ResponseAsync; + writtenIds.AddRange(events.Select(x => Uuid.FromDto(x.Id).ToGuid())); } return writtenIds.ToArray(); } - private static EventData[] CreateEventBatch(int numEvents) + private static AppendReq.Types.ProposedMessage[] CreateEventBatch(int numEvents) { - var events = new EventData[numEvents]; + var events = new AppendReq.Types.ProposedMessage[numEvents]; for (var i = 0; i < numEvents; i++) { - events[i] = new(eventId: Guid.NewGuid(), - type: "testEvent", - isJson: true, - data: "{ \"foo\":\"bar\" }"u8.ToArray(), - metadata: null); + events[i] = new() + { + Id = Uuid.NewUuid().ToDto(), + Data = ByteString.CopyFromUtf8("{ \"foo\":\"bar\" }"), + CustomMetadata = ByteString.Empty, + Metadata = { + { GrpcMetadata.Type, "testEvent" }, + { GrpcMetadata.ContentType, GrpcMetadata.ContentTypes.ApplicationJson } + } + }; } return events; } - private static async Task VerifyEvents(IEventStoreConnection connection, Guid[] writtenIds) + private static async Task VerifyEvents(GrpcChannel connection, Guid[] writtenIds) { - StreamEventsSlice slice; - var nextEventNumber = 0L; - var readIds = new List(); - do + var client = new StreamsClient(connection); + using var call = client.Read(new ReadReq { - slice = await connection.ReadStreamEventsForwardAsync("test", nextEventNumber, BatchSize, resolveLinkTos: false); - readIds.AddRange(slice.Events.Select(evt => evt.Event.EventId)); - nextEventNumber = slice.NextEventNumber; - } while (!slice.IsEndOfStream); + Options = new() + { + Stream = new() + { + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8("test") }, + Start = new() + }, + ReadDirection = ReadReq.Types.Options.Types.ReadDirection.Forwards, + Count = ulong.MaxValue, + NoFilter = new(), + UuidOption = new() { Structured = new() } + } + }); + var readIds = new List(); + while (await call.ResponseStream.MoveNext(default)) + if (call.ResponseStream.Current.Event is { } resolvedEvent) + readIds.Add(Uuid.FromDto(resolvedEvent.Event.Id).ToGuid()); Assert.AreEqual(NumEvents, readIds.Count); Assert.True(writtenIds.SequenceEqual(readIds)); diff --git a/src/EventStore.Core.Tests/copying_metadata.cs b/src/EventStore.Core.Tests/copying_metadata.cs deleted file mode 100644 index 502e451223..0000000000 --- a/src/EventStore.Core.Tests/copying_metadata.cs +++ /dev/null @@ -1,78 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using EventStore.ClientAPI; -using NUnit.Framework; - -namespace EventStore.Core.Tests; - -[TestFixture] -public class copying_metadata -{ - [Test] - public void copies_empty_metadata() - { - var empty = StreamMetadata.Build().Build(); - var copied = empty.Copy().Build(); - Assert.AreEqual(empty.AsJsonString(), copied.AsJsonString()); - } - - [Test] - public void copies_all_values() - { - var source = StreamMetadata.Build() - .SetCacheControl(TimeSpan.FromDays(1)) - .SetCustomProperty("Test", "Value") - .SetReadRole("foo") - .SetWriteRole("bar") - .SetDeleteRole("baz") - .SetMetadataReadRole("qux") - .SetMetadataWriteRole("quux") - .SetMaxAge(TimeSpan.FromHours(1)) - .SetMaxCount(2) - .SetTruncateBefore(4) - .Build(); - var copied = source.Copy().Build(); - Assert.AreEqual(source.AsJsonString(), copied.AsJsonString()); - } - - [Test] - public void can_mutate_copy() - { - var source = StreamMetadata.Build() - .SetCacheControl(TimeSpan.FromDays(1)) - .SetCustomProperty("Test", "Value") - .SetReadRole("foo") - .SetWriteRole("bar") - .SetDeleteRole("baz") - .SetMetadataReadRole("qux") - .SetMetadataWriteRole("quux") - .SetMaxAge(TimeSpan.FromHours(1)) - .SetMaxCount(2) - .SetTruncateBefore(4) - .Build(); - - var expected = StreamMetadata.Build() - .SetCacheControl(TimeSpan.FromDays(1)) - .SetCustomProperty("Test", "Value") - .SetCustomProperty("Test2", "Value2") - .SetReadRole("foo") - .SetWriteRole("bar") - .SetDeleteRole("baz") - .SetMetadataReadRole("qux") - .SetMetadataWriteRole("quux") - .SetMaxAge(TimeSpan.FromHours(1)) - .SetMaxCount(4) - .SetTruncateBefore(4) - .Build(); - - - var copied = source.Copy() - .SetMaxCount(4) - .SetCustomProperty("Test2", "Value2") - .Build(); - - Assert.AreEqual(expected.AsJsonString(), copied.AsJsonString()); - } -} diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/ClusterVNodeOptionsScenarios.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/ClusterVNodeOptionsScenarios.cs index 7590813f89..a3493c56f4 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/ClusterVNodeOptionsScenarios.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/ClusterVNodeOptionsScenarios.cs @@ -8,7 +8,7 @@ using EventStore.Core.Certificates; using EventStore.Core.LogAbstraction; using EventStore.Core.Tests; -using EventStore.Core.Tests.Services.Transport.Tcp; +using EventStore.Core.Tests.Helpers; using NUnit.Framework; namespace EventStore.Core.XUnit.Tests.Configuration.ClusterNodeOptionsTests; @@ -33,8 +33,8 @@ public override async Task TestFixtureSetUp() _options = WithOptions(options .RunOnDisk(PathName) - .Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate())); + .Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate())); _node = new ClusterVNode(_options, _logFormatFactory, new AuthenticationProviderFactory(c => new InternalAuthenticationProviderFactory(c, _options.DefaultUser)), @@ -84,8 +84,8 @@ public override async Task TestFixtureSetUp() .ReduceMemoryUsageForTests() .InCluster(_clusterSize) .RunOnDisk(PathName) - .Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate())); + .Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate())); _node = new ClusterVNode(_options, _logFormatFactory, new AuthenticationProviderFactory(_ => new InternalAuthenticationProviderFactory(_, _options.DefaultUser)), diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_cluster_node_and_custom_settings.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_cluster_node_and_custom_settings.cs index d80eac39fd..2257c1bdbd 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_cluster_node_and_custom_settings.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_cluster_node_and_custom_settings.cs @@ -127,8 +127,6 @@ protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { return options .Insecure() - .WithExternalTcpOn(new IPEndPoint(IPAddress.Loopback, 11130)) - .WithReplicationEndpointOn(new IPEndPoint(IPAddress.Loopback, 11120)) .AdvertiseExternalHostAs(new DnsEndPoint("196.168.1.1", 11131)) .AdvertiseNodeAs(new DnsEndPoint("196.168.1.1", 21130)); } @@ -139,12 +137,6 @@ public void should_set_the_custom_advertise_info_for_external() Assert.AreEqual(new DnsEndPoint("196.168.1.1", 21130), _node.GossipAdvertiseInfo.HttpEndPoint); } - - [Test] - public void should_set_the_loopback_address_as_advertise_info_for_internal() - { - Assert.AreEqual(new DnsEndPoint(IPAddress.Loopback.ToString(), 11120), _node.GossipAdvertiseInfo.InternalTcp); - } } [TestFixture(typeof(LogFormat.V2), typeof(string))] @@ -154,8 +146,7 @@ protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { return options .Insecure() - .WithReplicationEndpointOn(new IPEndPoint(IPAddress.Any, 11120)) - .WithExternalTcpOn(new IPEndPoint(IPAddress.Any, 11130)) + .WithNodeEndpointOn(new IPEndPoint(IPAddress.Any, 2113)) .AdvertiseExternalHostAs(new DnsEndPoint("10.0.0.1", 11131)); } @@ -165,13 +156,6 @@ public void should_set_the_custom_advertise_info_for_external() Assert.AreEqual(new DnsEndPoint("10.0.0.1", 2113), _node.GossipAdvertiseInfo.HttpEndPoint); } - - [Test] - public void should_set_the_non_loopback_address_as_advertise_info_for_internal() - { - Assert.AreEqual(new DnsEndPoint(IPFinder.GetNonLoopbackAddress().ToString(), 11120), - _node.GossipAdvertiseInfo.InternalTcp); - } } [TestFixture(typeof(LogFormat.V2), typeof(string))] @@ -181,9 +165,7 @@ protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { return options .Insecure() - .WithNodeEndpointOn(new IPEndPoint(IPAddress.Any, 21130)) - .WithExternalTcpOn(new IPEndPoint(IPAddress.Any, 11130)) - .WithReplicationEndpointOn(new IPEndPoint(IPAddress.Loopback, 11120)); + .WithNodeEndpointOn(new IPEndPoint(IPAddress.Any, 21130)); } [Test] @@ -192,12 +174,6 @@ public void should_use_the_non_default_loopback_ip_as_advertise_info_for_externa Assert.AreEqual(new DnsEndPoint(IPFinder.GetNonLoopbackAddress().ToString(), 21130), _node.GossipAdvertiseInfo.HttpEndPoint); } - - [Test] - public void should_use_loopback_ip_as_advertise_info_for_internal() - { - Assert.AreEqual(new DnsEndPoint(IPAddress.Loopback.ToString(), 11120), _node.GossipAdvertiseInfo.InternalTcp); - } } [TestFixture(typeof(LogFormat.V2), typeof(string))] @@ -209,8 +185,6 @@ protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) return options .Insecure() .WithNodeEndpointOn(new IPEndPoint(IPAddress.Loopback, 21130)) - .WithExternalTcpOn(new IPEndPoint(IPAddress.Loopback, 11130)) - .WithReplicationEndpointOn(new IPEndPoint(IPAddress.Any, 11120)) .AdvertiseExternalHostAs(new DnsEndPoint("10.0.0.1", 11131)) .AdvertiseNodeAs(new DnsEndPoint("10.0.0.1", 21131)); } @@ -221,13 +195,6 @@ public void should_set_the_custom_advertise_info_for_external() Assert.AreEqual(new DnsEndPoint("10.0.0.1", 21131), _node.GossipAdvertiseInfo.HttpEndPoint); } - - [Test] - public void should_use_the_non_default_loopback_ip_as_advertise_info_for_internal() - { - Assert.AreEqual(new DnsEndPoint(IPFinder.GetNonLoopbackAddress().ToString(), 11120), - _node.GossipAdvertiseInfo.InternalTcp); - } } [TestFixture(typeof(LogFormat.V2), typeof(string))] diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs index 3ec6433bfd..97b2903377 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs @@ -35,7 +35,6 @@ public void should_create_single_cluster_node() [Test] public void should_have_default_endpoints() { - Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 1112), _node.NodeInfo.InternalSecureTcp); Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 2113), _node.NodeInfo.HttpEndPoint); } @@ -69,11 +68,6 @@ public void should_set_command_line_args_to_default_values() Assert.AreEqual(Opts.MaxProjectionStateSizeDefault, _options.Projection.MaxProjectionStateSize, "MaxProjectionStateSize"); - Assert.AreEqual(700, _options.Interface.ReplicationHeartbeatInterval, "ReplicationHeartbeatInterval"); - - Assert.AreEqual(700, _options.Interface.ReplicationHeartbeatTimeout, - "ReplicationHeartbeatTimeout"); - Assert.AreEqual(TFConsts.ChunkSize, _node.Db.Config.ChunkSize, "ChunkSize"); Assert.AreEqual(TFConsts.ChunksCacheSize, _node.Db.Config.MaxChunksCacheSize, "MaxChunksCacheSize"); } @@ -92,15 +86,11 @@ public void should_create_single_cluster_node() } [Test] - public void should_have_default_secure_endpoints() + public void should_have_default_endpoint() { - var internalTcp = new IPEndPoint(IPAddress.Loopback, 1112); var httpEndPoint = new IPEndPoint(IPAddress.Loopback, 2113); - Assert.AreEqual(internalTcp, _node.NodeInfo.InternalSecureTcp); Assert.AreEqual(httpEndPoint, _node.NodeInfo.HttpEndPoint); - - Assert.AreEqual(internalTcp.ToDnsEndPoint(), _node.GossipAdvertiseInfo.InternalSecureTcp); Assert.AreEqual(httpEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.HttpEndPoint); } @@ -125,13 +115,9 @@ public void should_create_single_cluster_node() [Test] public void should_have_default_endpoints() { - var internalTcp = new IPEndPoint(IPAddress.Loopback, 1112); var httpEndPoint = new IPEndPoint(IPAddress.Loopback, 2113); - Assert.AreEqual(internalTcp, _node.NodeInfo.InternalTcp); Assert.AreEqual(httpEndPoint, _node.NodeInfo.HttpEndPoint); - - Assert.AreEqual(internalTcp.ToDnsEndPoint(), _node.GossipAdvertiseInfo.InternalTcp); Assert.AreEqual(httpEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.HttpEndPoint); } diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_single_node_and_custom_settings.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_single_node_and_custom_settings.cs index b0c3643ceb..0cd84704a9 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_single_node_and_custom_settings.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_single_node_and_custom_settings.cs @@ -27,15 +27,10 @@ public void should_set_the_db_path() public class with_custom_ip_endpoints : SingleNodeScenario { private readonly IPEndPoint _httpEndPoint = new(IPAddress.Parse("127.0.1.15"), 1113); - private readonly IPEndPoint _internalTcp = new(IPAddress.Parse("127.0.1.15"), 1114); - private readonly IPEndPoint _externalTcp = new(IPAddress.Parse("127.0.1.15"), 1115); protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { - return options - .WithNodeEndpointOn(_httpEndPoint) - .WithExternalTcpOn(_externalTcp) - .WithReplicationEndpointOn(_internalTcp); + return options.WithNodeEndpointOn(_httpEndPoint); } [Test] @@ -43,12 +38,6 @@ public void should_set_http_endpoint() { Assert.AreEqual(_httpEndPoint, _node.NodeInfo.HttpEndPoint); } - - [Test] - public void should_set_internal_tcp_endpoint() - { - Assert.AreEqual(_internalTcp, _node.NodeInfo.InternalSecureTcp); - } } [TestFixture(typeof(LogFormat.V2), typeof(string))] @@ -118,10 +107,7 @@ public void should_set_max_chunk_size_to_the_size_of_the_number_of_cached_chunks [TestFixture(typeof(LogFormat.V2), typeof(string))] public class with_custom_advertise_as : SingleNodeScenario { - private readonly IPEndPoint _intTcpEndpoint = new(IPAddress.Parse(InternalIp), 1111); - private readonly IPEndPoint _extTcpEndpoint = new(IPAddress.Parse(ExternalIp), 1113); private readonly IPEndPoint _httpEndpoint = new(IPAddress.Parse(ExternalIp), 1116); - const string InternalIp = "127.0.1.1"; const string ExternalIp = "127.0.1.2"; @@ -129,25 +115,15 @@ protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { return options .WithNodeEndpointOn(_httpEndpoint) - .WithExternalTcpOn(_extTcpEndpoint) - .WithReplicationEndpointOn(_intTcpEndpoint) - .AdvertiseInternalHostAs(new DnsEndPoint($"{InternalIp}.com", _intTcpEndpoint.Port + 1000)) - .AdvertiseExternalHostAs(new DnsEndPoint($"{ExternalIp}.com", _extTcpEndpoint.Port + 1000)) + .AdvertiseExternalHostAs(new DnsEndPoint($"{ExternalIp}.com", _httpEndpoint.Port + 1000)) .AdvertiseNodeAs(new DnsEndPoint($"{ExternalIp}.com", _httpEndpoint.Port + 1000)); } [Test] public void should_set_the_advertise_as_info_to_the_specified() { - Assert.AreEqual(null, _node.GossipAdvertiseInfo.InternalTcp); - Assert.AreEqual(null, _node.GossipAdvertiseInfo.ExternalTcp); - Assert.AreEqual(new DnsEndPoint($"{InternalIp}.com", _intTcpEndpoint.Port + 1000), - _node.GossipAdvertiseInfo.InternalSecureTcp); Assert.AreEqual(new DnsEndPoint($"{ExternalIp}.com", _httpEndpoint.Port + 1000), _node.GossipAdvertiseInfo.HttpEndPoint); - Assert.AreEqual($"{InternalIp}.com", _node.GossipAdvertiseInfo.AdvertiseInternalHostAs); - Assert.AreEqual($"{ExternalIp}.com", _node.GossipAdvertiseInfo.AdvertiseExternalHostAs); - Assert.AreEqual(_httpEndpoint.Port + 1000, _node.GossipAdvertiseInfo.AdvertiseHttpPortAs); } } diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_secure_tcp.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_tls.cs similarity index 70% rename from src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_secure_tcp.cs rename to src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_tls.cs index d8c2c18659..a0f6942090 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_secure_tcp.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_tls.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using EventStore.Common.Utils; @@ -11,22 +10,18 @@ using EventStore.Core.Authorization.AuthorizationPolicies; using EventStore.Core.Certificates; using EventStore.Core.Tests; -using EventStore.Core.Tests.Services.Transport.Tcp; +using EventStore.Core.Tests.Helpers; using NUnit.Framework; namespace EventStore.Core.XUnit.Tests.Configuration.ClusterNodeOptionsTests.when_building; [Category("LongRunning")] [TestFixture(typeof(LogFormat.V2), typeof(string))] -public class with_ssl_enabled_and_using_a_security_certificate_from_file : SingleNodeScenario +public class with_tls_enabled_and_using_a_security_certificate_from_file : SingleNodeScenario { - private readonly IPEndPoint _internalSecTcp = new(IPAddress.Parse("127.0.1.15"), 1114); - private readonly IPEndPoint _externalSecTcp = new(IPAddress.Parse("127.0.1.15"), 1115); - protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { - - return options.WithReplicationEndpointOn(_internalSecTcp).WithExternalTcpOn(_externalSecTcp) with + return options with { CertificateFile = new() { @@ -43,16 +38,10 @@ public void should_set_certificate() Assert.AreNotEqual("n/a", _options.Certificate == null ? "n/a" : _options.Certificate.ToString()); } - [Test] - public void should_set_internal_secure_tcp_endpoint() - { - Assert.AreEqual(_internalSecTcp, _node.NodeInfo.InternalSecureTcp); - } - private string GetCertificatePath() { var filePath = Path.Combine(PathName, $"cert-{Guid.NewGuid()}.p12"); - var cert = ssl_connections.GetUntrustedCertificate(); + var cert = TestCertificates.GetUntrustedCertificate(); using var fileStream = File.Create(filePath); fileStream.Write(cert.ExportToPkcs12()); @@ -62,18 +51,13 @@ private string GetCertificatePath() } [TestFixture(typeof(LogFormat.V2), typeof(string))] -public class with_ssl_enabled_and_using_a_security_certificate : SingleNodeScenario +public class with_tls_enabled_and_using_a_security_certificate : SingleNodeScenario { - private readonly IPEndPoint _internalSecTcp = new(IPAddress.Parse("127.0.1.15"), 1114); - private readonly IPEndPoint _externalSecTcp = new(IPAddress.Parse("127.0.1.15"), 1115); - private readonly X509Certificate2 _certificate = ssl_connections.GetServerCertificate(); + private readonly X509Certificate2 _certificate = TestCertificates.GetServerCertificate(); protected override ClusterVNodeOptions WithOptions(ClusterVNodeOptions options) { - return options - .WithReplicationEndpointOn(_internalSecTcp) - .WithExternalTcpOn(_externalSecTcp) - .Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), _certificate); + return options.Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), _certificate); } [Test] @@ -81,16 +65,10 @@ public void should_set_certificate() { Assert.AreNotEqual("n/a", _options.Certificate == null ? "n/a" : _options.Certificate.ToString()); } - - [Test] - public void should_set_internal_secure_tcp_endpoint() - { - Assert.AreEqual(_internalSecTcp, _node.NodeInfo.InternalSecureTcp); - } } [TestFixture(typeof(LogFormat.V2), typeof(string))] -public class with_secure_tcp_endpoints_and_no_certificates : SpecificationWithDirectoryPerTestFixture +public class with_tls_enabled_and_no_certificates : SpecificationWithDirectoryPerTestFixture { private ClusterVNodeOptions _options; private Exception _caughtException; @@ -98,14 +76,9 @@ public class with_secure_tcp_endpoints_and_no_certificates(_options, LogFormatHelper.LogFormatFactory, @@ -168,7 +141,6 @@ public void should_disable_transport_tls() { Assert.IsTrue(_node.DisableHttps); Assert.IsTrue(_options.Application.TlsDisabled()); - Assert.AreEqual(new IPEndPoint(IPAddress.Loopback, 1112), _node.NodeInfo.InternalTcp); } [Test] diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_shutting_down_an_isolated_cluster_member.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_shutting_down_an_isolated_cluster_member.cs index 900aeddce5..d496a8701f 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_shutting_down_an_isolated_cluster_member.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_shutting_down_an_isolated_cluster_member.cs @@ -13,7 +13,7 @@ using EventStore.Core.LogAbstraction; using EventStore.Core.Messages; using EventStore.Core.Tests; -using EventStore.Core.Tests.Services.Transport.Tcp; +using EventStore.Core.Tests.Helpers; using NUnit.Framework; namespace EventStore.Core.XUnit.Tests.Configuration.ClusterNodeOptionsTests; @@ -43,8 +43,8 @@ static string[] Snapshot(HashSet services) .ReduceMemoryUsageForTests() .InCluster(3) .RunOnDisk(PathName) - .Secure(new X509Certificate2Collection(ssl_connections.GetRootCertificate()), - ssl_connections.GetServerCertificate()); + .Secure(new X509Certificate2Collection(TestCertificates.GetRootCertificate()), + TestCertificates.GetServerCertificate()); var node = new ClusterVNode(options, logFormatFactory, new AuthenticationProviderFactory(c => diff --git a/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs b/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs index 4b2e6ba989..8e47744ed8 100644 --- a/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Metrics/ElectionsCounterTrackerTests.cs @@ -25,8 +25,7 @@ public ElectionsCounterTrackerTests() var endPoint = new DnsEndPoint("127.0.0.1", 1113); var memberInfo = Cluster.MemberInfo.Initial(Guid.Empty, DateTime.UtcNow, VNodeState.Unknown, true, - endPoint, endPoint, endPoint, endPoint, endPoint, - null, 0, 0, 0, false); + endPoint, null, 0, 0, false); _electionsDoneMessage = new ElectionMessage.ElectionsDone(1, 1, memberInfo); } diff --git a/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs b/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs index 84e972bfba..888b7ddda2 100644 --- a/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Services/Storage/InMemory/GossipListenerServiceTests.cs @@ -47,14 +47,9 @@ public async Task notify_state_change() timeStamp: DateTime.Now, state: Data.VNodeState.DiscoverLeader, isAlive: true, - internalTcpEndPoint: default, - internalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), - externalTcpEndPoint: default, - externalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), httpEndPoint: new DnsEndPoint("myhost", random()), advertiseHostToClientAs: "advertiseHostToClientAs", advertiseHttpPortToClientAs: random(), - advertiseTcpPortToClientAs: random(), lastCommitPosition: random(), writerCheckpoint: random(), chaserCheckpoint: random(), diff --git a/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs b/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs index 1193feeb54..ccaa036e64 100644 --- a/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Telemetry/TelemetryServiceTests.cs @@ -77,14 +77,9 @@ private static MemberInfo CreateMemberInfo(Guid instanceId, VNodeState state, bo timeStamp: DateTime.Now, state: state, isAlive: true, - internalTcpEndPoint: default, - internalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), - externalTcpEndPoint: default, - externalSecureTcpEndPoint: new DnsEndPoint("myhost", random()), httpEndPoint: new DnsEndPoint("myhost", random()), advertiseHostToClientAs: "advertiseHostToClientAs", advertiseHttpPortToClientAs: random(), - advertiseTcpPortToClientAs: random(), lastCommitPosition: random(), writerCheckpoint: random(), chaserCheckpoint: random(), diff --git a/src/EventStore.Core/Authorization/AuthorizationPolicies/LegacyPolicySelectorFactory.cs b/src/EventStore.Core/Authorization/AuthorizationPolicies/LegacyPolicySelectorFactory.cs index 951bed56fa..70c44939ae 100644 --- a/src/EventStore.Core/Authorization/AuthorizationPolicies/LegacyPolicySelectorFactory.cs +++ b/src/EventStore.Core/Authorization/AuthorizationPolicies/LegacyPolicySelectorFactory.cs @@ -62,7 +62,6 @@ public IPolicySelector Create(IPublisher publisher) addToPolicy(Operations.Node.Options); addToPolicy(Operations.Node.Statistics.Read); addToPolicy(Operations.Node.Statistics.Replication); - addToPolicy(Operations.Node.Statistics.Tcp); addToPolicy(Operations.Node.Statistics.Custom); policy.AddMatchAnyAssertion(Operations.Node.Information.Subsystems, Grant.Allow, OperationsOrAdmins); diff --git a/src/EventStore.Core/Cluster/ClientClusterInfo.cs b/src/EventStore.Core/Cluster/ClientClusterInfo.cs index 442abee0d0..3c0f6bc3ce 100644 --- a/src/EventStore.Core/Cluster/ClientClusterInfo.cs +++ b/src/EventStore.Core/Cluster/ClientClusterInfo.cs @@ -37,14 +37,6 @@ public class ClientMemberInfo public VNodeState State { get; set; } public bool IsAlive { get; set; } - public string InternalTcpIp { get; set; } - public int InternalTcpPort { get; set; } - public int InternalSecureTcpPort { get; set; } - - public string ExternalTcpIp { get; set; } - public int ExternalTcpPort { get; set; } - public int ExternalSecureTcpPort { get; set; } - public string InternalHttpEndPointIp { get; set; } public int InternalHttpEndPointPort { get; set; } @@ -76,12 +68,6 @@ public ClientMemberInfo(MemberInfo member) State = member.State; IsAlive = member.IsAlive; - InternalTcpIp = member.InternalTcpEndPoint is null - ? member.InternalSecureTcpEndPoint.GetHost() - : member.InternalTcpEndPoint.GetHost(); - InternalSecureTcpPort = member.InternalSecureTcpEndPoint?.GetPort() ?? 0; - InternalTcpPort = member.InternalTcpEndPoint?.GetPort() ?? 0; - InternalHttpEndPointIp = member.HttpEndPoint.GetHost(); InternalHttpEndPointPort = member.HttpEndPoint.GetPort(); @@ -92,18 +78,6 @@ public ClientMemberInfo(MemberInfo member) ? member.HttpEndPoint.GetPort() : member.AdvertiseHttpPortToClientAs; - ExternalTcpIp = string.IsNullOrEmpty(member.AdvertiseHostToClientAs) - ? member.ExternalSecureTcpEndPoint?.GetHost() ?? - member.ExternalTcpEndPoint?.GetHost() ?? member.HttpEndPoint.GetHost() - : member.AdvertiseHostToClientAs; - - ExternalTcpPort = member.AdvertiseTcpPortToClientAs == 0 - ? member.ExternalTcpEndPoint?.GetPort() ?? 0 - : member.AdvertiseTcpPortToClientAs; - ExternalSecureTcpPort = member.AdvertiseTcpPortToClientAs == 0 - ? member.ExternalSecureTcpEndPoint?.GetPort() ?? 0 - : member.AdvertiseTcpPortToClientAs; - LastCommitPosition = member.LastCommitPosition; WriterCheckpoint = member.WriterCheckpoint; ChaserCheckpoint = member.ChaserCheckpoint; @@ -122,8 +96,6 @@ public override string ToString() { return $"InstanceId: {InstanceId:B}, TimeStamp: {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}, State: {State}, IsAlive: {IsAlive}, " + - $"InternalTcpIp: {InternalTcpIp}, InternalTcpPort: {InternalTcpPort}, InternalSecureTcpPort: {InternalSecureTcpPort}, " + - $"ExternalTcpIp: {ExternalTcpIp}, ExternalTcpPort: {ExternalTcpPort}, ExternalSecureTcpPort: {ExternalSecureTcpPort}, " + $"InternalHttpEndPointIp: {InternalHttpEndPointIp}, InternalHttpEndPointPort: {InternalHttpEndPointPort}, " + $"HttpEndPointIp: {HttpEndPointIp}, HttpEndPointPort: {HttpEndPointPort}, " + $"LastCommitPosition: {LastCommitPosition}, WriterCheckpoint: {WriterCheckpoint}, ChaserCheckpoint: {ChaserCheckpoint}, " + @@ -133,4 +105,3 @@ public override string ToString() } } } - diff --git a/src/EventStore.Core/Cluster/ClusterInfo.cs b/src/EventStore.Core/Cluster/ClusterInfo.cs index 42bf18fb0e..460c3c0586 100644 --- a/src/EventStore.Core/Cluster/ClusterInfo.cs +++ b/src/EventStore.Core/Cluster/ClusterInfo.cs @@ -5,7 +5,6 @@ using EventStore.Client; using EventStore.Common.Utils; using EventStore.Core.Data; -using EventStore.Core.Messages; using EventStore.Core.Services.Transport.Grpc; namespace EventStore.Core.Cluster @@ -26,12 +25,6 @@ public ClusterInfo(IEnumerable members) .ToArray(); } - public ClusterInfo(ClusterInfoDto dto) - { - Members = dto.Members.Safe().Select(x => new MemberInfo(x)) - .OrderByDescending(x => x.HttpEndPoint, Comparer).ToArray(); - } - public override string ToString() { return string.Join("\n", Members.Select(s => s.ToString())); @@ -71,16 +64,8 @@ internal static ClusterInfo FromGrpcClusterInfo(EventStore.Cluster.ClusterInfo g new MemberInfo( Uuid.FromDto(x.InstanceId).ToGuid(), x.TimeStamp.FromTicksSinceEpoch(), (VNodeState)x.State, x.IsAlive, - !x.InternalTcpUsesTls ? new DnsEndPoint(x.InternalTcp.Address, (int)x.InternalTcp.Port).WithClusterDns(clusterDns) : null, - x.InternalTcpUsesTls ? new DnsEndPoint(x.InternalTcp.Address, (int)x.InternalTcp.Port).WithClusterDns(clusterDns) : null, - !x.ExternalTcpUsesTls && x.ExternalTcp != null - ? new DnsEndPoint(x.ExternalTcp.Address, (int)x.ExternalTcp.Port).WithClusterDns(clusterDns) - : null, - x.ExternalTcpUsesTls && x.ExternalTcp != null - ? new DnsEndPoint(x.ExternalTcp.Address, (int)x.ExternalTcp.Port).WithClusterDns(clusterDns) - : null, new DnsEndPoint(x.HttpEndPoint.Address, (int)x.HttpEndPoint.Port).WithClusterDns(clusterDns), - x.AdvertiseHostToClientAs, (int)x.AdvertiseHttpPortToClientAs, (int)x.AdvertiseTcpPortToClientAs, + x.AdvertiseHostToClientAs, (int)x.AdvertiseHttpPortToClientAs, x.LastCommitPosition, x.WriterCheckpoint, x.ChaserCheckpoint, x.EpochPosition, x.EpochNumber, Uuid.FromDto(x.EpochId).ToGuid(), x.NodePriority, x.IsReadOnlyReplica, x.EsVersion == String.Empty ? null : x.EsVersion @@ -99,23 +84,6 @@ internal static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(ClusterInfo clu HttpEndPoint = new EventStore.Cluster.EndPoint( x.HttpEndPoint.GetHost(), (uint)x.HttpEndPoint.GetPort()), - InternalTcp = x.InternalSecureTcpEndPoint != null ? - new EventStore.Cluster.EndPoint( - x.InternalSecureTcpEndPoint.GetHost(), - (uint)x.InternalSecureTcpEndPoint.GetPort()) : - new EventStore.Cluster.EndPoint( - x.InternalTcpEndPoint.GetHost(), - (uint)x.InternalTcpEndPoint.GetPort()), - InternalTcpUsesTls = x.InternalSecureTcpEndPoint != null, - ExternalTcp = x.ExternalSecureTcpEndPoint != null ? - new EventStore.Cluster.EndPoint( - x.ExternalSecureTcpEndPoint.GetHost(), - (uint)x.ExternalSecureTcpEndPoint.GetPort()) : - x.ExternalTcpEndPoint != null ? - new EventStore.Cluster.EndPoint( - x.ExternalTcpEndPoint.GetHost(), - (uint)x.ExternalTcpEndPoint.GetPort()) : null, - ExternalTcpUsesTls = x.ExternalSecureTcpEndPoint != null, LastCommitPosition = x.LastCommitPosition, WriterCheckpoint = x.WriterCheckpoint, ChaserCheckpoint = x.ChaserCheckpoint, @@ -126,7 +94,6 @@ internal static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(ClusterInfo clu IsReadOnlyReplica = x.IsReadOnlyReplica, AdvertiseHostToClientAs = x.AdvertiseHostToClientAs ?? "", AdvertiseHttpPortToClientAs = (uint)x.AdvertiseHttpPortToClientAs, - AdvertiseTcpPortToClientAs = (uint)x.AdvertiseTcpPortToClientAs, EsVersion = x.ESVersion ?? String.Empty }).ToArray(); var info = new EventStore.Cluster.ClusterInfo(); diff --git a/src/EventStore.Core/Cluster/MemberInfo.cs b/src/EventStore.Core/Cluster/MemberInfo.cs index f006448cc1..f0a22007f8 100644 --- a/src/EventStore.Core/Cluster/MemberInfo.cs +++ b/src/EventStore.Core/Cluster/MemberInfo.cs @@ -2,7 +2,6 @@ using System.Net; using EventStore.Common.Utils; using EventStore.Core.Data; -using EventStore.Core.Messages; using EventStore.Core.TransactionLog.LogRecords; namespace EventStore.Core.Cluster @@ -15,14 +14,9 @@ public class MemberInfo : IEquatable public readonly VNodeState State; public readonly bool IsAlive; - public readonly EndPoint InternalTcpEndPoint; - public readonly EndPoint InternalSecureTcpEndPoint; - public readonly EndPoint ExternalTcpEndPoint; - public readonly EndPoint ExternalSecureTcpEndPoint; public readonly EndPoint HttpEndPoint; public readonly string AdvertiseHostToClientAs; public readonly int AdvertiseHttpPortToClientAs; - public readonly int AdvertiseTcpPortToClientAs; public readonly long LastCommitPosition; public readonly long WriterCheckpoint; @@ -40,8 +34,7 @@ public static MemberInfo ForManager(Guid instanceId, DateTime timeStamp, bool is EndPoint httpEndPoint, string esVersion = VersionInfo.UnknownVersion) { return new MemberInfo(instanceId, timeStamp, VNodeState.Manager, isAlive, - httpEndPoint, null, httpEndPoint, null, - httpEndPoint, null, 0, 0, + httpEndPoint, null, 0, -1, -1, -1, -1, -1, Guid.Empty, 0, false, esVersion); } @@ -49,14 +42,9 @@ public static MemberInfo ForVNode(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, - EndPoint internalTcpEndPoint, - EndPoint internalSecureTcpEndPoint, - EndPoint externalTcpEndPoint, - EndPoint externalSecureTcpEndPoint, EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, - int advertiseTcpPortToClientAs, long lastCommitPosition, long writerCheckpoint, long chaserCheckpoint, @@ -72,9 +60,7 @@ public static MemberInfo ForVNode(Guid instanceId, } return new MemberInfo(instanceId, timeStamp, state, isAlive, - internalTcpEndPoint, internalSecureTcpEndPoint, - externalTcpEndPoint, externalSecureTcpEndPoint, - httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, advertiseTcpPortToClientAs, + httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, lastCommitPosition, writerCheckpoint, chaserCheckpoint, epochPosition, epochNumber, epochId, nodePriority, isReadOnlyReplica, esVersion); } @@ -83,14 +69,9 @@ public static MemberInfo Initial(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, - EndPoint internalTcpEndPoint, - EndPoint internalSecureTcpEndPoint, - EndPoint externalTcpEndPoint, - EndPoint externalSecureTcpEndPoint, EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, - int advertiseTcpPortToClientAs, int nodePriority, bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion) { @@ -100,20 +81,15 @@ public static MemberInfo Initial(Guid instanceId, } return new MemberInfo(instanceId, timeStamp, state, isAlive, - internalTcpEndPoint, internalSecureTcpEndPoint, - externalTcpEndPoint, externalSecureTcpEndPoint, - httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, advertiseTcpPortToClientAs, + httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, -1, -1, -1, -1, -1, Guid.Empty, nodePriority, isReadOnlyReplica, esVersion); } internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, - EndPoint internalTcpEndPoint, EndPoint internalSecureTcpEndPoint, - EndPoint externalTcpEndPoint, EndPoint externalSecureTcpEndPoint, - EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, int advertiseTcpPortToClientAs, + EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, long lastCommitPosition, long writerCheckpoint, long chaserCheckpoint, long epochPosition, int epochNumber, Guid epochId, int nodePriority, bool isReadOnlyReplica, string esVersion = null) { - Ensure.Equal(false, internalTcpEndPoint == null && internalSecureTcpEndPoint == null, "Both internal TCP endpoints are null"); Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); InstanceId = instanceId; @@ -122,14 +98,9 @@ internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool State = state; IsAlive = isAlive; - InternalTcpEndPoint = internalTcpEndPoint; - InternalSecureTcpEndPoint = internalSecureTcpEndPoint; - ExternalTcpEndPoint = externalTcpEndPoint; - ExternalSecureTcpEndPoint = externalSecureTcpEndPoint; HttpEndPoint = httpEndPoint; AdvertiseHostToClientAs = advertiseHostToClientAs; AdvertiseHttpPortToClientAs = advertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = advertiseTcpPortToClientAs; LastCommitPosition = lastCommitPosition; WriterCheckpoint = writerCheckpoint; @@ -145,42 +116,9 @@ internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool ESVersion = esVersion; } - internal MemberInfo(MemberInfoDto dto) - { - InstanceId = dto.InstanceId; - TimeStamp = dto.TimeStamp; - State = dto.State; - IsAlive = dto.IsAlive; - InternalTcpEndPoint = new DnsEndPoint(dto.InternalTcpIp, dto.InternalTcpPort); - InternalSecureTcpEndPoint = dto.InternalSecureTcpPort > 0 - ? new DnsEndPoint(dto.InternalTcpIp, dto.InternalSecureTcpPort) - : null; - ExternalTcpEndPoint = dto.ExternalTcpIp != null ? new DnsEndPoint(dto.ExternalTcpIp, dto.ExternalTcpPort) : null; - ExternalSecureTcpEndPoint = dto.ExternalTcpIp != null && dto.ExternalSecureTcpPort > 0 - ? new DnsEndPoint(dto.ExternalTcpIp, dto.ExternalSecureTcpPort) - : null; - HttpEndPoint = new DnsEndPoint(dto.HttpEndPointIp, dto.HttpEndPointPort); - AdvertiseHostToClientAs = dto.AdvertiseHostToClientAs; - AdvertiseHttpPortToClientAs = dto.AdvertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = dto.AdvertiseTcpPortToClientAs; - LastCommitPosition = dto.LastCommitPosition; - WriterCheckpoint = dto.WriterCheckpoint; - ChaserCheckpoint = dto.ChaserCheckpoint; - EpochPosition = dto.EpochPosition; - EpochNumber = dto.EpochNumber; - EpochId = dto.EpochId; - NodePriority = dto.NodePriority; - IsReadOnlyReplica = dto.IsReadOnlyReplica; - } - public bool Is(EndPoint endPoint) { - return endPoint != null - && HttpEndPoint.EndPointEquals(endPoint) - || (InternalTcpEndPoint != null && InternalTcpEndPoint.EndPointEquals(endPoint)) - || (InternalSecureTcpEndPoint != null && InternalSecureTcpEndPoint.EndPointEquals(endPoint)) - || (ExternalTcpEndPoint != null && ExternalTcpEndPoint.EndPointEquals(endPoint)) - || (ExternalSecureTcpEndPoint != null && ExternalSecureTcpEndPoint.EndPointEquals(endPoint)); + return endPoint != null && HttpEndPoint.EndPointEquals(endPoint); } public MemberInfo Updated(DateTime utcNow, @@ -196,14 +134,9 @@ public MemberInfo Updated(DateTime utcNow, utcNow, state ?? State, isAlive ?? IsAlive, - InternalTcpEndPoint, - InternalSecureTcpEndPoint, - ExternalTcpEndPoint, - ExternalSecureTcpEndPoint, HttpEndPoint, AdvertiseHostToClientAs, AdvertiseHttpPortToClientAs, - AdvertiseTcpPortToClientAs, lastCommitPosition ?? LastCommitPosition, writerCheckpoint ?? WriterCheckpoint, chaserCheckpoint ?? ChaserCheckpoint, @@ -224,11 +157,7 @@ public override string ToString() return $"Priority: {NodePriority} VND {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, " + - $"{(InternalTcpEndPoint == null ? "n/a" : InternalTcpEndPoint.ToString())}, " + - $"{(InternalSecureTcpEndPoint == null ? "n/a" : InternalSecureTcpEndPoint.ToString())}, " + - $"{(ExternalTcpEndPoint == null ? "n/a" : ExternalTcpEndPoint.ToString())}, " + - $"{(ExternalSecureTcpEndPoint == null ? "n/a" : ExternalSecureTcpEndPoint.ToString())}, " + - $"{HttpEndPoint}, (ADVERTISED: HTTP:{AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}, TCP:{AdvertiseHostToClientAs}:{AdvertiseTcpPortToClientAs}), " + + $"{HttpEndPoint}, (ADVERTISED: HTTP:{AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}), " + $"Version: {ESVersion}] " + $"{LastCommitPosition}/{WriterCheckpoint}/{ChaserCheckpoint}/E{EpochNumber}@{EpochPosition}:{EpochId:B} | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; } @@ -249,14 +178,9 @@ public bool Equals(MemberInfo other) return other.InstanceId == InstanceId && other.State == State && other.IsAlive == IsAlive - && Equals(other.InternalTcpEndPoint, InternalTcpEndPoint) - && Equals(other.InternalSecureTcpEndPoint, InternalSecureTcpEndPoint) - && Equals(other.ExternalTcpEndPoint, ExternalTcpEndPoint) - && Equals(other.ExternalSecureTcpEndPoint, ExternalSecureTcpEndPoint) && Equals(other.HttpEndPoint, HttpEndPoint) && other.AdvertiseHostToClientAs == AdvertiseHostToClientAs && other.AdvertiseHttpPortToClientAs == AdvertiseHttpPortToClientAs - && other.AdvertiseTcpPortToClientAs == AdvertiseTcpPortToClientAs && other.EpochPosition == EpochPosition && other.EpochNumber == EpochNumber && other.EpochId == EpochId @@ -292,16 +216,9 @@ public override int GetHashCode() int result = InstanceId.GetHashCode(); result = (result * 397) ^ State.GetHashCode(); result = (result * 397) ^ IsAlive.GetHashCode(); - result = (result * 397) ^ (InternalTcpEndPoint != null ? InternalTcpEndPoint.GetHashCode() : 0); - result = (result * 397) ^ - (InternalSecureTcpEndPoint != null ? InternalSecureTcpEndPoint.GetHashCode() : 0); - result = (result * 397) ^ (ExternalTcpEndPoint != null ? ExternalTcpEndPoint.GetHashCode() : 0); - result = (result * 397) ^ - (ExternalSecureTcpEndPoint != null ? ExternalSecureTcpEndPoint.GetHashCode() : 0); result = (result * 397) ^ HttpEndPoint.GetHashCode(); result = (result * 397) ^ (AdvertiseHostToClientAs != null ? AdvertiseHostToClientAs.GetHashCode() : 0); result = (result * 397) ^ AdvertiseHttpPortToClientAs.GetHashCode(); - result = (result * 397) ^ AdvertiseTcpPortToClientAs.GetHashCode(); result = (result * 397) ^ EpochPosition.GetHashCode(); result = (result * 397) ^ EpochNumber.GetHashCode(); result = (result * 397) ^ EpochId.GetHashCode(); diff --git a/src/EventStore.Core/ClusterVNode.cs b/src/EventStore.Core/ClusterVNode.cs index 327e52189a..1629df49e1 100644 --- a/src/EventStore.Core/ClusterVNode.cs +++ b/src/EventStore.Core/ClusterVNode.cs @@ -55,7 +55,6 @@ using EventStore.Core.Services.Transport.Http; using EventStore.Core.Services.Transport.Http.Authentication; using EventStore.Core.Services.Transport.Http.NodeHttpClientFactory; -using EventStore.Core.Services.Transport.Tcp; using EventStore.Core.Services.VNode; using EventStore.Core.Settings; using EventStore.Core.Synchronization; @@ -291,47 +290,11 @@ public ClusterVNode(ClusterVNodeOptions options, archiveOptions.Validate(); OptionsFormatter.LogConfig("Archive", archiveOptions); - var disableInternalTcpTls = options.Application.TlsDisabled(); - var disableExternalTcpTls = options.Application.TlsDisabled(); - var nodeTcpOptions = configuration.GetSection("EventStore:TcpPlugin").Get() ?? new(); - var enableExternalTcp = nodeTcpOptions.EnableExternalTcp; - var httpEndPoint = new IPEndPoint(options.Interface.NodeIp, options.Interface.NodePort); - var intTcp = disableInternalTcpTls - ? new IPEndPoint(options.Interface.ReplicationIp, - options.Interface.ReplicationPort) - : null; - var intSecIp = !disableInternalTcpTls - ? new IPEndPoint(options.Interface.ReplicationIp, - options.Interface.ReplicationPort) - : null; - - var extTcp = disableExternalTcpTls && enableExternalTcp - ? new IPEndPoint(options.Interface.NodeIp, - nodeTcpOptions.NodeTcpPort) - : null; - var extSecIp = !disableExternalTcpTls && enableExternalTcp - ? new IPEndPoint(options.Interface.NodeIp, - nodeTcpOptions.NodeTcpPort) - : null; - - var intTcpPortAdvertiseAs = disableInternalTcpTls ? options.Interface.ReplicationTcpPortAdvertiseAs : 0; - var intSecTcpPortAdvertiseAs = !disableInternalTcpTls ? options.Interface.ReplicationTcpPortAdvertiseAs : 0; - - var extTcpPortAdvertiseAs = - enableExternalTcp && disableExternalTcpTls && nodeTcpOptions.NodeTcpPortAdvertiseAs.HasValue - ? nodeTcpOptions.NodeTcpPortAdvertiseAs.Value! - : 0; - var extSecTcpPortAdvertiseAs = - enableExternalTcp && !disableExternalTcpTls && nodeTcpOptions.NodeTcpPortAdvertiseAs.HasValue - ? nodeTcpOptions.NodeTcpPortAdvertiseAs.Value! - : 0; - Log.Information("Quorum size set to {quorum}.", options.Cluster.QuorumSize); - NodeInfo = new VNodeInfo(instanceId.Value, debugIndex, intTcp, intSecIp, extTcp, extSecIp, - httpEndPoint, options.Cluster.ReadOnlyReplica); + NodeInfo = new VNodeInfo(instanceId.Value, debugIndex, httpEndPoint, options.Cluster.ReadOnlyReplica); var metricsConfiguration = MetricsConfiguration.Get(configuration); var trackers = new Trackers(); @@ -631,8 +594,6 @@ void StartSubsystems() TimeSpan.FromSeconds(options.Application.StatsPeriodSec), NodeInfo.HttpEndPoint, options.Database.StatsStorage, - NodeInfo.ExternalTcp, - NodeInfo.ExternalSecureTcp, statsHelper); _mainBus.Subscribe(monitoringQueue); @@ -646,7 +607,6 @@ void StartSubsystems() monitoringInnerBus.Subscribe(monitoring); monitoringInnerBus.Subscribe(monitoring); monitoringInnerBus.Subscribe(monitoring); - monitoringInnerBus.Subscribe(monitoring); _threadPoolBacklogMonitor = new ThreadPoolBacklogMonitor(_queueStatsManager, trackers.QueueTrackers); _threadPoolBacklogMonitor.Start(); @@ -914,64 +874,23 @@ void StartSubsystems() GossipAdvertiseInfo GetGossipAdvertiseInfo() { - IPAddress intIpAddress = options.Interface.ReplicationIp; - - IPAddress extIpAddress = options.Interface.NodeIp; + var nodeIpAddress = options.Interface.NodeIp; + var hostToAdvertise = options.Interface.NodeHostAdvertiseAs ?? nodeIpAddress.ToString(); - var intHostToAdvertise = options.Interface.ReplicationHostAdvertiseAs ?? intIpAddress.ToString(); - var extHostToAdvertise = options.Interface.NodeHostAdvertiseAs ?? extIpAddress.ToString(); - - if (intIpAddress.Equals(IPAddress.Any) || extIpAddress.Equals(IPAddress.Any)) + if (nodeIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) { IPAddress nonLoopbackAddress = IPFinder.GetNonLoopbackAddress(); - IPAddress addressToAdvertise = - options.Cluster.ClusterSize > 1 ? nonLoopbackAddress : IPAddress.Loopback; - - if (intIpAddress.Equals(IPAddress.Any) && options.Interface.ReplicationHostAdvertiseAs == null) - { - intHostToAdvertise = addressToAdvertise.ToString(); - } - - if (extIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) - { - extHostToAdvertise = addressToAdvertise.ToString(); - } + hostToAdvertise = + (options.Cluster.ClusterSize > 1 ? nonLoopbackAddress : IPAddress.Loopback).ToString(); } - var intTcpEndPoint = NodeInfo.InternalTcp == null - ? null - : new DnsEndPoint(intHostToAdvertise, intTcpPortAdvertiseAs > 0 - ? (options.Interface.ReplicationTcpPortAdvertiseAs) - : NodeInfo.InternalTcp.Port); - - var intSecureTcpEndPoint = NodeInfo.InternalSecureTcp == null - ? null - : new DnsEndPoint(intHostToAdvertise, intSecTcpPortAdvertiseAs > 0 - ? intSecTcpPortAdvertiseAs - : NodeInfo.InternalSecureTcp.Port); - - var extTcpEndPoint = NodeInfo.ExternalTcp == null - ? null - : new DnsEndPoint(extHostToAdvertise, extTcpPortAdvertiseAs > 0 - ? extTcpPortAdvertiseAs - : NodeInfo.ExternalTcp.Port); - - var extSecureTcpEndPoint = NodeInfo.ExternalSecureTcp == null - ? null - : new DnsEndPoint(extHostToAdvertise, extSecTcpPortAdvertiseAs > 0 - ? extSecTcpPortAdvertiseAs - : NodeInfo.ExternalSecureTcp.Port); - - var httpEndPoint = new DnsEndPoint(extHostToAdvertise, + var httpEndPoint = new DnsEndPoint(hostToAdvertise, options.Interface.NodePortAdvertiseAs > 0 ? options.Interface.NodePortAdvertiseAs : NodeInfo.HttpEndPoint.GetPort()); - return new GossipAdvertiseInfo(intTcpEndPoint, intSecureTcpEndPoint, extTcpEndPoint, - extSecureTcpEndPoint, httpEndPoint, options.Interface.ReplicationHostAdvertiseAs, - options.Interface.NodeHostAdvertiseAs, options.Interface.NodePortAdvertiseAs, - options.Interface.AdvertiseHostToClientAs, options.Interface.AdvertiseNodePortToClientAs, - nodeTcpOptions?.NodeTcpPortAdvertiseAs ?? 0); + return new GossipAdvertiseInfo(httpEndPoint, options.Interface.AdvertiseHostToClientAs, + options.Interface.AdvertiseNodePortToClientAs); } _httpService = new KestrelHttpService(_mainQueue, NodeInfo.HttpEndPoint); @@ -1015,15 +934,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() var authorizationGateway = new AuthorizationGateway(_authorizationProvider); - SubscribeWorkers(bus => - { - var tcpSendService = new TcpSendService(); - // ReSharper disable RedundantTypeArgumentsOfMethod - bus.Subscribe(tcpSendService); - // ReSharper restore RedundantTypeArgumentsOfMethod - }); - - var httpAuthenticationProviders = new List(); foreach (var authenticationScheme in _authenticationProvider.GetSupportedAuthenticationSchemes() ?? @@ -1098,7 +1008,7 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); - _mainBus.Subscribe(forwardingService); + _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); _mainBus.Subscribe(forwardingService); @@ -1145,7 +1055,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() subscriptionQueueSlowMessageThreshold); _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); - _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); _mainBus.Subscribe(subscrQueue); @@ -1160,7 +1069,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() virtualStreamReader); subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); - subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); subscrBus.Subscribe(subscription); @@ -1195,7 +1103,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() perSubscrBus.Subscribe(psubDispatcher); perSubscrBus.Subscribe(psubDispatcher); _mainBus.Subscribe(perSubscrQueue); - _mainBus.Subscribe(perSubscrQueue); _mainBus.Subscribe(perSubscrQueue); _mainBus.Subscribe(perSubscrQueue); _mainBus.Subscribe(perSubscrQueue); @@ -1229,7 +1136,6 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); - perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); perSubscrBus.Subscribe(persistentSubscription); @@ -1476,14 +1382,9 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() _mainBus.Subscribe(_timerService); var memberInfo = MemberInfo.Initial(NodeInfo.InstanceId, _timeProvider.UtcNow, VNodeState.Unknown, true, - GossipAdvertiseInfo.InternalTcp, - GossipAdvertiseInfo.InternalSecureTcp, - GossipAdvertiseInfo.ExternalTcp, - GossipAdvertiseInfo.ExternalSecureTcp, GossipAdvertiseInfo.HttpEndPoint, GossipAdvertiseInfo.AdvertiseHostToClientAs, GossipAdvertiseInfo.AdvertiseHttpPortToClientAs, - GossipAdvertiseInfo.AdvertiseTcpPortToClientAs, options.Cluster.NodePriority, options.Cluster.ReadOnlyReplica, VersionInfo.Version); // ELECTIONS TRACKER @@ -2263,5 +2164,5 @@ private void ReloadCertificates(ClusterVNodeOptions options) } public override string ToString() => - $"[{NodeInfo.InstanceId:B}, {NodeInfo.InternalTcp}, {NodeInfo.ExternalTcp}, {NodeInfo.HttpEndPoint}]"; + $"[{NodeInfo.InstanceId:B}, {NodeInfo.HttpEndPoint}]"; } diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs index f4d51719a8..39b8b7a729 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs @@ -178,10 +178,10 @@ public record ApplicationOptions [Description("The maximum size of appends, in bytes. May not exceed 16MB.")] public int MaxAppendSize { get; init; } = 1_024 * 1_024; - [Description("Disable Authentication, Authorization and TLS on all TCP/HTTP interfaces.")] + [Description("Disable authentication, authorization, and TLS on the shared HTTP/gRPC listener.")] public bool Insecure { get; init; } = false; - [Description("Disable TLS on all TCP/HTTP interfaces while keeping authentication and authorization enabled.")] + [Description("Disable TLS on the shared HTTP/gRPC listener while keeping authentication and authorization enabled.")] public bool DisableTls { get; init; } = false; public bool AuthDisabled() => Insecure; @@ -620,24 +620,15 @@ public record GrpcOptions [Description("Interface Options")] public record InterfaceOptions { - [Description("The IP Address used by internal replication between nodes in the cluster.")] - public IPAddress ReplicationIp { get; init; } = IPAddress.Loopback; - [Description("The IP Address for the node.")] public IPAddress NodeIp { get; init; } = IPAddress.Loopback; [Description("The Port to run the HTTP server on.")] public int NodePort { get; init; } = 2113; - [Description("The TCP port used by internal replication between nodes in the cluster.")] - public int ReplicationPort { get; init; } = 1112; - [Description("Advertise the Node's host name to other nodes and external clients as.")] public string? NodeHostAdvertiseAs { get; init; } = null; - [Description("Advertise the Replication host name to other nodes in the cluster as.")] - public string? ReplicationHostAdvertiseAs { get; init; } = null; - [Description("Advertise Host in Gossip to Client As.")] public string? AdvertiseHostToClientAs { get; init; } = null; @@ -647,26 +638,9 @@ public record InterfaceOptions [Description("Advertise Http Port As.")] public int NodePortAdvertiseAs { get; init; } = 0; - [Description("Advertise Replication Tcp Port As.")] - public int ReplicationTcpPortAdvertiseAs { get; init; } = 0; - - [Description("Heartbeat timeout for Replication TCP sockets."), - Unit("ms")] - public int ReplicationHeartbeatTimeout { get; init; } = 700; - - [Description("Heartbeat interval for Replication TCP sockets."), - Unit("ms")] - public int ReplicationHeartbeatInterval { get; init; } = 700; - [Description("Whether to allow local connections via a UNIX domain socket.")] public bool EnableUnixSocket { get; init; } = false; - [Description("The maximum number of pending send bytes allowed before a connection is closed.")] - public int ConnectionPendingSendBytesThreshold { get; init; } = 10 * 1_024 * 1_024; - - [Description("The maximum number of pending connection operations allowed before a connection is closed.")] - public int ConnectionQueueSizeThreshold { get; init; } = 50_000; - [Description("Disables the admin ui on the HTTP endpoint.")] public bool DisableAdminUi { get; init; } = false; diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs index 562cbcd82a..b7555bf60c 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs @@ -88,29 +88,6 @@ options with } }; - /// - /// Sets the external tcp endpoint to the specified value - /// - /// The - /// The external endpoint to use - /// A with the options set - public static ClusterVNodeOptions WithExternalTcpOn( - this ClusterVNodeOptions options, IPEndPoint endPoint) => - options with { Interface = options.Interface with { NodeIp = endPoint.Address, } }; - - /// - /// Sets the internal tcp endpoint to the specified value - /// - /// The - /// The internal endpoint to use - /// A with the options set - public static ClusterVNodeOptions WithReplicationEndpointOn( - this ClusterVNodeOptions options, IPEndPoint endPoint) => - options with - { - Interface = options.Interface with { ReplicationIp = endPoint.Address, ReplicationPort = endPoint.Port } - }; - /// /// Sets the http endpoint to the specified value /// @@ -131,23 +108,6 @@ public static ClusterVNodeOptions AdvertiseExternalHostAs(this ClusterVNodeOptions options, EndPoint endPoint) => options with { Interface = options.Interface with { NodeHostAdvertiseAs = endPoint.GetHost(), } }; - /// - /// Sets up the Internal Host that would be advertised - /// - /// The - /// The advertised host - /// A with the options set - public static ClusterVNodeOptions - AdvertiseInternalHostAs(this ClusterVNodeOptions options, EndPoint endPoint) => - options with - { - Interface = options.Interface with - { - ReplicationHostAdvertiseAs = endPoint.GetHost(), - ReplicationTcpPortAdvertiseAs = endPoint.GetPort() - } - }; - /// /// /// The diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs index 41c7b4b39f..cc0ad32d0a 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs @@ -27,11 +27,6 @@ public static void Validate(ClusterVNodeOptions options) throw new ArgumentNullException(nameof(options.Interface.NodeIp)); } - if (options.Interface.ReplicationIp == null) - { - throw new ArgumentNullException(nameof(options.Interface.ReplicationIp)); - } - if (options.Cluster.ClusterSize <= 0) { throw new ArgumentOutOfRangeException(nameof(options.Cluster.ClusterSize), options.Cluster.ClusterSize, diff --git a/src/EventStore.Core/Data/GossipAdvertiseInfo.cs b/src/EventStore.Core/Data/GossipAdvertiseInfo.cs index 4c2c09cd25..d60f980c21 100644 --- a/src/EventStore.Core/Data/GossipAdvertiseInfo.cs +++ b/src/EventStore.Core/Data/GossipAdvertiseInfo.cs @@ -5,47 +5,22 @@ namespace EventStore.Core.Data { public class GossipAdvertiseInfo { - public DnsEndPoint InternalTcp { get; } - public DnsEndPoint InternalSecureTcp { get; } - public DnsEndPoint ExternalTcp { get; } - public DnsEndPoint ExternalSecureTcp { get; } public DnsEndPoint HttpEndPoint { get; } - public string AdvertiseInternalHostAs { get; } - public string AdvertiseExternalHostAs { get; } - public int AdvertiseHttpPortAs { get; } public string AdvertiseHostToClientAs { get; } public int AdvertiseHttpPortToClientAs { get; } - public int AdvertiseTcpPortToClientAs { get; } - public GossipAdvertiseInfo(DnsEndPoint internalTcp, DnsEndPoint internalSecureTcp, - DnsEndPoint externalTcp, DnsEndPoint externalSecureTcp, - DnsEndPoint httpEndPoint, - string advertiseInternalHostAs, string advertiseExternalHostAs, int advertiseHttpPortAs, - string advertiseHostToClientAs, int advertiseHttpPortToClientAs, int advertiseTcpPortToClientAs) + public GossipAdvertiseInfo(DnsEndPoint httpEndPoint, + string advertiseHostToClientAs, int advertiseHttpPortToClientAs) { - Ensure.Equal(false, internalTcp == null && internalSecureTcp == null, "Both internal TCP endpoints are null"); - - InternalTcp = internalTcp; - InternalSecureTcp = internalSecureTcp; - ExternalTcp = externalTcp; - ExternalSecureTcp = externalSecureTcp; + Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); HttpEndPoint = httpEndPoint; - AdvertiseInternalHostAs = advertiseInternalHostAs; - AdvertiseExternalHostAs = advertiseExternalHostAs; - AdvertiseHttpPortAs = advertiseHttpPortAs; AdvertiseHostToClientAs = advertiseHostToClientAs; AdvertiseHttpPortToClientAs = advertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = advertiseTcpPortToClientAs; } public override string ToString() { - return string.Format( - $"IntTcp: {InternalTcp}, IntSecureTcp: {InternalSecureTcp}\n" + - $"ExtTcp: {ExternalTcp}, ExtSecureTcp: {ExternalSecureTcp}\n" + - $"Http: {HttpEndPoint}, HttpAdvertiseAs: {AdvertiseExternalHostAs}:{AdvertiseHttpPortAs},\n" + - $"HttpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs},\n" + - $"TcpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseTcpPortToClientAs}"); + return $"Http: {HttpEndPoint}, HttpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}"; } } } diff --git a/src/EventStore.Core/Data/VNodeInfo.cs b/src/EventStore.Core/Data/VNodeInfo.cs index a217f5bbd5..6d1407d038 100644 --- a/src/EventStore.Core/Data/VNodeInfo.cs +++ b/src/EventStore.Core/Data/VNodeInfo.cs @@ -8,55 +8,29 @@ public class VNodeInfo { public readonly Guid InstanceId; public readonly int DebugIndex; - public readonly IPEndPoint InternalTcp; - public readonly IPEndPoint InternalSecureTcp; - public readonly IPEndPoint ExternalTcp; - public readonly IPEndPoint ExternalSecureTcp; public readonly EndPoint HttpEndPoint; public readonly bool IsReadOnlyReplica; - public VNodeInfo(Guid instanceId, int debugIndex, - IPEndPoint internalTcp, IPEndPoint internalSecureTcp, - IPEndPoint externalTcp, IPEndPoint externalSecureTcp, - EndPoint httpEndPoint, + public VNodeInfo(Guid instanceId, int debugIndex, EndPoint httpEndPoint, bool isReadOnlyReplica) { Ensure.NotEmptyGuid(instanceId, "instanceId"); - Ensure.Equal(false, internalTcp == null && internalSecureTcp == null, "Both internal TCP endpoints are null"); Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); DebugIndex = debugIndex; InstanceId = instanceId; - InternalTcp = internalTcp; - InternalSecureTcp = internalSecureTcp; - ExternalTcp = externalTcp; - ExternalSecureTcp = externalSecureTcp; HttpEndPoint = httpEndPoint; IsReadOnlyReplica = isReadOnlyReplica; } public bool Is(EndPoint endPoint) { - return endPoint != null - && HttpEndPoint.Equals(endPoint) - || (InternalTcp != null && InternalTcp.Equals(endPoint)) - || (InternalSecureTcp != null && InternalSecureTcp.Equals(endPoint)) - || (ExternalTcp != null && ExternalTcp.Equals(endPoint)) - || (ExternalSecureTcp != null && ExternalSecureTcp.Equals(endPoint)); + return endPoint != null && HttpEndPoint.Equals(endPoint); } public override string ToString() { - return string.Format("InstanceId: {0:B}, InternalTcp: {1}, InternalSecureTcp: {2}, " + - "ExternalTcp: {3}, ExternalSecureTcp: {4}, HttpEndPoint: {5}," + - "IsReadOnlyReplica: {6}", - InstanceId, - InternalTcp, - InternalSecureTcp, - ExternalTcp, - ExternalSecureTcp, - HttpEndPoint, - IsReadOnlyReplica); + return $"InstanceId: {InstanceId:B}, HttpEndPoint: {HttpEndPoint}, IsReadOnlyReplica: {IsReadOnlyReplica}"; } } } diff --git a/src/EventStore.Core/EventStore.Core.csproj b/src/EventStore.Core/EventStore.Core.csproj index fbe5f47564..5c3b635cda 100644 --- a/src/EventStore.Core/EventStore.Core.csproj +++ b/src/EventStore.Core/EventStore.Core.csproj @@ -43,15 +43,11 @@ - - - Services\Transport\Tcp\protos\ClientMessageDtos.proto - Services\Transport\Grpc\Protos\code.proto @@ -98,7 +94,4 @@ Services\Transport\Grpc\Protos\forwarding.proto - - - diff --git a/src/EventStore.Core/Helpers/IAsyncMessageFramer.cs b/src/EventStore.Core/Helpers/IAsyncMessageFramer.cs new file mode 100644 index 0000000000..23820cb0f5 --- /dev/null +++ b/src/EventStore.Core/Helpers/IAsyncMessageFramer.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace EventStore.Core.Helpers; + +public interface IAsyncMessageFramer +{ + bool HasData { get; } + IEnumerable> FrameData(ArraySegment data); + ValueTask UnFrameData(IEnumerable> data, CancellationToken token); + ValueTask UnFrameData(ArraySegment data, CancellationToken token); + void RegisterMessageArrivedCallback(Func handler); + void Reset(); +} diff --git a/src/EventStore.Core/Helpers/LengthPrefixSuffixFramer.cs b/src/EventStore.Core/Helpers/LengthPrefixSuffixFramer.cs index 64f7ea7dcd..afd89966de 100644 --- a/src/EventStore.Core/Helpers/LengthPrefixSuffixFramer.cs +++ b/src/EventStore.Core/Helpers/LengthPrefixSuffixFramer.cs @@ -7,7 +7,6 @@ using System.Threading.Tasks; using EventStore.Common.Utils; using EventStore.Core.TransactionLog.Chunks; -using EventStore.Transport.Tcp.Framing; using ILogger = Serilog.ILogger; namespace EventStore.Core.Helpers; @@ -85,7 +84,7 @@ private async ValueTask Parse(ArraySegment bytes, CancellationToken token) if (_packageLength <= 0 || _packageLength > _maxPackageSize) { Log.Error("FRAMING ERROR! Data:\n{data}", Helper.FormatBinaryDump(bytes)); - throw new PackageFramingException(string.Format( + throw new MessageFramingException(string.Format( "Package size is out of bounds: {0} (max: {1}).", _packageLength, _maxPackageSize)); } diff --git a/src/EventStore.Core/Helpers/MessageFramingException.cs b/src/EventStore.Core/Helpers/MessageFramingException.cs new file mode 100644 index 0000000000..f8f10f9f03 --- /dev/null +++ b/src/EventStore.Core/Helpers/MessageFramingException.cs @@ -0,0 +1,10 @@ +using System; + +namespace EventStore.Core.Helpers; + +public sealed class MessageFramingException : Exception +{ + public MessageFramingException(string message) : base(message) + { + } +} diff --git a/src/EventStore.Core/Messages/ClientMessage.cs b/src/EventStore.Core/Messages/ClientMessage.cs index 905652eca3..a881f1451e 100644 --- a/src/EventStore.Core/Messages/ClientMessage.cs +++ b/src/EventStore.Core/Messages/ClientMessage.cs @@ -203,20 +203,29 @@ public enum NotHandledReason public class LeaderInfo { - public LeaderInfo(EndPoint externalTcp, bool isSecure, EndPoint http) + public LeaderInfo(EndPoint http) { - ExternalTcp = externalTcp; - IsSecure = isSecure; Http = http; } - public bool IsSecure { get; } - public EndPoint ExternalTcp { get; } public EndPoint Http { get; } } } } + [DerivedMessage(CoreMessage.Client)] + public partial class NotAuthenticated : Message + { + public readonly Guid CorrelationId; + public readonly string Reason; + + public NotAuthenticated(Guid correlationId, string reason) + { + CorrelationId = correlationId; + Reason = reason; + } + } + [DerivedMessage(CoreMessage.Client)] public partial class WriteEvents : WriteRequestMessage { diff --git a/src/EventStore.Core/Messages/ClusterInfoDto.cs b/src/EventStore.Core/Messages/ClusterInfoDto.cs deleted file mode 100644 index 68ab95c1e1..0000000000 --- a/src/EventStore.Core/Messages/ClusterInfoDto.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Linq; -using System.Net; -using EventStore.Common.Utils; -using EventStore.Core.Cluster; - -namespace EventStore.Core.Messages -{ - public class ClusterInfoDto - { - public MemberInfoDto[] Members { get; set; } - public string ServerIp { get; set; } - public int ServerPort { get; set; } - - public ClusterInfoDto() - { - } - - public ClusterInfoDto(ClusterInfo clusterInfo, EndPoint serverEndPoint) - { - Members = clusterInfo.Members.Select(x => new MemberInfoDto(x)).ToArray(); - ServerIp = serverEndPoint.GetHost(); - ServerPort = serverEndPoint.GetPort(); - } - - public override string ToString() - { - return string.Format("Server: {0}:{1}, Members: [{2}]", - ServerIp, ServerPort, - Members != null ? string.Join(",", Members.Select(m => m.ToString())) : "null"); - } - } -} diff --git a/src/EventStore.Core/Messages/MemberInfoDto.cs b/src/EventStore.Core/Messages/MemberInfoDto.cs deleted file mode 100644 index eb491f64d6..0000000000 --- a/src/EventStore.Core/Messages/MemberInfoDto.cs +++ /dev/null @@ -1,95 +0,0 @@ -using System; -using EventStore.Common.Utils; -using EventStore.Core.Cluster; -using EventStore.Core.Data; - -namespace EventStore.Core.Messages -{ - public class MemberInfoDto - { - public Guid InstanceId { get; set; } - - public DateTime TimeStamp { get; set; } - public VNodeState State { get; set; } - public bool IsAlive { get; set; } - - public string InternalTcpIp { get; set; } - public int InternalTcpPort { get; set; } - public int InternalSecureTcpPort { get; set; } - - public string ExternalTcpIp { get; set; } - public int ExternalTcpPort { get; set; } - public int ExternalSecureTcpPort { get; set; } - - public string HttpEndPointIp { get; set; } - public int HttpEndPointPort { get; set; } - public string AdvertiseHostToClientAs { get; set; } - public int AdvertiseHttpPortToClientAs { get; set; } - public int AdvertiseTcpPortToClientAs { get; set; } - - public long LastCommitPosition { get; set; } - public long WriterCheckpoint { get; set; } - public long ChaserCheckpoint { get; set; } - - public long EpochPosition { get; set; } - public int EpochNumber { get; set; } - public Guid EpochId { get; set; } - - public int NodePriority { get; set; } - public bool IsReadOnlyReplica { get; set; } - - public MemberInfoDto() - { - } - - public MemberInfoDto(MemberInfo member) - { - InstanceId = member.InstanceId; - - TimeStamp = member.TimeStamp; - State = member.State; - IsAlive = member.IsAlive; - - InternalTcpIp = member.InternalTcpEndPoint?.GetHost() ?? member.InternalSecureTcpEndPoint?.GetHost(); - InternalTcpPort = member.InternalTcpEndPoint == null ? 0 : member.InternalTcpEndPoint.GetPort(); - InternalSecureTcpPort = - member.InternalSecureTcpEndPoint == null ? 0 : member.InternalSecureTcpEndPoint.GetPort(); - - ExternalTcpIp = member.ExternalTcpEndPoint?.GetHost() ?? member.ExternalSecureTcpEndPoint?.GetHost(); - ExternalTcpPort = member.ExternalTcpEndPoint == null ? 0 : member.ExternalTcpEndPoint.GetPort(); - ExternalSecureTcpPort = - member.ExternalSecureTcpEndPoint == null ? 0 : member.ExternalSecureTcpEndPoint.GetPort(); - - HttpEndPointIp = member.HttpEndPoint.GetHost(); - HttpEndPointPort = member.HttpEndPoint.GetPort(); - AdvertiseHostToClientAs = member.AdvertiseHostToClientAs; - AdvertiseHttpPortToClientAs = member.AdvertiseHttpPortToClientAs; - AdvertiseTcpPortToClientAs = member.AdvertiseTcpPortToClientAs; - - LastCommitPosition = member.LastCommitPosition; - WriterCheckpoint = member.WriterCheckpoint; - ChaserCheckpoint = member.ChaserCheckpoint; - - EpochPosition = member.EpochPosition; - EpochNumber = member.EpochNumber; - EpochId = member.EpochId; - - NodePriority = member.NodePriority; - IsReadOnlyReplica = member.IsReadOnlyReplica; - } - - public override string ToString() - { - return - $"InstanceId: {InstanceId:B}, TimeStamp: {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}, State: {State}, IsAlive: {IsAlive}, " + - $"InternalTcpIp: {InternalTcpIp}, InternalTcpPort: {InternalTcpPort}, InternalSecureTcpPort: {InternalSecureTcpPort}, " + - $"ExternalTcpIp: {ExternalTcpIp}, ExternalTcpPort: {ExternalTcpPort}, ExternalSecureTcpPort: {ExternalSecureTcpPort}, " + - $"HttpEndPointIp: {HttpEndPointIp}, HttpEndPointPort: {HttpEndPointPort}, " + - $"{nameof(AdvertiseHostToClientAs)}: {AdvertiseHostToClientAs}, {nameof(AdvertiseHttpPortToClientAs)}: {AdvertiseHttpPortToClientAs}, " + - $"{nameof(AdvertiseTcpPortToClientAs)}: {AdvertiseTcpPortToClientAs}, " + - $"LastCommitPosition: {LastCommitPosition}, WriterCheckpoint: {WriterCheckpoint}, ChaserCheckpoint: {ChaserCheckpoint}, " + - $"EpochPosition: {EpochPosition}, EpochNumber: {EpochNumber}, EpochId: {EpochId:B}, NodePriority: {NodePriority}, " + - $"IsReadOnlyReplica: {IsReadOnlyReplica}"; - } - } -} diff --git a/src/EventStore.Core/Messages/MonitoringMessage.cs b/src/EventStore.Core/Messages/MonitoringMessage.cs index bde535e546..5616e573cf 100644 --- a/src/EventStore.Core/Messages/MonitoringMessage.cs +++ b/src/EventStore.Core/Messages/MonitoringMessage.cs @@ -205,44 +205,6 @@ public GetFreshStatsCompleted(bool success, Dictionary stats) } } - [DerivedMessage(CoreMessage.Monitoring)] - public partial class GetFreshTcpConnectionStats : Message - { - public readonly IEnvelope Envelope; - - public GetFreshTcpConnectionStats(IEnvelope envelope) - { - Ensure.NotNull(envelope, "envelope"); - - Envelope = envelope; - } - } - - [DerivedMessage(CoreMessage.Monitoring)] - public partial class GetFreshTcpConnectionStatsCompleted : Message - { - public readonly List ConnectionStats; - - public GetFreshTcpConnectionStatsCompleted(List connectionStats) - { - ConnectionStats = connectionStats; - } - } - - public class TcpConnectionStats - { - public string RemoteEndPoint { get; set; } - public string LocalEndPoint { get; set; } - public string ClientConnectionName { get; set; } - public Guid ConnectionId { get; set; } - public long TotalBytesSent { get; set; } - public long TotalBytesReceived { get; set; } - public int PendingSendBytes { get; set; } - public int PendingReceivedBytes { get; set; } - public bool IsExternalConnection { get; set; } - public bool IsSslConnection { get; set; } - } - [DerivedMessage(CoreMessage.Monitoring)] public partial class InternalStatsRequest : Message { diff --git a/src/EventStore.Core/Messages/TcpClientMessageDto.cs b/src/EventStore.Core/Messages/TcpClientMessageDto.cs deleted file mode 100755 index b62f9cf1bd..0000000000 --- a/src/EventStore.Core/Messages/TcpClientMessageDto.cs +++ /dev/null @@ -1,670 +0,0 @@ -using System; -using System.Net; -using EventStore.Common.Utils; -using EventStore.Core.Messages; -using Google.Protobuf; - -// ReSharper disable once CheckNamespace -namespace EventStore.Client.Messages -{ - partial class NewEvent - { - public NewEvent(byte[] eventId, string eventType, int dataContentType, int metadataContentType, byte[] data, byte[] metadata) - { - - EventId = ByteString.CopyFrom(eventId); - EventType = eventType; - DataContentType = dataContentType; - MetadataContentType = metadataContentType; - Data = ByteString.CopyFrom(data); - Metadata = ByteString.CopyFrom(metadata); - } - } - - partial class EventRecord - { - public EventRecord(string eventStreamId, long eventNumber, byte[] eventId, string eventType, int dataContentType, int metadataContentType, byte[] data, byte[] metadata, long created, long createdEpoch) - { - AssignValues(eventStreamId, eventNumber, eventId, eventType, dataContentType, metadataContentType, data, metadata, created, createdEpoch); - } - - private void AssignValues(string eventStreamId, long eventNumber, byte[] eventId, string eventType, int dataContentType, - int metadataContentType, byte[] data, byte[] metadata, long created, long createdEpoch) - { - EventStreamId = eventStreamId; - EventNumber = eventNumber; - EventId = ByteString.CopyFrom(eventId); - EventType = eventType; - DataContentType = dataContentType; - MetadataContentType = metadataContentType; - Data = ByteString.CopyFrom(data); - Metadata = ByteString.CopyFrom(metadata); - Created = created; - CreatedEpoch = createdEpoch; - } - - public EventRecord(Core.Data.EventRecord eventRecord) - { - if (eventRecord == null) - { - return; - } - - AssignValues( - eventRecord.EventStreamId, - eventRecord.EventNumber, - eventRecord.EventId.ToByteArray(), - eventRecord.EventType, - eventRecord.IsJson ? 1 : 0, - eventRecord.IsJson ? 1 : 0, - eventRecord.Data.ToArray(), - eventRecord.Metadata.ToArray(), - eventRecord.TimeStamp.ToBinary(), - (long)(eventRecord.TimeStamp - new DateTime(1970, 1, 1)).TotalMilliseconds - ); - } - - public EventRecord(Core.Data.EventRecord eventRecord, long eventNumber) - { - if (eventRecord == null) - { - return; - } - - AssignValues( - eventRecord.EventStreamId, - eventNumber, - eventRecord.EventId.ToByteArray(), - eventRecord.EventType, - eventRecord.IsJson ? 1 : 0, - eventRecord.IsJson ? 1 : 0, - eventRecord.Data.ToArray(), - eventRecord.Metadata.ToArray(), - eventRecord.TimeStamp.ToBinary(), - (long)(eventRecord.TimeStamp - new DateTime(1970, 1, 1)).TotalMilliseconds - ); - } - } - - partial class ResolvedIndexedEvent - { - public ResolvedIndexedEvent(EventRecord @event, EventRecord link) - { - Event = @event; - Link = link; - } - - public ResolvedIndexedEvent(Core.Data.EventRecord @event, Core.Data.EventRecord link) - { - if (@event != null) - { - Event = new EventRecord(@event); - } - - if (link != null) - { - Link = new EventRecord(link); - } - } - } - - partial class ResolvedEvent - { - public ResolvedEvent(EventRecord @event, EventRecord link, long commitPosition, long preparePosition) - { - if (@event != null) - { - Event = @event; - } - - if (link != null) - { - Link = link; - } - - CommitPosition = commitPosition; - PreparePosition = preparePosition; - } - - public ResolvedEvent(Core.Data.ResolvedEvent pair) - : this(pair.Event != null ? new EventRecord(pair.Event) : null, - pair.Link != null ? new EventRecord(pair.Link) : null, - pair.OriginalPosition?.CommitPosition ?? -1, - pair.OriginalPosition?.PreparePosition ?? -1) - { - } - } - - partial class WriteEvents - { - public WriteEvents(string eventStreamId, long expectedVersion, NewEvent[] events, bool requireLeader) - { - EventStreamId = eventStreamId; - ExpectedVersion = expectedVersion; - Events.AddRange(events); - RequireLeader = requireLeader; - } - } - - partial class WriteEventsCompleted - { - public WriteEventsCompleted(OperationResult result, string message, long firstEventNumber, long lastEventNumber, long preparePosition, long commitPosition, long currentVersion) - { - Result = result; - if (message != null) - { - Message = message; - } - - FirstEventNumber = firstEventNumber; - LastEventNumber = lastEventNumber; - PreparePosition = preparePosition; - CommitPosition = commitPosition; - CurrentVersion = currentVersion; - } - } - - partial class DeleteStream - { - public DeleteStream(string eventStreamId, long expectedVersion, bool requireLeader, bool hardDelete) - { - EventStreamId = eventStreamId; - ExpectedVersion = expectedVersion; - RequireLeader = requireLeader; - HardDelete = hardDelete; - } - } - - partial class DeleteStreamCompleted - { - public DeleteStreamCompleted(OperationResult result, string message, long currentVersion, long preparePosition, - long commitPosition) - { - Result = result; - if (!string.IsNullOrEmpty(message)) - { - Message = message; - } - - CurrentVersion = currentVersion; - PreparePosition = preparePosition; - CommitPosition = commitPosition; - } - } - - partial class TransactionStart - { - public TransactionStart(string eventStreamId, long expectedVersion, bool requireLeader) - { - EventStreamId = eventStreamId; - ExpectedVersion = expectedVersion; - RequireLeader = requireLeader; - } - } - - partial class TransactionStartCompleted - { - public TransactionStartCompleted(long transactionId, OperationResult result, string message) - { - TransactionId = transactionId; - Result = result; - if (message != null) - { - Message = message; - } - } - } - - partial class TransactionWrite - { - public TransactionWrite(long transactionId, NewEvent[] events, bool requireLeader) - { - TransactionId = transactionId; - Events.AddRange(events); - RequireLeader = requireLeader; - } - } - - partial class TransactionWriteCompleted - { - public TransactionWriteCompleted(long transactionId, OperationResult result, string message) - { - TransactionId = transactionId; - Result = result; - if (message != null) - { - Message = message; - } - } - } - - partial class TransactionCommit - { - public TransactionCommit(long transactionId, bool requireLeader) - { - TransactionId = transactionId; - RequireLeader = requireLeader; - } - } - - partial class TransactionCommitCompleted - { - public TransactionCommitCompleted(long transactionId, OperationResult result, string message, long firstEventNumber, long lastEventNumber, long preparePosition, long commitPosition) - { - TransactionId = transactionId; - Result = result; - if (message != null) - { - Message = message; - } - - FirstEventNumber = firstEventNumber; - LastEventNumber = lastEventNumber; - PreparePosition = preparePosition; - CommitPosition = commitPosition; - } - } - - partial class ReadEvent - { - public ReadEvent(string eventStreamId, long eventNumber, bool resolveLinkTos, bool requireLeader) - { - EventStreamId = eventStreamId; - EventNumber = eventNumber; - ResolveLinkTos = resolveLinkTos; - RequireLeader = requireLeader; - } - } - - partial class ReadEventCompleted - { - public ReadEventCompleted(ReadEventCompleted.Types.ReadEventResult result, ResolvedIndexedEvent @event, string error) - { - Result = result; - if (@event != null) - { - Event = @event; - } - - if (error != null) - { - Error = error; - } - } - } - - partial class ReadStreamEvents - { - public ReadStreamEvents(string eventStreamId, long fromEventNumber, int maxCount, bool resolveLinkTos, bool requireLeader) - { - EventStreamId = eventStreamId; - FromEventNumber = fromEventNumber; - MaxCount = maxCount; - ResolveLinkTos = resolveLinkTos; - RequireLeader = requireLeader; - } - } - - partial class ReadStreamEventsCompleted - { - public ReadStreamEventsCompleted(ResolvedIndexedEvent[] events, ReadStreamEventsCompleted.Types.ReadStreamResult result, long nextEventNumber, long lastEventNumber, bool isEndOfStream, long lastCommitPosition, string error) - { - Events.AddRange(events); - Result = result; - NextEventNumber = nextEventNumber; - LastEventNumber = lastEventNumber; - IsEndOfStream = isEndOfStream; - LastCommitPosition = lastCommitPosition; - if (error != null) - { - Error = error; - } - } - } - - partial class ReadAllEvents - { - public ReadAllEvents(long commitPosition, long preparePosition, int maxCount, bool resolveLinkTos, bool requireLeader) - { - CommitPosition = commitPosition; - PreparePosition = preparePosition; - MaxCount = maxCount; - ResolveLinkTos = resolveLinkTos; - RequireLeader = requireLeader; - } - } - - partial class ReadAllEventsCompleted - { - public ReadAllEventsCompleted(long commitPosition, long preparePosition, ResolvedEvent[] events, long nextCommitPosition, long nextPreparePosition, ReadAllEventsCompleted.Types.ReadAllResult result, string error) - { - CommitPosition = commitPosition; - PreparePosition = preparePosition; - Events.AddRange(events); - NextCommitPosition = nextCommitPosition; - NextPreparePosition = nextPreparePosition; - Result = result; - if (error != null) - { - Error = error; - } - } - } - - partial class Filter - { - public Filter(Filter.Types.FilterContext context, Filter.Types.FilterType type, string[] data) - { - Context = context; - Type = type; - Data.AddRange(data); - } - } - - partial class FilteredReadAllEvents - { - public FilteredReadAllEvents(long commitPosition, long preparePosition, int maxCount, int maxSearchWindow, bool resolveLinkTos, bool requireLeader, Filter filter) - { - CommitPosition = commitPosition; - PreparePosition = preparePosition; - MaxCount = maxCount; - MaxSearchWindow = maxSearchWindow; - ResolveLinkTos = resolveLinkTos; - RequireLeader = requireLeader; - Filter = filter; - } - } - - partial class FilteredReadAllEventsCompleted - { - public FilteredReadAllEventsCompleted(long commitPosition, long preparePosition, ResolvedEvent[] events, long nextCommitPosition, long nextPreparePosition, bool isEndOfStream, FilteredReadAllEventsCompleted.Types.FilteredReadAllResult result, string error) - { - CommitPosition = commitPosition; - PreparePosition = preparePosition; - Events.AddRange(events); - NextCommitPosition = nextCommitPosition; - NextPreparePosition = nextPreparePosition; - IsEndOfStream = isEndOfStream; - Result = result; - if (error != null) - { - Error = error; - } - } - } - - partial class CreatePersistentSubscription - { - public CreatePersistentSubscription(string subscriptionGroupName, string eventStreamId, bool resolveLinkTos, long startFrom, int messageTimeoutMilliseconds, bool recordStatistics, int liveBufferSize, int readBatchSize, int bufferSize, int maxRetryCount, bool preferRoundRobin, int checkpointAfterTime, int checkpointMaxCount, int checkpointMinCount, int subscriberMaxCount, string namedConsumerStrategy) - { - SubscriptionGroupName = subscriptionGroupName; - EventStreamId = eventStreamId; - ResolveLinkTos = resolveLinkTos; - StartFrom = startFrom; - MessageTimeoutMilliseconds = messageTimeoutMilliseconds; - RecordStatistics = recordStatistics; - LiveBufferSize = liveBufferSize; - ReadBatchSize = readBatchSize; - BufferSize = bufferSize; - MaxRetryCount = maxRetryCount; - PreferRoundRobin = preferRoundRobin; - CheckpointAfterTime = checkpointAfterTime; - CheckpointMaxCount = checkpointMaxCount; - CheckpointMinCount = checkpointMinCount; - SubscriberMaxCount = subscriberMaxCount; - NamedConsumerStrategy = namedConsumerStrategy; - } - } - - partial class CreatePersistentSubscriptionCompleted - { - public CreatePersistentSubscriptionCompleted(CreatePersistentSubscriptionCompleted.Types.CreatePersistentSubscriptionResult result, string reason) - { - Result = result; - if (reason != null) - { - Reason = reason; - } - } - } - - partial class DeletePersistentSubscription - { - public DeletePersistentSubscription(string subscriptionGroupName, string eventStreamId) - { - SubscriptionGroupName = subscriptionGroupName; - EventStreamId = eventStreamId; - } - } - - partial class DeletePersistentSubscriptionCompleted - { - public DeletePersistentSubscriptionCompleted(Types.DeletePersistentSubscriptionResult result, string reason) - { - Result = result; - Reason = reason; - } - } - - partial class UpdatePersistentSubscription - { - public UpdatePersistentSubscription(string subscriptionGroupName, string eventStreamId, bool resolveLinkTos, long startFrom, int messageTimeoutMilliseconds, bool recordStatistics, int liveBufferSize, int readBatchSize, int bufferSize, int maxRetryCount, bool preferRoundRobin, int checkpointAfterTime, int checkpointMaxCount, int checkpointMinCount, int subscriberMaxCount, string namedConsumerStrategy) - { - SubscriptionGroupName = subscriptionGroupName; - EventStreamId = eventStreamId; - ResolveLinkTos = resolveLinkTos; - StartFrom = startFrom; - MessageTimeoutMilliseconds = messageTimeoutMilliseconds; - RecordStatistics = recordStatistics; - LiveBufferSize = liveBufferSize; - ReadBatchSize = readBatchSize; - BufferSize = bufferSize; - MaxRetryCount = maxRetryCount; - PreferRoundRobin = preferRoundRobin; - CheckpointAfterTime = checkpointAfterTime; - CheckpointMaxCount = checkpointMaxCount; - CheckpointMinCount = checkpointMinCount; - SubscriberMaxCount = subscriberMaxCount; - NamedConsumerStrategy = namedConsumerStrategy; - } - } - - partial class UpdatePersistentSubscriptionCompleted - { - public UpdatePersistentSubscriptionCompleted(UpdatePersistentSubscriptionCompleted.Types.UpdatePersistentSubscriptionResult result, string reason) - { - Result = result; - if (reason != null) - { - Reason = reason; - } - } - } - - partial class ConnectToPersistentSubscription - { - public ConnectToPersistentSubscription(string subscriptionId, string eventStreamId, int allowedInFlightMessages) - { - SubscriptionId = subscriptionId; - EventStreamId = eventStreamId; - AllowedInFlightMessages = allowedInFlightMessages; - } - } - - partial class PersistentSubscriptionConfirmation - { - public PersistentSubscriptionConfirmation(long lastCommitPosition, string subscriptionId, long lastEventNumber) - { - LastCommitPosition = lastCommitPosition; - SubscriptionId = subscriptionId; - LastEventNumber = lastEventNumber; - } - } - - partial class PersistentSubscriptionAckEvents - { - public PersistentSubscriptionAckEvents(string subscriptionId, byte[][] processedEventIds) - { - SubscriptionId = subscriptionId; - for (int i = 0; i < processedEventIds.Length; i++) - { - ProcessedEventIds.Add(ByteString.CopyFrom(processedEventIds[i])); - } - } - } - - partial class PersistentSubscriptionNakEvents - { - public PersistentSubscriptionNakEvents(string subscriptionId, byte[][] processedEventIds, string message, PersistentSubscriptionNakEvents.Types.NakAction action) - { - SubscriptionId = subscriptionId; - for (int i = 0; i < processedEventIds.Length; i++) - { - ProcessedEventIds.Add(ByteString.CopyFrom(processedEventIds[i])); - } - - if (message != null) - { - Message = message; - } - - Action = action; - } - } - - partial class PersistentSubscriptionStreamEventAppeared - { - public PersistentSubscriptionStreamEventAppeared(ResolvedIndexedEvent @event, int retryCount) - { - Event = @event; - RetryCount = retryCount; - } - } - - partial class SubscribeToStream - { - public SubscribeToStream(string eventStreamId, bool resolveLinkTos) - { - EventStreamId = eventStreamId; - ResolveLinkTos = resolveLinkTos; - } - } - - partial class FilteredSubscribeToStream - { - public FilteredSubscribeToStream(string eventStreamId, bool resolveLinkTos, Filter filter, int checkpointInterval) - { - EventStreamId = eventStreamId; - ResolveLinkTos = resolveLinkTos; - Filter = filter; - CheckpointInterval = checkpointInterval; - } - } - - partial class CheckpointReached - { - public CheckpointReached(long commitPosition, long preparePosition) - { - CommitPosition = commitPosition; - PreparePosition = preparePosition; - } - } - - partial class SubscriptionConfirmation - { - public SubscriptionConfirmation(long lastCommitPosition, long lastEventNumber) - { - LastCommitPosition = lastCommitPosition; - LastEventNumber = lastEventNumber; - } - } - - partial class StreamEventAppeared - { - public StreamEventAppeared(ResolvedEvent @event) - { - Event = @event; - } - } - - partial class SubscriptionDropped - { - public SubscriptionDropped(SubscriptionDropped.Types.SubscriptionDropReason reason) - { - Reason = reason; - } - } - - partial class NotHandled - { - public NotHandled(ClientMessage.NotHandled source) - { - Reason = source.Reason switch - { - ClientMessage.NotHandled.Types.NotHandledReason.NotReady => Types.NotHandledReason.NotReady, - ClientMessage.NotHandled.Types.NotHandledReason.TooBusy => Types.NotHandledReason.TooBusy, - ClientMessage.NotHandled.Types.NotHandledReason.NotLeader => Types.NotHandledReason.NotLeader, - ClientMessage.NotHandled.Types.NotHandledReason.IsReadOnly => Types.NotHandledReason.IsReadOnly, - _ => throw new ArgumentOutOfRangeException() - }; - //this is horrible and only for transport compatibility purposes - if (source.LeaderInfo != null) - { - AdditionalInfo = new Types.LeaderInfo(source.LeaderInfo.ExternalTcp, source.LeaderInfo.IsSecure, source.LeaderInfo.Http).ToByteString(); - } - } - partial class Types - { - partial class LeaderInfo - { - public LeaderInfo(string externalTcpAddress, int externalTcpPort, string httpAddress, int httpPort, string externalSecureTcpAddress, int externalSecureTcpPort) - { - ExternalTcpAddress = externalTcpAddress; - ExternalTcpPort = externalTcpPort; - HttpAddress = httpAddress; - HttpPort = httpPort; - ExternalSecureTcpAddress = externalSecureTcpAddress; - ExternalSecureTcpPort = externalSecureTcpPort; - } - - public LeaderInfo(EndPoint tcpEndPoint, bool isTcpEndPointSecure, EndPoint httpEndPoint) - { - if (isTcpEndPointSecure) - { - ExternalSecureTcpAddress = tcpEndPoint?.GetHost(); - ExternalSecureTcpPort = tcpEndPoint?.GetPort() ?? 0; - } - else - { - ExternalTcpAddress = tcpEndPoint?.GetHost(); - ExternalTcpPort = tcpEndPoint?.GetPort() ?? 0; - } - - HttpAddress = httpEndPoint.GetHost(); - HttpPort = httpEndPoint.GetPort(); - } - } - } - } - - partial class ScavengeDatabaseResponse - { - public ScavengeDatabaseResponse(ScavengeDatabaseResponse.Types.ScavengeResult result, string scavengeId) - { - Result = result; - ScavengeId = scavengeId; - } - } - - partial class IdentifyClient - { - public IdentifyClient(int version, string connectionName) - { - Version = version; - if (connectionName != null) - { - ConnectionName = connectionName; - } - } - } -} diff --git a/src/EventStore.Core/Messages/TcpMessage.cs b/src/EventStore.Core/Messages/TcpMessage.cs deleted file mode 100644 index 40f1425898..0000000000 --- a/src/EventStore.Core/Messages/TcpMessage.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Net.Sockets; -using EventStore.Core.Messaging; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.Core.Messages -{ - public static partial class TcpMessage - { - [DerivedMessage(CoreMessage.Tcp)] - public partial class TcpSend : Message - { - public override object SynchronizationGroup - { - get { return ConnectionManager; } - } - - public readonly TcpConnectionManager ConnectionManager; - public readonly Message Message; - - public TcpSend(TcpConnectionManager connectionManager, Message message) - { - ConnectionManager = connectionManager; - Message = message; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class Heartbeat : Message - { - public readonly long ReceiveProgressIndicator; - public readonly long SendProgressIndicator; - - public Heartbeat(long receiveProgressIndicator, long sendProgressIndicator) - { - ReceiveProgressIndicator = receiveProgressIndicator; - SendProgressIndicator = sendProgressIndicator; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class HeartbeatTimeout : Message - { - public readonly long ReceiveProgressIndicator; - - public HeartbeatTimeout(long receiveProgressIndicator) - { - ReceiveProgressIndicator = receiveProgressIndicator; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class PongMessage : Message - { - public readonly Guid CorrelationId; - public readonly byte[] Payload; - - public PongMessage(Guid correlationId, byte[] payload) - { - CorrelationId = correlationId; - Payload = payload; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class ConnectionEstablished : Message - { - public readonly TcpConnectionManager Connection; - - public ConnectionEstablished(TcpConnectionManager connection) - { - Connection = connection; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class ConnectionClosed : Message - { - public readonly TcpConnectionManager Connection; - public readonly SocketError SocketError; - - public ConnectionClosed(TcpConnectionManager connection, SocketError socketError) - { - Connection = connection; - SocketError = socketError; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class NotReady : Message - { - public readonly Guid CorrelationId; - public readonly string Reason; - - public NotReady(Guid correlationId, string reason) - { - CorrelationId = correlationId; - Reason = reason; - } - } - - - [DerivedMessage(CoreMessage.Tcp)] - public partial class NotAuthenticated : Message - { - public readonly Guid CorrelationId; - public readonly string Reason; - - public NotAuthenticated(Guid correlationId, string reason) - { - CorrelationId = correlationId; - Reason = reason; - } - } - - [DerivedMessage(CoreMessage.Tcp)] - public partial class Authenticated : Message - { - public readonly Guid CorrelationId; - - public Authenticated(Guid correlationId) - { - CorrelationId = correlationId; - } - } - } -} diff --git a/src/EventStore.Core/Messaging/CoreMessage.cs b/src/EventStore.Core/Messaging/CoreMessage.cs index 89d29acc6f..4e6a16e0de 100644 --- a/src/EventStore.Core/Messaging/CoreMessage.cs +++ b/src/EventStore.Core/Messaging/CoreMessage.cs @@ -22,7 +22,6 @@ public enum CoreMessage Storage, Subscription, System, - Tcp, Telemetry, Timer, UserManagement diff --git a/src/EventStore.Core/NodeTcpOptions.cs b/src/EventStore.Core/NodeTcpOptions.cs deleted file mode 100644 index 880c10b9d5..0000000000 --- a/src/EventStore.Core/NodeTcpOptions.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace EventStore.Core; - -// Still needed for gossip and stats. -public class NodeTcpOptions -{ - public int NodeTcpPort { get; init; } = 1113; - public bool EnableExternalTcp { get; init; } - public int? NodeTcpPortAdvertiseAs { get; init; } -} diff --git a/src/EventStore.Core/Services/AuthorizationGateway.cs b/src/EventStore.Core/Services/AuthorizationGateway.cs index 927e83bb27..e95f692661 100644 --- a/src/EventStore.Core/Services/AuthorizationGateway.cs +++ b/src/EventStore.Core/Services/AuthorizationGateway.cs @@ -211,7 +211,7 @@ public void Authorize(Message toValidate, IPublisher destination) case ClientMessage.PersistentSubscriptionNackEvents _: case ClientMessage.UnsubscribeFromStream _: case ClientMessage.NotHandled _: - case TcpMessage.NotAuthenticated _: + case ClientMessage.NotAuthenticated _: case ClientMessage.WriteEventsCompleted _: case ClientMessage.TransactionStartCompleted _: case ClientMessage.TransactionWriteCompleted _: @@ -244,8 +244,6 @@ public void Authorize(Message toValidate, IPublisher destination) break; default: #if DEBUG - //This sucks, because if new tcp messages are added there is no way to be sure they have to be authorized... - //They should be caught by debug builds though throw new ArgumentOutOfRangeException(nameof(toValidate), toValidate.GetType().FullName, "Unhandled client message"); #else diff --git a/src/EventStore.Core/Services/ElectionsService.cs b/src/EventStore.Core/Services/ElectionsService.cs index d865b66e26..e3f7abe195 100644 --- a/src/EventStore.Core/Services/ElectionsService.cs +++ b/src/EventStore.Core/Services/ElectionsService.cs @@ -137,10 +137,8 @@ public ElectionsService(IPublisher publisher, _timeProvider.UtcNow, VNodeState.Initializing, true, - memberInfo.InternalTcpEndPoint, memberInfo.InternalSecureTcpEndPoint, - memberInfo.ExternalTcpEndPoint, memberInfo.ExternalSecureTcpEndPoint, memberInfo.HttpEndPoint, - memberInfo.AdvertiseHostToClientAs, memberInfo.AdvertiseHttpPortToClientAs, memberInfo.AdvertiseTcpPortToClientAs, + memberInfo.AdvertiseHostToClientAs, memberInfo.AdvertiseHttpPortToClientAs, ownInfo.LastCommitPosition, ownInfo.WriterCheckpoint, ownInfo.ChaserCheckpoint, ownInfo.EpochPosition, ownInfo.EpochNumber, ownInfo.EpochId, ownInfo.NodePriority, memberInfo.IsReadOnlyReplica, VersionInfo.Version) diff --git a/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs b/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs index 643147e6ed..9b1720c796 100644 --- a/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs +++ b/src/EventStore.Core/Services/Gossip/GossipServiceBase.cs @@ -274,7 +274,7 @@ public void Handle(GossipMessage.GossipSendFailed message) if (node.InstanceId == CurrentLeader?.InstanceId) { Log.Information( - "Leader [{leaderEndPoint}, {instanceId:B}] appears to be DEAD (Gossip send failed); wait for TCP to decide.", + "Leader [{leaderEndPoint}, {instanceId:B}] appears to be DEAD (Gossip send failed); wait for replication transport to decide.", message.Recipient, node.InstanceId); return; } @@ -302,7 +302,7 @@ public void Handle(SystemMessage.VNodeConnectionLost message) return; } - Log.Information("Looks like node [{nodeEndPoint}] is DEAD (TCP connection lost). Issuing a gossip to confirm.", + Log.Information("Looks like node [{nodeEndPoint}] is DEAD (replication connection lost). Issuing a gossip to confirm.", message.VNodeEndPoint); _bus.Publish(new GrpcMessage.SendOverGrpc(node.HttpEndPoint, new GossipMessage.GetGossip(), @@ -354,7 +354,7 @@ public void Handle(GossipMessage.GetGossipFailed message) if (_cluster.HasChangedSince(oldCluster)) { LogClusterChange(oldCluster, _cluster, - string.Format("TCP connection lost to [{0}]", message.Recipient)); + string.Format("Replication connection lost to [{0}]", message.Recipient)); } _bus.Publish(new GossipMessage.GossipUpdated(_cluster)); @@ -371,7 +371,7 @@ public void Handle(SystemMessage.VNodeConnectionEstablished message) if (_cluster.HasChangedSince(oldCluster)) { LogClusterChange(oldCluster, _cluster, - string.Format("TCP connection established to [{0}]", message.VNodeEndPoint)); + string.Format("Replication connection established to [{0}]", message.VNodeEndPoint)); } _bus.Publish(new GossipMessage.GossipUpdated(_cluster)); diff --git a/src/EventStore.Core/Services/Gossip/NodeGossipService.cs b/src/EventStore.Core/Services/Gossip/NodeGossipService.cs index 803e69d77d..2007807a7c 100644 --- a/src/EventStore.Core/Services/Gossip/NodeGossipService.cs +++ b/src/EventStore.Core/Services/Gossip/NodeGossipService.cs @@ -57,14 +57,9 @@ protected override MemberInfo GetInitialMe() _timeProvider.UtcNow, initialState, true, - _memberInfo.InternalTcpEndPoint, - _memberInfo.InternalSecureTcpEndPoint, - _memberInfo.ExternalTcpEndPoint, - _memberInfo.ExternalSecureTcpEndPoint, _memberInfo.HttpEndPoint, _memberInfo.AdvertiseHostToClientAs, _memberInfo.AdvertiseHttpPortToClientAs, - _memberInfo.AdvertiseTcpPortToClientAs, _getLastCommitPosition(), _writerCheckpoint.Read(), _chaserCheckpoint.Read(), diff --git a/src/EventStore.Core/Services/Monitoring/MonitoringService.cs b/src/EventStore.Core/Services/Monitoring/MonitoringService.cs index f342fdfcdc..c4db88ef49 100644 --- a/src/EventStore.Core/Services/Monitoring/MonitoringService.cs +++ b/src/EventStore.Core/Services/Monitoring/MonitoringService.cs @@ -13,7 +13,6 @@ using EventStore.Core.Messaging; using EventStore.Core.Services.Monitoring.Stats; using EventStore.Core.Services.UserManagement; -using EventStore.Transport.Tcp; using ILogger = Serilog.ILogger; using Timeout = System.Threading.Timeout; @@ -33,8 +32,7 @@ public class MonitoringService : IHandle, IAsyncHandle, IHandle, IHandle, - IAsyncHandle, - IHandle + IAsyncHandle { private static readonly ILogger RegularLog = Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "REGULAR-STATS-LOGGER"); @@ -62,19 +60,12 @@ public class MonitoringService : IHandle, private readonly string _nodeStatsStream; private bool _statsStreamCreated; private Guid _streamMetadataWriteCorrId; - private IMonitoredTcpConnection[] _memoizedTcpConnections; - private DateTime _lastTcpConnectionsRequestTime; - private IPEndPoint _tcpEndpoint; - private IPEndPoint _tcpSecureEndpoint; - public MonitoringService(IQueuedHandler monitoringQueue, IAsyncHandle statsCollectionDispatcher, IPublisher mainQueue, TimeSpan statsCollectionPeriod, EndPoint nodeEndpoint, StatsStorage statsStorage, - IPEndPoint tcpEndpoint, - IPEndPoint tcpSecureEndpoint, SystemStatsHelper systemStatsHelper) { Ensure.NotNull(monitoringQueue, "monitoringQueue"); @@ -100,8 +91,6 @@ public MonitoringService(IQueuedHandler monitoringQueue, } _nodeStatsStream = $"{SystemStreams.StatsStreamPrefix}-{nodeEndpoint}"; - _tcpEndpoint = tcpEndpoint; - _tcpSecureEndpoint = tcpSecureEndpoint; _timer = Task.CompletedTask; _systemStats = systemStatsHelper; @@ -343,74 +332,6 @@ public async ValueTask HandleAsync(MonitoringMessage.GetFreshStats message, Canc } } - public void Handle(MonitoringMessage.GetFreshTcpConnectionStats message) - { - try - { - IMonitoredTcpConnection[] connections = null; - if (!TryGetMemoizedTcpConnections(out connections)) - { - connections = TcpConnectionMonitor.Default.GetTcpConnectionStats(); - if (connections != null) - { - _memoizedTcpConnections = connections; - _lastTcpConnectionsRequestTime = DateTime.UtcNow; - } - } - - List connStats = new List(); - foreach (var conn in connections) - { - var tcpConn = conn as TcpConnection; - if (tcpConn != null) - { - var isExternalConnection = _tcpEndpoint != null && _tcpEndpoint.Port == tcpConn.LocalEndPoint.GetPort(); - connStats.Add(new MonitoringMessage.TcpConnectionStats - { - IsExternalConnection = isExternalConnection, - RemoteEndPoint = tcpConn.RemoteEndPoint.ToString(), - LocalEndPoint = tcpConn.LocalEndPoint.ToString(), - ConnectionId = tcpConn.ConnectionId, - ClientConnectionName = tcpConn.ClientConnectionName, - TotalBytesSent = tcpConn.TotalBytesSent, - TotalBytesReceived = tcpConn.TotalBytesReceived, - PendingSendBytes = tcpConn.PendingSendBytes, - PendingReceivedBytes = tcpConn.PendingReceivedBytes, - IsSslConnection = false - }); - } - - var tcpConnSsl = conn as TcpConnectionSsl; - if (tcpConnSsl != null) - { - var isExternalConnection = _tcpSecureEndpoint != null && - _tcpSecureEndpoint.Port == tcpConnSsl.LocalEndPoint.GetPort(); - connStats.Add(new MonitoringMessage.TcpConnectionStats - { - IsExternalConnection = isExternalConnection, - RemoteEndPoint = tcpConnSsl.RemoteEndPoint.ToString(), - LocalEndPoint = tcpConnSsl.LocalEndPoint.ToString(), - ConnectionId = tcpConnSsl.ConnectionId, - ClientConnectionName = tcpConnSsl.ClientConnectionName, - TotalBytesSent = tcpConnSsl.TotalBytesSent, - TotalBytesReceived = tcpConnSsl.TotalBytesReceived, - PendingSendBytes = tcpConnSsl.PendingSendBytes, - PendingReceivedBytes = tcpConnSsl.PendingReceivedBytes, - IsSslConnection = true - }); - } - } - - message.Envelope.ReplyWith( - new MonitoringMessage.GetFreshTcpConnectionStatsCompleted(connStats) - ); - } - catch (Exception ex) - { - Log.Error(ex, "Error on getting fresh tcp connection stats"); - } - } - private bool TryGetMemoizedStats(out StatsContainer stats) { if (_memoizedStats == null || DateTime.UtcNow - _lastStatsRequestTime > MemoizePeriod) @@ -423,16 +344,5 @@ private bool TryGetMemoizedStats(out StatsContainer stats) return true; } - private bool TryGetMemoizedTcpConnections(out IMonitoredTcpConnection[] connections) - { - if (_memoizedTcpConnections == null || DateTime.UtcNow - _lastTcpConnectionsRequestTime > MemoizePeriod) - { - connections = null; - return false; - } - - connections = _memoizedTcpConnections; - return true; - } } } diff --git a/src/EventStore.Core/Services/Monitoring/SystemStatsHelper.cs b/src/EventStore.Core/Services/Monitoring/SystemStatsHelper.cs index e81c4a859c..7ffa6d8323 100644 --- a/src/EventStore.Core/Services/Monitoring/SystemStatsHelper.cs +++ b/src/EventStore.Core/Services/Monitoring/SystemStatsHelper.cs @@ -8,7 +8,6 @@ using EventStore.Core.Services.Monitoring.Stats; using EventStore.Core.Services.Monitoring.Utils; using EventStore.Core.TransactionLog.Checkpoint; -using EventStore.Transport.Tcp; using ILogger = Serilog.ILogger; namespace EventStore.Core.Services.Monitoring @@ -48,19 +47,6 @@ public IDictionary GetSystemStats() stats["proc-diskIo-readOps"] = diskIo.ReadOps; stats["proc-diskIo-writeOps"] = diskIo.WriteOps; - var tcp = TcpConnectionMonitor.Default.GetTcpStats(); - stats["proc-tcp-connections"] = tcp.Connections; - stats["proc-tcp-receivingSpeed"] = tcp.ReceivingSpeed; - stats["proc-tcp-sendingSpeed"] = tcp.SendingSpeed; - stats["proc-tcp-inSend"] = tcp.InSend; - stats["proc-tcp-measureTime"] = tcp.MeasureTime; - stats["proc-tcp-pendingReceived"] = tcp.PendingReceived; - stats["proc-tcp-pendingSend"] = tcp.PendingSend; - stats["proc-tcp-receivedBytesSinceLastRun"] = tcp.ReceivedBytesSinceLastRun; - stats["proc-tcp-receivedBytesTotal"] = tcp.ReceivedBytesTotal; - stats["proc-tcp-sentBytesSinceLastRun"] = tcp.SentBytesSinceLastRun; - stats["proc-tcp-sentBytesTotal"] = tcp.SentBytesTotal; - stats["es-checksum"] = _writerCheckpoint.Read(); stats["es-checksumNonFlushed"] = _writerCheckpoint.ReadNonFlushed(); diff --git a/src/EventStore.Core/Services/PersistentSubscription/PersistentSubscriptionService.cs b/src/EventStore.Core/Services/PersistentSubscription/PersistentSubscriptionService.cs index a1c81c8374..05a46df4ea 100644 --- a/src/EventStore.Core/Services/PersistentSubscription/PersistentSubscriptionService.cs +++ b/src/EventStore.Core/Services/PersistentSubscription/PersistentSubscriptionService.cs @@ -29,7 +29,6 @@ public abstract class PersistentSubscriptionService public class PersistentSubscriptionService : PersistentSubscriptionService, IHandle, - IHandle, IHandle, IHandle, IHandle, @@ -1103,24 +1102,6 @@ private void UnsubscribeFromStream(Guid correlationId, bool sendDropNotification } } - public void Handle(TcpMessage.ConnectionClosed message) - { - if (_subscriptionsById == null) - { - return; //haven't built yet. - } - - foreach (var subscription in _subscriptionsById.Values) - { - if (subscription.RemoveClientByConnectionId(message.Connection.ConnectionId)) - { - Log.Debug("Persistent subscription {subscription} lost connection from {remoteEndPoint}", - subscription.SubscriptionId, - message.Connection.RemoteEndPoint); - } - } - } - public async ValueTask ConnectToPersistentSubscription( IPersistentSubscriptionEventSource eventSource, string groupName, diff --git a/src/EventStore.Core/Services/Replication/LogRecordFramer.cs b/src/EventStore.Core/Services/Replication/LogRecordFramer.cs index 117e626d6a..b4c870ae24 100644 --- a/src/EventStore.Core/Services/Replication/LogRecordFramer.cs +++ b/src/EventStore.Core/Services/Replication/LogRecordFramer.cs @@ -6,8 +6,8 @@ using System.Threading.Tasks; using DotNext.IO; using EventStore.Common.Utils; +using EventStore.Core.Helpers; using EventStore.Core.TransactionLog.LogRecords; -using EventStore.Transport.Tcp.Framing; namespace EventStore.Core.Services.Replication; diff --git a/src/EventStore.Core/Services/Replication/TransactionFramer.cs b/src/EventStore.Core/Services/Replication/TransactionFramer.cs index 8b6788e635..e1166610df 100644 --- a/src/EventStore.Core/Services/Replication/TransactionFramer.cs +++ b/src/EventStore.Core/Services/Replication/TransactionFramer.cs @@ -3,8 +3,8 @@ using System.Threading; using System.Threading.Tasks; using EventStore.Common.Utils; +using EventStore.Core.Helpers; using EventStore.Core.TransactionLog.LogRecords; -using EventStore.Transport.Tcp.Framing; namespace EventStore.Core.Services.Replication; diff --git a/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs b/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs index 0d91272c93..cfd7d7a51b 100644 --- a/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs +++ b/src/EventStore.Core/Services/RequestForwarding/GrpcRequestForwardingSupervisor.cs @@ -136,7 +136,7 @@ public void Handle(ClientMessage.ForwardMessage message) "Request forwarding stream is closed.")); break; case RequestForwardingAdmission.CredentialsRequireTls: - PublishIfActive(active, new TcpMessage.NotAuthenticated( + PublishIfActive(active, new ClientMessage.NotAuthenticated( request.InternalCorrId, "Credentials cannot be forwarded unless transport security is enabled.")); break; diff --git a/src/EventStore.Core/Services/RequestForwardingService.cs b/src/EventStore.Core/Services/RequestForwardingService.cs index 00ec4690c2..94670ddd75 100644 --- a/src/EventStore.Core/Services/RequestForwardingService.cs +++ b/src/EventStore.Core/Services/RequestForwardingService.cs @@ -10,7 +10,7 @@ namespace EventStore.Core.Services public class RequestForwardingService : IHandle, IHandle, IHandle, - IHandle, + IHandle, IHandle, IHandle, IHandle, @@ -54,11 +54,11 @@ public void Handle(ClientMessage.NotHandled message) (clientCorrId, m) => new ClientMessage.NotHandled(clientCorrId, m.Reason, m.LeaderInfo)); } - public void Handle(TcpMessage.NotAuthenticated message) + public void Handle(ClientMessage.NotAuthenticated message) { _forwardingProxy.TryForwardReply( message.CorrelationId, message, - (clientCorrId, m) => new TcpMessage.NotAuthenticated(clientCorrId, m.Reason)); + (clientCorrId, m) => new ClientMessage.NotAuthenticated(clientCorrId, m.Reason)); } public void Handle(ClientMessage.WriteEventsCompleted message) diff --git a/src/EventStore.Core/Services/Storage/ReaderIndex/EventFilter.cs b/src/EventStore.Core/Services/Storage/ReaderIndex/EventFilter.cs index fbb3ff716f..52326bdf74 100644 --- a/src/EventStore.Core/Services/Storage/ReaderIndex/EventFilter.cs +++ b/src/EventStore.Core/Services/Storage/ReaderIndex/EventFilter.cs @@ -36,31 +36,6 @@ public static IEventFilter Regex(bool isAllStream, string regex) => new EventTypeRegexStrategy(isAllStream, regex); } - public static IEventFilter Get(bool isAllStream, Client.Messages.Filter filter) - { - if (filter == null || filter.Data.Count == 0) - { - return isAllStream ? (IEventFilter)new DefaultAllFilterStrategy() : new DefaultStreamFilterStrategy(); - } - - return filter.Context switch - { - Client.Messages.Filter.Types.FilterContext.EventType when filter.Type == - Client.Messages.Filter.Types.FilterType.Prefix => - EventType.Prefixes(isAllStream, filter.Data.ToArray()), - Client.Messages.Filter.Types.FilterContext.EventType when filter.Type == - Client.Messages.Filter.Types.FilterType.Regex => - EventType.Regex(isAllStream, filter.Data[0]), - Client.Messages.Filter.Types.FilterContext.StreamId when filter.Type == - Client.Messages.Filter.Types.FilterType.Prefix => - StreamName.Prefixes(isAllStream, filter.Data.ToArray()), - Client.Messages.Filter.Types.FilterContext.StreamId when filter.Type == - Client.Messages.Filter.Types.FilterType.Regex => - StreamName.Regex(isAllStream, filter.Data[0]), - _ => throw new Exception() // Invalid filter - }; - } - private sealed class DefaultStreamFilterStrategy : IEventFilter { [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] @@ -281,34 +256,16 @@ public static (bool success, string reason) TryParse(EventFilterDto dto, out IEv public static (bool Success, string Reason) TryParse(string context, bool isAllStream, string type, string data, out IEventFilter filter) { - Client.Messages.Filter.Types.FilterContext parsedContext; - switch (context) + if (context is not EventTypeContext and not StreamIdContext) { - case EventTypeContext: - parsedContext = Client.Messages.Filter.Types.FilterContext.EventType; - break; - case StreamIdContext: - parsedContext = Client.Messages.Filter.Types.FilterContext.StreamId; - break; - default: - filter = null; - var names = string.Join(", ", Enum.GetNames(typeof(Client.Messages.Filter.Types.FilterContext))); - return (false, $"Invalid context please provide one of the following: {names}."); + filter = null; + return (false, "Invalid context please provide one of the following: EventType, StreamId."); } - Client.Messages.Filter.Types.FilterType parsedType; - switch (type) + if (type is not RegexType and not PrefixType) { - case RegexType: - parsedType = Client.Messages.Filter.Types.FilterType.Regex; - break; - case PrefixType: - parsedType = Client.Messages.Filter.Types.FilterType.Prefix; - break; - default: - filter = null; - var names = string.Join(", ", Enum.GetNames(typeof(Client.Messages.Filter.Types.FilterType))); - return (false, $"Invalid type please provide one of the following: {names}."); + filter = null; + return (false, "Invalid type please provide one of the following: Regex, Prefix."); } if (string.IsNullOrEmpty(data)) @@ -317,14 +274,18 @@ public static (bool Success, string Reason) TryParse(string context, bool isAllS return (false, "Please provide a comma delimited list of data with at least one item."); } - if (parsedType == Client.Messages.Filter.Types.FilterType.Regex) + if (type == RegexType) { - filter = Get(isAllStream, new Client.Messages.Filter(parsedContext, parsedType, new[] { data })); + filter = context == EventTypeContext + ? EventType.Regex(isAllStream, data) + : StreamName.Regex(isAllStream, data); return (true, null); } - filter = Get(isAllStream, new Client.Messages.Filter(parsedContext, parsedType, - data.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries))); + var prefixes = data.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); + filter = context == EventTypeContext + ? EventType.Prefixes(isAllStream, prefixes) + : StreamName.Prefixes(isAllStream, prefixes); return (true, null); } } diff --git a/src/EventStore.Core/Services/SubscriptionsService.cs b/src/EventStore.Core/Services/SubscriptionsService.cs index dba64708b5..6709c71ae0 100644 --- a/src/EventStore.Core/Services/SubscriptionsService.cs +++ b/src/EventStore.Core/Services/SubscriptionsService.cs @@ -37,7 +37,6 @@ public class SubscriptionsService : SubscriptionsService, IHandle, IHandle, - IHandle, IAsyncHandle, IAsyncHandle, IHandle, @@ -111,41 +110,6 @@ public void Handle(SystemMessage.BecomeShuttingDown message) _queuedHandler.RequestStop(); } - public void Handle(TcpMessage.ConnectionClosed message) - { - List subscriptionGroupsToRemove = null; - foreach (var subscriptionGroup in _subscriptionTopics) - { - var subscriptions = subscriptionGroup.Value; - for (int i = 0, n = subscriptions.Count; i < n; ++i) - { - if (subscriptions[i].ConnectionId == message.Connection.ConnectionId) - { - _subscriptionsById.Remove(subscriptions[i].CorrelationId); - } - } - - subscriptions.RemoveAll(x => x.ConnectionId == message.Connection.ConnectionId); - if (subscriptions.Count == 0) // schedule removal of list instance - { - if (subscriptionGroupsToRemove == null) - { - subscriptionGroupsToRemove = new List(); - } - - subscriptionGroupsToRemove.Add(subscriptionGroup.Key); - } - } - - if (subscriptionGroupsToRemove != null) - { - for (int i = 0, n = subscriptionGroupsToRemove.Count; i < n; ++i) - { - _subscriptionTopics.Remove(subscriptionGroupsToRemove[i]); - } - } - } - async ValueTask IAsyncHandle.HandleAsync(ClientMessage.SubscribeToStream msg, CancellationToken token) { diff --git a/src/EventStore.Core/Services/TcpSendService.cs b/src/EventStore.Core/Services/TcpSendService.cs deleted file mode 100644 index e3e2d31bcd..0000000000 --- a/src/EventStore.Core/Services/TcpSendService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using EventStore.Core.Bus; -using EventStore.Core.Messages; - -namespace EventStore.Core.Services -{ - public class TcpSendService : IHandle - { - public void Handle(TcpMessage.TcpSend message) - { - // todo: histogram metric? - message.ConnectionManager.SendMessage(message.Message); - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs index be24786c2e..c9e857c9de 100644 --- a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs +++ b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingGrpcCodec.cs @@ -172,7 +172,7 @@ public static ClientMessage.WriteRequestMessage FromGrpc( ClientMessage.TransactionCommitCompleted completed => ToGrpc(completed), ClientMessage.DeleteStreamCompleted completed => ToGrpc(completed), ClientMessage.NotHandled notHandled => ToGrpc(notHandled), - TcpMessage.NotAuthenticated notAuthenticated => ToGrpc(notAuthenticated), + ClientMessage.NotAuthenticated notAuthenticated => ToGrpc(notAuthenticated), _ => throw new ArgumentOutOfRangeException(nameof(message), message.GetType().FullName, "Unsupported forwarding response") } @@ -195,7 +195,7 @@ public static Message FromGrpc(Proto.LeaderFrame frame) Proto.ForwardResponse.PayloadOneofCase.DeleteStream => FromGrpc(correlationId, response.DeleteStream), Proto.ForwardResponse.PayloadOneofCase.NotHandled => FromGrpc(correlationId, response.NotHandled), Proto.ForwardResponse.PayloadOneofCase.NotAuthenticated => - new TcpMessage.NotAuthenticated(correlationId, response.NotAuthenticated.Reason), + new ClientMessage.NotAuthenticated(correlationId, response.NotAuthenticated.Reason), _ => throw new ArgumentOutOfRangeException(nameof(frame), response.PayloadCase, "Unknown forwarding response") }; @@ -457,8 +457,6 @@ private static Proto.ForwardResponse ToGrpc(ClientMessage.NotHandled message) { notHandled.LeaderInfo = new Proto.LeaderInfo { - ExternalTcp = ToGrpc(message.LeaderInfo.ExternalTcp), - IsSecure = message.LeaderInfo.IsSecure, Http = ToGrpc(message.LeaderInfo.Http) }; } @@ -471,7 +469,7 @@ private static Proto.ForwardResponse ToGrpc(ClientMessage.NotHandled message) return response; } - private static Proto.ForwardResponse ToGrpc(TcpMessage.NotAuthenticated message) + private static Proto.ForwardResponse ToGrpc(ClientMessage.NotAuthenticated message) { var response = NewResponse(message.CorrelationId); response.NotAuthenticated = new Proto.NotAuthenticated { Reason = message.Reason ?? string.Empty }; @@ -552,8 +550,6 @@ private static ClientMessage.NotHandled FromGrpc(Guid correlationId, Proto.NotHa correlationId, reason, new ClientMessage.NotHandled.Types.LeaderInfo( - FromGrpc(message.LeaderInfo.ExternalTcp), - message.LeaderInfo.IsSecure, FromGrpc(message.LeaderInfo.Http))), Proto.NotHandled.DetailOneofCase.Description => new ClientMessage.NotHandled(correlationId, reason, message.Description), diff --git a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs index 39b5ef358d..c08f7d546f 100644 --- a/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs +++ b/src/EventStore.Core/Services/Transport/Grpc/Forwarding/ForwardingService.cs @@ -355,7 +355,7 @@ public static ForwardedAuthentication Authenticated( public static ForwardedAuthentication NotAuthenticated(string reason) => new( null, null, - requestId => new TcpMessage.NotAuthenticated(requestId, reason)); + requestId => new ClientMessage.NotAuthenticated(requestId, reason)); public static ForwardedAuthentication NotReady(string reason) => new( null, diff --git a/src/EventStore.Core/Services/Transport/Grpc/Monitoring.cs b/src/EventStore.Core/Services/Transport/Grpc/Monitoring.cs index 1518bf82a3..9736e30036 100644 --- a/src/EventStore.Core/Services/Transport/Grpc/Monitoring.cs +++ b/src/EventStore.Core/Services/Transport/Grpc/Monitoring.cs @@ -84,43 +84,6 @@ async Task StreamStats() } } - public override Task TcpStats(TcpStatsReq request, ServerCallContext context) - { - var responseSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var envelope = new CallbackEnvelope(message => - { - if (message is not MonitoringMessage.GetFreshTcpConnectionStatsCompleted completed) - { - responseSource.TrySetException( - UnknownMessage(message)); - return; - } - - var response = new TcpStatsResp(); - foreach (var connection in completed.ConnectionStats) - { - response.Connections.Add(new TcpConnectionStats - { - RemoteEndpoint = connection.RemoteEndPoint ?? string.Empty, - LocalEndpoint = connection.LocalEndPoint ?? string.Empty, - ClientConnectionName = connection.ClientConnectionName ?? string.Empty, - ConnectionId = connection.ConnectionId.ToString("D"), - TotalBytesSent = connection.TotalBytesSent, - TotalBytesReceived = connection.TotalBytesReceived, - PendingSendBytes = connection.PendingSendBytes, - PendingReceivedBytes = connection.PendingReceivedBytes, - IsExternalConnection = connection.IsExternalConnection, - IsSslConnection = connection.IsSslConnection - }); - } - - responseSource.TrySetResult(response); - }); - - _publisher.Publish(new MonitoringMessage.GetFreshTcpConnectionStats(envelope)); - return responseSource.Task.WaitAsync(context.CancellationToken); - } - public override Task ReplicationStats(ReplicationStatsReq request, ServerCallContext context) { var responseSource = diff --git a/src/EventStore.Core/Services/Transport/Tcp/ClientTcpDispatcher.cs b/src/EventStore.Core/Services/Transport/Tcp/ClientTcpDispatcher.cs deleted file mode 100644 index fd95d28cff..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/ClientTcpDispatcher.cs +++ /dev/null @@ -1,600 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Services.Storage.ReaderIndex; -using EventStore.Core.Util; -using ResolvedEvent = EventStore.Core.Data.ResolvedEvent; - -namespace EventStore.Core.Services.Transport.Tcp; - -public class ClientTcpDispatcher : ClientWriteTcpDispatcher -{ - public ClientTcpDispatcher(int writeTimeoutMs) - : this(TimeSpan.FromMilliseconds(writeTimeoutMs)) - { - } - - public ClientTcpDispatcher(TimeSpan writeTimeout) : base(writeTimeout) - { - AddUnwrapper(TcpCommand.Ping, UnwrapPing, ClientVersion.V2); - AddWrapper(WrapPong, ClientVersion.V2); - - AddUnwrapper(TcpCommand.IdentifyClient, UnwrapIdentifyClient, ClientVersion.V2); - - AddUnwrapper(TcpCommand.ReadEvent, UnwrapReadEvent, ClientVersion.V2); - AddWrapper(WrapReadEventCompleted, ClientVersion.V2); - - AddUnwrapper(TcpCommand.ReadStreamEventsForward, UnwrapReadStreamEventsForward, ClientVersion.V2); - AddWrapper(WrapReadStreamEventsForwardCompleted, - ClientVersion.V2); - AddUnwrapper(TcpCommand.ReadStreamEventsBackward, UnwrapReadStreamEventsBackward, ClientVersion.V2); - AddWrapper(WrapReadStreamEventsBackwardCompleted, - ClientVersion.V2); - - AddUnwrapper(TcpCommand.ReadAllEventsForward, UnwrapReadAllEventsForward, ClientVersion.V2); - AddWrapper(WrapReadAllEventsForwardCompleted, - ClientVersion.V2); - AddUnwrapper(TcpCommand.ReadAllEventsBackward, UnwrapReadAllEventsBackward, ClientVersion.V2); - AddWrapper(WrapReadAllEventsBackwardCompleted, - ClientVersion.V2); - - AddUnwrapper(TcpCommand.FilteredReadAllEventsForward, UnwrapFilteredReadAllEventsForward, ClientVersion.V2); - AddWrapper(WrapFilteredReadAllEventsForwardCompleted, - ClientVersion.V2); - - AddUnwrapper(TcpCommand.FilteredReadAllEventsBackward, UnwrapFilteredReadAllEventsBackward, - ClientVersion.V2); - AddWrapper(WrapFilteredReadAllEventsBackwardCompleted, - ClientVersion.V2); - - AddUnwrapper(TcpCommand.SubscribeToStream, UnwrapSubscribeToStream, ClientVersion.V2); - AddUnwrapper(TcpCommand.FilteredSubscribeToStream, UnwrapFilteredSubscribeToStream, ClientVersion.V2); - AddUnwrapper(TcpCommand.UnsubscribeFromStream, UnwrapUnsubscribeFromStream, ClientVersion.V2); - - AddWrapper(WrapCheckpointReached, ClientVersion.V2); - - AddWrapper(WrapSubscribedToStream, ClientVersion.V2); - AddWrapper(WrapStreamEventAppeared, ClientVersion.V2); - AddWrapper(WrapSubscriptionDropped, ClientVersion.V2); - AddUnwrapper(TcpCommand.CreatePersistentSubscription, UnwrapCreatePersistentSubscription, ClientVersion.V2); - AddUnwrapper(TcpCommand.DeletePersistentSubscription, UnwrapDeletePersistentSubscription, ClientVersion.V2); - AddWrapper( - WrapCreatePersistentSubscriptionCompleted, - ClientVersion.V2); - AddWrapper( - WrapDeletePersistentSubscriptionCompleted, - ClientVersion.V2); - AddUnwrapper(TcpCommand.UpdatePersistentSubscription, UnwrapUpdatePersistentSubscription, ClientVersion.V2); - AddWrapper( - WrapUpdatePersistentSubscriptionCompleted, - ClientVersion.V2); - - - AddUnwrapper(TcpCommand.ConnectToPersistentSubscription, UnwrapConnectToPersistentSubscription, - ClientVersion.V2); - AddUnwrapper(TcpCommand.PersistentSubscriptionAckEvents, UnwrapPersistentSubscriptionAckEvents, - ClientVersion.V2); - AddUnwrapper(TcpCommand.PersistentSubscriptionNakEvents, UnwrapPersistentSubscriptionNackEvents, - ClientVersion.V2); - AddWrapper(WrapPersistentSubscriptionConfirmation, - ClientVersion.V2); - AddWrapper( - WrapPersistentSubscriptionStreamEventAppeared, ClientVersion.V2); - - AddUnwrapper(TcpCommand.ScavengeDatabase, UnwrapScavengeDatabase, ClientVersion.V2); - AddWrapper(WrapScavengeDatabaseResponse, ClientVersion.V2); - AddWrapper(WrapScavengeDatabaseResponse, ClientVersion.V2); - AddWrapper(WrapScavengeDatabaseResponse, ClientVersion.V2); - - AddWrapper(WrapAuthenticated, ClientVersion.V2); - } - - - private TcpPackage WrapCheckpointReached(ClientMessage.CheckpointReached msg) - { - var dto = new CheckpointReached(msg.Position.Value.CommitPosition, - msg.Position.Value.PreparePosition); - return new TcpPackage(TcpCommand.CheckpointReached, msg.CorrelationId, dto.Serialize()); - } - - private static Message UnwrapPing(TcpPackage package, IEnvelope envelope) - { - var data = new byte[package.Data.Count]; - Buffer.BlockCopy(package.Data.Array, package.Data.Offset, data, 0, package.Data.Count); - var pongMessage = new TcpMessage.PongMessage(package.CorrelationId, data); - envelope.ReplyWith(pongMessage); - return pongMessage; - } - - private static Message UnwrapIdentifyClient(TcpPackage package, IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.IdentifyClient(package.CorrelationId, dto.Version, dto.ConnectionName); - } - - private static TcpPackage WrapPong(TcpMessage.PongMessage message) - { - return new TcpPackage(TcpCommand.Pong, message.CorrelationId, message.Payload); - } - - private static ClientMessage.ReadEvent - UnwrapReadEvent(TcpPackage package, IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.ReadEvent(Guid.NewGuid(), package.CorrelationId, envelope, dto.EventStreamId, - dto.EventNumber, dto.ResolveLinkTos, dto.RequireLeader, user); - } - - private static TcpPackage WrapReadEventCompleted(ClientMessage.ReadEventCompleted msg) - { - var dto = new ReadEventCompleted( - (ReadEventCompleted.Types.ReadEventResult)msg.Result, - new ResolvedIndexedEvent(msg.Record.Event, msg.Record.Link), msg.Error); - return new TcpPackage(TcpCommand.ReadEventCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.ReadStreamEventsForward UnwrapReadStreamEventsForward(TcpPackage package, - IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.ReadStreamEventsForward(Guid.NewGuid(), package.CorrelationId, envelope, - dto.EventStreamId, dto.FromEventNumber, dto.MaxCount, - dto.ResolveLinkTos, dto.RequireLeader, null, user, - replyOnExpired: false); - } - - private static TcpPackage WrapReadStreamEventsForwardCompleted( - ClientMessage.ReadStreamEventsForwardCompleted msg) - { - var dto = new ReadStreamEventsCompleted( - ConvertToResolvedIndexedEvents(msg.Events), - (ReadStreamEventsCompleted.Types.ReadStreamResult)msg.Result, - msg.NextEventNumber, msg.LastEventNumber, msg.IsEndOfStream, msg.TfLastCommitPosition, msg.Error); - return new TcpPackage(TcpCommand.ReadStreamEventsForwardCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.ReadStreamEventsBackward UnwrapReadStreamEventsBackward(TcpPackage package, - IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.ReadStreamEventsBackward(Guid.NewGuid(), package.CorrelationId, envelope, - dto.EventStreamId, dto.FromEventNumber, dto.MaxCount, - dto.ResolveLinkTos, dto.RequireLeader, null, user); - } - - private static TcpPackage WrapReadStreamEventsBackwardCompleted( - ClientMessage.ReadStreamEventsBackwardCompleted msg) - { - var dto = new ReadStreamEventsCompleted( - ConvertToResolvedIndexedEvents(msg.Events), - (ReadStreamEventsCompleted.Types.ReadStreamResult)msg.Result, - msg.NextEventNumber, msg.LastEventNumber, msg.IsEndOfStream, msg.TfLastCommitPosition, msg.Error); - return new TcpPackage(TcpCommand.ReadStreamEventsBackwardCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ResolvedIndexedEvent[] - ConvertToResolvedIndexedEvents(IReadOnlyList events) - { - var result = new ResolvedIndexedEvent[events.Count]; - for (int i = 0; i < events.Count; ++i) - { - result[i] = new ResolvedIndexedEvent(events[i].Event, events[i].Link); - } - - return result; - } - - private static ClientMessage.ReadAllEventsForward UnwrapReadAllEventsForward(TcpPackage package, - IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.ReadAllEventsForward(Guid.NewGuid(), package.CorrelationId, envelope, - dto.CommitPosition, dto.PreparePosition, dto.MaxCount, - dto.ResolveLinkTos, dto.RequireLeader, null, user, - replyOnExpired: false, - longPollTimeout: null); - } - - - private static TcpPackage WrapReadAllEventsForwardCompleted(ClientMessage.ReadAllEventsForwardCompleted msg) - { - var dto = new ReadAllEventsCompleted( - msg.CurrentPos.CommitPosition, msg.CurrentPos.PreparePosition, ConvertToResolvedEvents(msg.Events), - msg.NextPos.CommitPosition, msg.NextPos.PreparePosition, - (ReadAllEventsCompleted.Types.ReadAllResult)msg.Result, msg.Error); - return new TcpPackage(TcpCommand.ReadAllEventsForwardCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.ReadAllEventsBackward UnwrapReadAllEventsBackward(TcpPackage package, - IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.ReadAllEventsBackward(Guid.NewGuid(), package.CorrelationId, envelope, - dto.CommitPosition, dto.PreparePosition, dto.MaxCount, - dto.ResolveLinkTos, dto.RequireLeader, null, user); - } - - private static TcpPackage WrapReadAllEventsBackwardCompleted(ClientMessage.ReadAllEventsBackwardCompleted msg) - { - var dto = new ReadAllEventsCompleted( - msg.CurrentPos.CommitPosition, msg.CurrentPos.PreparePosition, ConvertToResolvedEvents(msg.Events), - msg.NextPos.CommitPosition, msg.NextPos.PreparePosition, - (ReadAllEventsCompleted.Types.ReadAllResult)msg.Result, msg.Error); - return new TcpPackage(TcpCommand.ReadAllEventsBackwardCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.FilteredReadAllEventsForward UnwrapFilteredReadAllEventsForward(TcpPackage package, - IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - IEventFilter eventFilter = EventFilter.Get(true, dto.Filter); - - int maxSearchWindow = dto.MaxCount; - if (dto.MaxSearchWindow > 0) - { - maxSearchWindow = dto.MaxSearchWindow; - } - - return new ClientMessage.FilteredReadAllEventsForward(Guid.NewGuid(), package.CorrelationId, envelope, - dto.CommitPosition, dto.PreparePosition, dto.MaxCount, - dto.ResolveLinkTos, dto.RequireLeader, maxSearchWindow, null, eventFilter, user, - replyOnExpired: false, - longPollTimeout: null); - } - - private static TcpPackage WrapFilteredReadAllEventsForwardCompleted( - ClientMessage.FilteredReadAllEventsForwardCompleted msg) - { - var dto = new FilteredReadAllEventsCompleted( - msg.CurrentPos.CommitPosition, msg.CurrentPos.PreparePosition, ConvertToResolvedEvents(msg.Events), - msg.NextPos.CommitPosition, msg.NextPos.PreparePosition, msg.IsEndOfStream, - (FilteredReadAllEventsCompleted.Types.FilteredReadAllResult)msg.Result, msg.Error); - return new TcpPackage(TcpCommand.FilteredReadAllEventsForwardCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static Client.Messages.ResolvedEvent[] ConvertToResolvedEvents(IReadOnlyList events) - { - var result = new Client.Messages.ResolvedEvent[events.Count]; - for (int i = 0; i < events.Count; ++i) - { - result[i] = new Client.Messages.ResolvedEvent(events[i]); - } - - return result; - } - - private static ClientMessage.FilteredReadAllEventsBackward UnwrapFilteredReadAllEventsBackward( - TcpPackage package, - IEnvelope envelope, ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - IEventFilter eventFilter = EventFilter.Get(true, dto.Filter); - - int maxSearchWindow = dto.MaxCount; - if (dto.MaxSearchWindow > 0) - { - maxSearchWindow = dto.MaxSearchWindow; - } - - return new ClientMessage.FilteredReadAllEventsBackward(Guid.NewGuid(), package.CorrelationId, envelope, - dto.CommitPosition, dto.PreparePosition, dto.MaxCount, - dto.ResolveLinkTos, dto.RequireLeader, maxSearchWindow, null, eventFilter, user, null); - } - - private static TcpPackage WrapFilteredReadAllEventsBackwardCompleted( - ClientMessage.FilteredReadAllEventsBackwardCompleted msg) - { - var dto = new FilteredReadAllEventsCompleted( - msg.CurrentPos.CommitPosition, msg.CurrentPos.PreparePosition, ConvertToResolvedEvents(msg.Events), - msg.NextPos.CommitPosition, msg.NextPos.PreparePosition, msg.IsEndOfStream, - (FilteredReadAllEventsCompleted.Types.FilteredReadAllResult)msg.Result, msg.Error); - return new TcpPackage(TcpCommand.FilteredReadAllEventsBackwardCompleted, msg.CorrelationId, - dto.Serialize()); - } - - private ClientMessage.SubscribeToStream UnwrapSubscribeToStream(TcpPackage package, - IEnvelope envelope, - ClaimsPrincipal user, - TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.SubscribeToStream(Guid.NewGuid(), package.CorrelationId, envelope, - connection.ConnectionId, dto.EventStreamId, dto.ResolveLinkTos, user); - } - - private ClientMessage.FilteredSubscribeToStream UnwrapFilteredSubscribeToStream(TcpPackage package, - IEnvelope envelope, - ClaimsPrincipal user, - TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - IEventFilter eventFilter = EventFilter.Get(dto.EventStreamId.IsEmptyString(), dto.Filter); - - return new ClientMessage.FilteredSubscribeToStream(Guid.NewGuid(), package.CorrelationId, envelope, - connection.ConnectionId, dto.EventStreamId, dto.ResolveLinkTos, user, eventFilter, - dto.CheckpointInterval, checkpointIntervalCurrent: 0); - } - - private ClientMessage.UnsubscribeFromStream UnwrapUnsubscribeFromStream(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.UnsubscribeFromStream(Guid.NewGuid(), package.CorrelationId, envelope, user); - } - - private TcpPackage WrapSubscribedToStream(ClientMessage.SubscriptionConfirmation msg) - { - var dto = new SubscriptionConfirmation(msg.LastIndexedPosition, msg.LastEventNumber ?? 0); - return new TcpPackage(TcpCommand.SubscriptionConfirmation, msg.CorrelationId, dto.Serialize()); - } - - private ClientMessage.CreatePersistentSubscriptionToStream UnwrapCreatePersistentSubscription( - TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - var namedConsumerStrategy = dto.NamedConsumerStrategy; - if (string.IsNullOrEmpty(namedConsumerStrategy)) - { - namedConsumerStrategy = dto.PreferRoundRobin - ? SystemConsumerStrategies.RoundRobin - : SystemConsumerStrategies.DispatchToSingle; - } - - return new ClientMessage.CreatePersistentSubscriptionToStream(Guid.NewGuid(), package.CorrelationId, envelope, - dto.EventStreamId, dto.SubscriptionGroupName, dto.ResolveLinkTos, dto.StartFrom, - dto.MessageTimeoutMilliseconds, - dto.RecordStatistics, dto.MaxRetryCount, dto.BufferSize, dto.LiveBufferSize, - dto.ReadBatchSize, dto.CheckpointAfterTime, dto.CheckpointMinCount, - dto.CheckpointMaxCount, dto.SubscriberMaxCount, namedConsumerStrategy, - user); - } - - private ClientMessage.UpdatePersistentSubscriptionToStream UnwrapUpdatePersistentSubscription( - TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - var namedConsumerStrategy = dto.NamedConsumerStrategy; - if (string.IsNullOrEmpty(namedConsumerStrategy)) - { - namedConsumerStrategy = dto.PreferRoundRobin - ? SystemConsumerStrategies.RoundRobin - : SystemConsumerStrategies.DispatchToSingle; - } - - return new ClientMessage.UpdatePersistentSubscriptionToStream(Guid.NewGuid(), package.CorrelationId, envelope, - dto.EventStreamId, dto.SubscriptionGroupName, dto.ResolveLinkTos, dto.StartFrom, - dto.MessageTimeoutMilliseconds, - dto.RecordStatistics, dto.MaxRetryCount, dto.BufferSize, dto.LiveBufferSize, - dto.ReadBatchSize, dto.CheckpointAfterTime, dto.CheckpointMinCount, - dto.CheckpointMaxCount, dto.SubscriberMaxCount, namedConsumerStrategy, - user); - } - - private ClientMessage.DeletePersistentSubscriptionToStream UnwrapDeletePersistentSubscription( - TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.DeletePersistentSubscriptionToStream(Guid.NewGuid(), package.CorrelationId, envelope, - dto.EventStreamId, dto.SubscriptionGroupName, user); - } - - private TcpPackage WrapDeletePersistentSubscriptionCompleted( - ClientMessage.DeletePersistentSubscriptionToStreamCompleted msg) - { - var dto = new DeletePersistentSubscriptionCompleted( - (DeletePersistentSubscriptionCompleted.Types.DeletePersistentSubscriptionResult)msg - .Result, msg.Reason); - return new TcpPackage(TcpCommand.DeletePersistentSubscriptionCompleted, msg.CorrelationId, dto.Serialize()); - } - - private TcpPackage WrapCreatePersistentSubscriptionCompleted( - ClientMessage.CreatePersistentSubscriptionToStreamCompleted msg) - { - var dto = new CreatePersistentSubscriptionCompleted( - (CreatePersistentSubscriptionCompleted.Types.CreatePersistentSubscriptionResult)msg - .Result, msg.Reason); - return new TcpPackage(TcpCommand.CreatePersistentSubscriptionCompleted, msg.CorrelationId, dto.Serialize()); - } - - private TcpPackage WrapUpdatePersistentSubscriptionCompleted( - ClientMessage.UpdatePersistentSubscriptionToStreamCompleted msg) - { - var dto = new UpdatePersistentSubscriptionCompleted( - (UpdatePersistentSubscriptionCompleted.Types.UpdatePersistentSubscriptionResult)msg - .Result, msg.Reason); - return new TcpPackage(TcpCommand.UpdatePersistentSubscriptionCompleted, msg.CorrelationId, dto.Serialize()); - } - - - private ClientMessage.ConnectToPersistentSubscriptionToStream UnwrapConnectToPersistentSubscription( - TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.ConnectToPersistentSubscriptionToStream(Guid.NewGuid(), package.CorrelationId, - envelope, - connection.ConnectionId, connection.ClientConnectionName, dto.SubscriptionId, dto.EventStreamId, - dto.AllowedInFlightMessages, - connection.RemoteEndPoint.ToString(), user); - } - - private ClientMessage.PersistentSubscriptionAckEvents UnwrapPersistentSubscriptionAckEvents( - TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.PersistentSubscriptionAckEvents( - Guid.NewGuid(), package.CorrelationId, envelope, dto.SubscriptionId, - dto.ProcessedEventIds.Select(x => new Guid(x.ToByteArray())).ToArray(), user); - } - - private ClientMessage.PersistentSubscriptionNackEvents UnwrapPersistentSubscriptionNackEvents( - TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, TcpConnectionManager connection) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.PersistentSubscriptionNackEvents( - Guid.NewGuid(), package.CorrelationId, envelope, dto.SubscriptionId, - dto.Message, (ClientMessage.PersistentSubscriptionNackEvents.NakAction)dto.Action, - dto.ProcessedEventIds.Select(x => new Guid(x.ToByteArray())).ToArray(), user); - } - - private TcpPackage - WrapPersistentSubscriptionConfirmation(ClientMessage.PersistentSubscriptionConfirmation msg) - { - var dto = new PersistentSubscriptionConfirmation(msg.LastIndexedPosition, - msg.SubscriptionId, msg.LastEventNumber ?? 0); - return new TcpPackage(TcpCommand.PersistentSubscriptionConfirmation, msg.CorrelationId, dto.Serialize()); - } - - private TcpPackage WrapPersistentSubscriptionStreamEventAppeared( - ClientMessage.PersistentSubscriptionStreamEventAppeared msg) - { - var dto = new PersistentSubscriptionStreamEventAppeared( - new ResolvedIndexedEvent(msg.Event.Event, msg.Event.Link), msg.RetryCount); - return new TcpPackage(TcpCommand.PersistentSubscriptionStreamEventAppeared, msg.CorrelationId, - dto.Serialize()); - } - - private TcpPackage WrapStreamEventAppeared(ClientMessage.StreamEventAppeared msg) - { - var dto = new StreamEventAppeared(new Client.Messages.ResolvedEvent(msg.Event)); - return new TcpPackage(TcpCommand.StreamEventAppeared, msg.CorrelationId, dto.Serialize()); - } - - private TcpPackage WrapSubscriptionDropped(ClientMessage.SubscriptionDropped msg) - { - var dto = new SubscriptionDropped( - (SubscriptionDropped.Types.SubscriptionDropReason)msg.Reason); - return new TcpPackage(TcpCommand.SubscriptionDropped, msg.CorrelationId, dto.Serialize()); - } - - private ClientMessage.ScavengeDatabase UnwrapScavengeDatabase(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - return new ClientMessage.ScavengeDatabase(envelope, package.CorrelationId, user, 0, 1, null, null, false); - } - - private TcpPackage WrapScavengeDatabaseResponse(Message msg) - { - ScavengeDatabaseResponse.Types.ScavengeResult result; - string scavengeId; - Guid correlationId; - - switch (msg) - { - case ClientMessage.ScavengeDatabaseStartedResponse startedResponse: - result = ScavengeDatabaseResponse.Types.ScavengeResult.Started; - scavengeId = startedResponse.ScavengeId; - correlationId = startedResponse.CorrelationId; - break; - case ClientMessage.ScavengeDatabaseInProgressResponse inProgressResponse: - result = ScavengeDatabaseResponse.Types.ScavengeResult.InProgress; - scavengeId = inProgressResponse.ScavengeId; - correlationId = inProgressResponse.CorrelationId; - break; - case ClientMessage.ScavengeDatabaseUnauthorizedResponse unauthorizedResponse: - result = ScavengeDatabaseResponse.Types.ScavengeResult.Unauthorized; - scavengeId = unauthorizedResponse.ScavengeId; - correlationId = unauthorizedResponse.CorrelationId; - break; - default: - throw new ArgumentOutOfRangeException(); - } - - var dto = new ScavengeDatabaseResponse(result, scavengeId); - return new TcpPackage(TcpCommand.ScavengeDatabaseResponse, correlationId, dto.Serialize()); - } - - private TcpPackage WrapAuthenticated(TcpMessage.Authenticated msg) - { - return new TcpPackage(TcpCommand.Authenticated, msg.CorrelationId, Empty.ByteArray); - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/ClientWriteTcpDispatcher.cs b/src/EventStore.Core/Services/Transport/Tcp/ClientWriteTcpDispatcher.cs deleted file mode 100644 index 3b8bf388c8..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/ClientWriteTcpDispatcher.cs +++ /dev/null @@ -1,439 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Security.Claims; -using System.Security.Principal; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Authentication; -using EventStore.Core.Authentication.DelegatedAuthentication; -using EventStore.Core.Data; -using EventStore.Core.Helpers; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Util; -using Google.Protobuf; -using Microsoft.Diagnostics.Tracing.Parsers.IIS_Trace; -using OperationResult = EventStore.Core.Messages.OperationResult; - -namespace EventStore.Core.Services.Transport.Tcp -{ - public enum ClientVersion : byte - { - V1 = 0, - V2 = 1 - } - - public class ClientWriteTcpDispatcher : TcpDispatcher - { - private readonly TimeSpan _writeTimeout; - - protected ClientWriteTcpDispatcher(TimeSpan writeTimeout) - { - _writeTimeout = writeTimeout; - AddUnwrapper(TcpCommand.WriteEvents, UnwrapWriteEvents, ClientVersion.V2); - AddWrapper(WrapWriteEvents, ClientVersion.V2); - AddUnwrapper(TcpCommand.WriteEventsCompleted, UnwrapWriteEventsCompleted, ClientVersion.V2); - AddWrapper(WrapWriteEventsCompleted, ClientVersion.V2); - - AddUnwrapper(TcpCommand.TransactionStart, UnwrapTransactionStart, ClientVersion.V2); - AddWrapper(WrapTransactionStart, ClientVersion.V2); - AddUnwrapper(TcpCommand.TransactionStartCompleted, UnwrapTransactionStartCompleted, ClientVersion.V2); - AddWrapper(WrapTransactionStartCompleted, ClientVersion.V2); - - AddUnwrapper(TcpCommand.TransactionWrite, UnwrapTransactionWrite, ClientVersion.V2); - AddWrapper(WrapTransactionWrite, ClientVersion.V2); - AddUnwrapper(TcpCommand.TransactionWriteCompleted, UnwrapTransactionWriteCompleted, ClientVersion.V2); - AddWrapper(WrapTransactionWriteCompleted, ClientVersion.V2); - - AddUnwrapper(TcpCommand.TransactionCommit, UnwrapTransactionCommit, ClientVersion.V2); - AddWrapper(WrapTransactionCommit, ClientVersion.V2); - AddUnwrapper(TcpCommand.TransactionCommitCompleted, UnwrapTransactionCommitCompleted, ClientVersion.V2); - AddWrapper(WrapTransactionCommitCompleted, ClientVersion.V2); - - AddUnwrapper(TcpCommand.DeleteStream, UnwrapDeleteStream, ClientVersion.V2); - AddWrapper(WrapDeleteStream, ClientVersion.V2); - AddUnwrapper(TcpCommand.DeleteStreamCompleted, UnwrapDeleteStreamCompleted, ClientVersion.V2); - AddWrapper(WrapDeleteStreamCompleted, ClientVersion.V2); - - AddUnwrapper(TcpCommand.NotHandled, UnwrapNotHandled, ClientVersion.V2); - AddWrapper(WrapNotHandled, ClientVersion.V2); - - AddUnwrapper(TcpCommand.NotAuthenticated, UnwrapNotAuthenticated, ClientVersion.V2); - AddWrapper(WrapNotAuthenticated, ClientVersion.V2); - } - private ClientMessage.WriteEvents UnwrapWriteEvents(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - var events = new Event[dto.Events.Count]; - for (int i = 0; i < events.Length; ++i) - { - // ReSharper disable PossibleNullReferenceException - var e = dto.Events[i]; - // ReSharper restore PossibleNullReferenceException - events[i] = new Event(new Guid(e.EventId.ToByteArray()), e.EventType, e.DataContentType == 1, e.Data.ToByteArray(), e.Metadata.ToByteArray()); - } - - var cts = new CancellationTokenSource(); - var envelopeWrapper = new CallbackEnvelope(OnMessage); - cts.CancelAfter(_writeTimeout); - - return new ClientMessage.WriteEvents(Guid.NewGuid(), package.CorrelationId, envelopeWrapper, dto.RequireLeader, - dto.EventStreamId, dto.ExpectedVersion, events, user, package.Tokens, cts.Token); - - void OnMessage(Message m) - { - cts.Dispose(); - envelope.ReplyWith(m); - } - } - - private static TcpPackage WrapWriteEvents(ClientMessage.WriteEvents msg) - { - var events = new NewEvent[msg.Events.Length]; - for (int i = 0; i < events.Length; ++i) - { - var e = msg.Events[i]; - events[i] = new NewEvent(e.EventId.ToByteArray(), - e.EventType, - e.IsJson ? 1 : 0, - 0, e.Data, - e.Metadata); - } - - var dto = new WriteEvents(msg.EventStreamId, msg.ExpectedVersion, events, - msg.RequireLeader); - return CreateWriteRequestPackage(TcpCommand.WriteEvents, msg, dto); - } - - private static TcpPackage CreateWriteRequestPackage(TcpCommand command, ClientMessage.WriteRequestMessage msg, - T dto) where T : IMessage - { - // we forwarding with InternalCorrId, not client's CorrelationId!!! - if (msg.User == UserManagement.SystemAccounts.System) - { - return new TcpPackage(command, TcpFlags.TrustedWrite, msg.InternalCorrId, null, null, dto.Serialize()); - } - - foreach (var identity in msg.User.Identities) - { - if (!(identity is DelegatedClaimsIdentity dci)) - { - continue; - } - - var jwtClaim = dci.FindFirst(AuthenticationTokenKeys.Jwt); - if (jwtClaim != null) - { - return new TcpPackage(command, TcpFlags.Authenticated, msg.InternalCorrId, jwtClaim.Value, - dto.Serialize()); - } - - var uidClaim = dci.FindFirst(AuthenticationTokenKeys.Username); - var pwdClaim = dci.FindFirst(AuthenticationTokenKeys.Password); - - if (uidClaim != null && pwdClaim != null) - { - return new TcpPackage(command, TcpFlags.Authenticated, msg.InternalCorrId, uidClaim.Value, - pwdClaim.Value, dto.Serialize()); - } - } - - return msg.Login != null && msg.Password != null - ? new TcpPackage(command, TcpFlags.Authenticated, msg.InternalCorrId, msg.Login, msg.Password, - dto.Serialize()) - : new TcpPackage(command, TcpFlags.None, msg.InternalCorrId, null, null, dto.Serialize()); - } - - private static ClientMessage.WriteEventsCompleted UnwrapWriteEventsCompleted(TcpPackage package, - IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - if (dto.Result == Client.Messages.OperationResult.Success) - { - return new ClientMessage.WriteEventsCompleted(package.CorrelationId, - dto.FirstEventNumber, - dto.LastEventNumber, - dto.PreparePosition, - dto.CommitPosition); - } - - return new ClientMessage.WriteEventsCompleted(package.CorrelationId, - (OperationResult)dto.Result, - dto.Message, - dto.CurrentVersion); - } - - private static TcpPackage WrapWriteEventsCompleted(ClientMessage.WriteEventsCompleted msg) - { - var dto = new WriteEventsCompleted((Client.Messages.OperationResult)msg.Result, - msg.Message, - msg.FirstEventNumber, - msg.LastEventNumber, - msg.PreparePosition, - msg.CommitPosition, - msg.CurrentVersion); - return new TcpPackage(TcpCommand.WriteEventsCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.TransactionStart UnwrapTransactionStart(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.TransactionStart(Guid.NewGuid(), package.CorrelationId, envelope, - dto.RequireLeader, - dto.EventStreamId, dto.ExpectedVersion, user, package.Tokens); - } - - private static TcpPackage WrapTransactionStart(ClientMessage.TransactionStart msg) - { - var dto = new TransactionStart(msg.EventStreamId, msg.ExpectedVersion, - msg.RequireLeader); - return CreateWriteRequestPackage(TcpCommand.TransactionStart, msg, dto); - } - - private static ClientMessage.TransactionStartCompleted UnwrapTransactionStartCompleted(TcpPackage package, - IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.TransactionStartCompleted(package.CorrelationId, dto.TransactionId, - (OperationResult)dto.Result, dto.Message); - } - - private static TcpPackage WrapTransactionStartCompleted(ClientMessage.TransactionStartCompleted msg) - { - var dto = new TransactionStartCompleted(msg.TransactionId, - (Client.Messages.OperationResult)msg.Result, msg.Message); - return new TcpPackage(TcpCommand.TransactionStartCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.TransactionWrite UnwrapTransactionWrite(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - var events = new Event[dto.Events.Count]; - for (int i = 0; i < events.Length; ++i) - { - // ReSharper disable PossibleNullReferenceException - var e = dto.Events[i]; - // ReSharper restore PossibleNullReferenceException - events[i] = new Event(new Guid(e.EventId.ToByteArray()), e.EventType, e.DataContentType == 1, e.Data.ToByteArray(), e.Metadata.ToByteArray()); - } - - return new ClientMessage.TransactionWrite(Guid.NewGuid(), package.CorrelationId, envelope, - dto.RequireLeader, - dto.TransactionId, events, user, package.Tokens); - } - - private static TcpPackage WrapTransactionWrite(ClientMessage.TransactionWrite msg) - { - var events = new NewEvent[msg.Events.Length]; - for (int i = 0; i < events.Length; ++i) - { - var e = msg.Events[i]; - events[i] = new NewEvent(e.EventId.ToByteArray(), e.EventType, e.IsJson ? 1 : 0, 0, - e.Data, e.Metadata); - } - - var dto = new TransactionWrite(msg.TransactionId, events, msg.RequireLeader); - return CreateWriteRequestPackage(TcpCommand.TransactionWrite, msg, dto); - } - - private static ClientMessage.TransactionWriteCompleted UnwrapTransactionWriteCompleted(TcpPackage package, - IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.TransactionWriteCompleted(package.CorrelationId, dto.TransactionId, - (OperationResult)dto.Result, dto.Message); - } - - private static TcpPackage WrapTransactionWriteCompleted(ClientMessage.TransactionWriteCompleted msg) - { - var dto = new TransactionWriteCompleted(msg.TransactionId, - (Client.Messages.OperationResult)msg.Result, msg.Message); - return new TcpPackage(TcpCommand.TransactionWriteCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.TransactionCommit UnwrapTransactionCommit(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.TransactionCommit(Guid.NewGuid(), package.CorrelationId, envelope, - dto.RequireLeader, dto.TransactionId, user, package.Tokens); - } - - private static TcpPackage WrapTransactionCommit(ClientMessage.TransactionCommit msg) - { - var dto = new TransactionCommit(msg.TransactionId, msg.RequireLeader); - return CreateWriteRequestPackage(TcpCommand.TransactionCommit, msg, dto); - } - - private static ClientMessage.TransactionCommitCompleted UnwrapTransactionCommitCompleted(TcpPackage package, - IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - if (dto.Result == Client.Messages.OperationResult.Success) - { - return new ClientMessage.TransactionCommitCompleted(package.CorrelationId, dto.TransactionId, - dto.FirstEventNumber, dto.LastEventNumber, dto.PreparePosition, dto.CommitPosition); - } - - return new ClientMessage.TransactionCommitCompleted(package.CorrelationId, dto.TransactionId, - (OperationResult)dto.Result, dto.Message); - } - - private static TcpPackage WrapTransactionCommitCompleted(ClientMessage.TransactionCommitCompleted msg) - { - var dto = new TransactionCommitCompleted(msg.TransactionId, - (Client.Messages.OperationResult)msg.Result, - msg.Message, msg.FirstEventNumber, msg.LastEventNumber, msg.PreparePosition, msg.CommitPosition); - return new TcpPackage(TcpCommand.TransactionCommitCompleted, msg.CorrelationId, dto.Serialize()); - } - - private ClientMessage.DeleteStream UnwrapDeleteStream(TcpPackage package, IEnvelope envelope, - ClaimsPrincipal user) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - var cts = new CancellationTokenSource(); - var envelopeWrapper = new CallbackEnvelope(OnMessage); - cts.CancelAfter(_writeTimeout); - - return new ClientMessage.DeleteStream(Guid.NewGuid(), package.CorrelationId, envelopeWrapper, dto.RequireLeader, - dto.EventStreamId, dto.ExpectedVersion, dto.HardDelete, user, package.Tokens, cts.Token); - - void OnMessage(Message m) - { - cts.Dispose(); - envelope.ReplyWith(m); - } - } - - private static TcpPackage WrapDeleteStream(ClientMessage.DeleteStream msg) - { - var dto = new DeleteStream(msg.EventStreamId, msg.ExpectedVersion, msg.RequireLeader, - msg.HardDelete); - return CreateWriteRequestPackage(TcpCommand.DeleteStream, msg, dto); - } - - private static ClientMessage.DeleteStreamCompleted UnwrapDeleteStreamCompleted(TcpPackage package, - IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - return new ClientMessage.DeleteStreamCompleted(package.CorrelationId, (OperationResult)dto.Result, - dto.Message, - dto.CurrentVersion, - dto.PreparePosition, - dto.CommitPosition); - } - - private static TcpPackage WrapDeleteStreamCompleted(ClientMessage.DeleteStreamCompleted msg) - { - var dto = new DeleteStreamCompleted((Client.Messages.OperationResult)msg.Result, - msg.Message, - msg.CurrentVersion, - msg.PreparePosition, - msg.CommitPosition); - return new TcpPackage(TcpCommand.DeleteStreamCompleted, msg.CorrelationId, dto.Serialize()); - } - - private static ClientMessage.NotHandled UnwrapNotHandled(TcpPackage package, IEnvelope envelope) - { - var dto = package.Data.Deserialize(); - if (dto == null) - { - return null; - } - - var reason = dto.Reason switch - { - NotHandled.Types.NotHandledReason.NotReady => ClientMessage.NotHandled.Types.NotHandledReason.NotReady, - NotHandled.Types.NotHandledReason.TooBusy => ClientMessage.NotHandled.Types.NotHandledReason.TooBusy, - NotHandled.Types.NotHandledReason.NotLeader => ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, - NotHandled.Types.NotHandledReason.IsReadOnly => ClientMessage.NotHandled.Types.NotHandledReason.IsReadOnly, - _ => ClientMessage.NotHandled.Types.NotHandledReason.NotReady - }; - var leaderInfoDto = dto.AdditionalInfo.IsEmpty - ? null - : dto.AdditionalInfo.ToByteArray().Deserialize(); - - var leaderInfo = leaderInfoDto switch - { - { ExternalTcpAddress.Length: > 0 } => new ClientMessage.NotHandled.Types.LeaderInfo( - new DnsEndPoint(leaderInfoDto.ExternalTcpAddress, leaderInfoDto.ExternalTcpPort), false, - new DnsEndPoint(leaderInfoDto.HttpAddress, leaderInfoDto.HttpPort)), - { ExternalSecureTcpAddress.Length: > 0 } => new ClientMessage.NotHandled.Types.LeaderInfo( - new DnsEndPoint(leaderInfoDto.ExternalSecureTcpAddress, leaderInfoDto.ExternalSecureTcpPort), true, - new DnsEndPoint(leaderInfoDto.HttpAddress, leaderInfoDto.HttpPort)), - _ => null - }; - return new ClientMessage.NotHandled(package.CorrelationId, reason, leaderInfo); - } - - private static TcpPackage WrapNotHandled(ClientMessage.NotHandled msg) - { - var dto = new Client.Messages.NotHandled(msg); - return new TcpPackage(TcpCommand.NotHandled, msg.CorrelationId, dto.Serialize()); - } - - private static TcpMessage.NotAuthenticated UnwrapNotAuthenticated(TcpPackage package, IEnvelope envelope) => - new(package.CorrelationId, - Helper.UTF8NoBom.GetString(package.Data.Array, package.Data.Offset, package.Data.Count)); - - private static TcpPackage WrapNotAuthenticated(TcpMessage.NotAuthenticated msg) => - new(TcpCommand.NotAuthenticated, msg.CorrelationId, - Helper.UTF8NoBom.GetBytes(msg.Reason ?? string.Empty)); - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/ITcpDispatcher.cs b/src/EventStore.Core/Services/Transport/Tcp/ITcpDispatcher.cs deleted file mode 100644 index d629eb6ab7..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/ITcpDispatcher.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using System.Security.Claims; -using EventStore.Core.Messaging; - -namespace EventStore.Core.Services.Transport.Tcp -{ - public interface ITcpDispatcher - { - TcpPackage? WrapMessage(Message message, byte version); - - Message UnwrapPackage(TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, - IReadOnlyDictionary tokens, TcpConnectionManager connection, byte version); - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/ProtobufExtensions.cs b/src/EventStore.Core/Services/Transport/Tcp/ProtobufExtensions.cs deleted file mode 100644 index 4a2e7bc89e..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/ProtobufExtensions.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Threading; -using Google.Protobuf; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Services.Transport.Tcp -{ - public static class ProtobufExtensions - { - private static readonly ConcurrentStack _streams; - - static ProtobufExtensions() - { - _streams = new ConcurrentStack(); - for (var i = 0; i < 300; i++) - { - _streams.Push(new MemoryStream(2048)); - } - } - - private static readonly ILogger Log = - Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "ProtobufExtensions"); - - static MemoryStream AcquireStream() - { - for (var i = 0; i < 1000; i++) - { - MemoryStream ret; - if (_streams.TryPop(out ret)) - { - ret.SetLength(0); - return ret; - } - - if ((i + 1) % 5 == 0) - { - Thread.Sleep(1); //need to do better than this - } - } - - throw new UnableToAcquireStreamException(); - } - - static void ReleaseStream(MemoryStream stream) - { - _streams.Push(stream); - } - - public static T Deserialize(this byte[] data) where T : IMessage, new() - { - return Deserialize(new ArraySegment(data)); - } - - public static T Deserialize(this ArraySegment data) where T : IMessage, new() - { - try - { - using (var memory = new MemoryStream(data.Array, data.Offset, data.Count) - ) //uses original buffer as memory - { - var res = new T(); - res.MergeFrom(memory); - return res; - } - } - catch (Exception e) - { - Log.Information(e, "Deserialization to {type} failed", typeof(T).FullName); - return default(T); - } - } - - public static ArraySegment Serialize(this T protoContract) where T : IMessage - { - MemoryStream stream = null; - try - { - stream = AcquireStream(); - protoContract.WriteTo(stream); - var res = new ArraySegment(stream.ToArray(), 0, (int)stream.Length); - return res; - } - finally - { - if (stream != null) - { - ReleaseStream(stream); - } - } - } - - public static byte[] SerializeToArray(this T protoContract) where T : IMessage - { - MemoryStream stream = null; - try - { - stream = AcquireStream(); - protoContract.WriteTo(stream); - return stream.ToArray(); - } - finally - { - if (stream != null) - { - ReleaseStream(stream); - } - } - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/SendOverTcpEnvelope.cs b/src/EventStore.Core/Services/Transport/Tcp/SendOverTcpEnvelope.cs deleted file mode 100644 index 2eeb6d6c87..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/SendOverTcpEnvelope.cs +++ /dev/null @@ -1,29 +0,0 @@ -using EventStore.Common.Utils; -using EventStore.Core.Bus; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; - -namespace EventStore.Core.Services.Transport.Tcp -{ - public class SendOverTcpEnvelope : IEnvelope - { - private readonly IPublisher _networkSendQueue; - private readonly TcpConnectionManager _manager; - - public SendOverTcpEnvelope(TcpConnectionManager manager, IPublisher networkSendQueue) - { - Ensure.NotNull(manager, "manager"); - Ensure.NotNull(networkSendQueue, "networkSendQueue"); - _networkSendQueue = networkSendQueue; - _manager = manager; - } - - public void ReplyWith(T message) where T : Message - { - if (_manager != null && !_manager.IsClosed) - { - _networkSendQueue.Publish(new TcpMessage.TcpSend(_manager, message)); - } - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/TcpCommand.cs b/src/EventStore.Core/Services/Transport/Tcp/TcpCommand.cs deleted file mode 100644 index d36a90d10e..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/TcpCommand.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace EventStore.Core.Services.Transport.Tcp -{ - public enum TcpCommand : byte - { - HeartbeatRequestCommand = 0x01, - HeartbeatResponseCommand = 0x02, - - Ping = 0x03, - Pong = 0x04, - - // CLIENT COMMANDS - // CreateStream = 0x80, - // CreateStreamCompleted = 0x81, - - WriteEvents = 0x82, - WriteEventsCompleted = 0x83, - - TransactionStart = 0x84, - TransactionStartCompleted = 0x85, - TransactionWrite = 0x86, - TransactionWriteCompleted = 0x87, - TransactionCommit = 0x88, - TransactionCommitCompleted = 0x89, - - DeleteStream = 0x8A, - DeleteStreamCompleted = 0x8B, - - ReadEvent = 0xB0, - ReadEventCompleted = 0xB1, - ReadStreamEventsForward = 0xB2, - ReadStreamEventsForwardCompleted = 0xB3, - ReadStreamEventsBackward = 0xB4, - ReadStreamEventsBackwardCompleted = 0xB5, - ReadAllEventsForward = 0xB6, - ReadAllEventsForwardCompleted = 0xB7, - ReadAllEventsBackward = 0xB8, - ReadAllEventsBackwardCompleted = 0xB9, - FilteredReadAllEventsForward = 0xBA, - FilteredReadAllEventsForwardCompleted = 0xBB, - FilteredReadAllEventsBackward = 0xBC, - FilteredReadAllEventsBackwardCompleted = 0xBE, - - SubscribeToStream = 0xC0, - SubscriptionConfirmation = 0xC1, - StreamEventAppeared = 0xC2, - UnsubscribeFromStream = 0xC3, - SubscriptionDropped = 0xC4, - ConnectToPersistentSubscription = 0xC5, - PersistentSubscriptionConfirmation = 0xC6, - PersistentSubscriptionStreamEventAppeared = 0xC7, - CreatePersistentSubscription = 0xC8, - CreatePersistentSubscriptionCompleted = 0xC9, - DeletePersistentSubscription = 0xCA, - DeletePersistentSubscriptionCompleted = 0xCB, - PersistentSubscriptionAckEvents = 0xCC, - PersistentSubscriptionNakEvents = 0xCD, - UpdatePersistentSubscription = 0xCE, - UpdatePersistentSubscriptionCompleted = 0xCF, - FilteredSubscribeToStream = 0xD2, - CheckpointReached = 0xD3, - - ScavengeDatabase = 0xD0, - ScavengeDatabaseResponse = 0xD1, - - BadRequest = 0xF0, - NotHandled = 0xF1, - Authenticate = 0xF2, - Authenticated = 0xF3, - NotAuthenticated = 0xF4, - IdentifyClient = 0xF5, - ClientIdentified = 0xF6, - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/TcpConnectionManager.cs b/src/EventStore.Core/Services/Transport/Tcp/TcpConnectionManager.cs deleted file mode 100644 index 035825907b..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/TcpConnectionManager.cs +++ /dev/null @@ -1,682 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Claims; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Bus; -using EventStore.Core.Messages; -using EventStore.Core.Messaging; -using EventStore.Core.Services.TimerService; -using EventStore.Core.Settings; -using EventStore.Plugins.Authentication; -using EventStore.Transport.Tcp; -using EventStore.Transport.Tcp.Framing; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Services.Transport.Tcp -{ - /// - /// Manager for individual TCP connection. It handles connection lifecycle, - /// heartbeats, message framing and dispatch to the memory bus. - /// - public class TcpConnectionManager : IHandle, IHandle - { - public static readonly TimeSpan ConnectionTimeout = TimeSpan.FromMilliseconds(1000); - - private static readonly ILogger Log = Serilog.Log.ForContext(); - private static readonly ClaimsPrincipal Anonymous = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Anonymous, ""), })); - public readonly Guid ConnectionId; - public readonly string ConnectionName; - public readonly EndPoint RemoteEndPoint; - - public EndPoint LocalEndPoint - { - get { return _connection.LocalEndPoint; } - } - - public bool IsClosed - { - get { return _isClosed != 0; } - } - - public int SendQueueSize - { - get { return _connection.SendQueueSize; } - } - - public string ClientConnectionName - { - get { return _clientConnectionName; } - } - - private readonly ITcpConnection _connection; - private readonly IEnvelope _tcpEnvelope; - private readonly IPublisher _publisher; - private readonly ITcpDispatcher _dispatcher; - private readonly IMessageFramer> _framer; - private long _receiveProgressIndicator => _connection?.TotalBytesReceived ?? 0L; - private long _sendProgressIndicator => _connection?.TotalBytesSent ?? 0L; - private int _isClosed; - private string _clientConnectionName; - - private byte _version; - - private readonly Action _connectionClosed; - private readonly Action _connectionEstablished; - - private readonly SendToWeakThisEnvelope _weakThisEnvelope; - private readonly TimeSpan _heartbeatInterval; - private readonly TimeSpan _heartbeatTimeout; - private bool _awaitingHeartbeatTimeoutCheck; - private readonly int _connectionPendingSendBytesThreshold; - private readonly int _connectionQueueSizeThreshold; - - private readonly IAuthenticationProvider _authProvider; - private readonly AuthorizationGateway _authorization; - private UserCredentials _defaultUser; - private TcpServiceType _serviceType; - - public TcpConnectionManager(string connectionName, - TcpServiceType serviceType, - ITcpDispatcher dispatcher, - IPublisher publisher, - ITcpConnection openedConnection, - IPublisher networkSendQueue, - IAuthenticationProvider authProvider, - AuthorizationGateway authorization, - TimeSpan heartbeatInterval, - TimeSpan heartbeatTimeout, - Action onConnectionClosed, - int connectionPendingSendBytesThreshold, - int connectionQueueSizeThreshold) - { - Ensure.NotNull(dispatcher, "dispatcher"); - Ensure.NotNull(publisher, "publisher"); - Ensure.NotNull(openedConnection, "openedConnnection"); - Ensure.NotNull(networkSendQueue, "networkSendQueue"); - Ensure.NotNull(authProvider, "authProvider"); - Ensure.NotNull(authorization, "authorization"); - ConnectionId = openedConnection.ConnectionId; - ConnectionName = connectionName; - - _serviceType = serviceType; - _tcpEnvelope = new SendOverTcpEnvelope(this, networkSendQueue); - _publisher = publisher; - _dispatcher = dispatcher; - _authProvider = authProvider; - _authorization = authorization; - - _framer = new LengthPrefixMessageFramer(); - _framer.RegisterMessageArrivedCallback(OnMessageArrived); - - _weakThisEnvelope = new SendToWeakThisEnvelope(this); - _heartbeatInterval = heartbeatInterval; - _heartbeatTimeout = heartbeatTimeout; - _connectionPendingSendBytesThreshold = connectionPendingSendBytesThreshold; - _connectionQueueSizeThreshold = connectionQueueSizeThreshold; - - _connectionClosed = onConnectionClosed; - - RemoteEndPoint = openedConnection.RemoteEndPoint; - _connection = openedConnection; - _connection.ConnectionClosed += OnConnectionClosed; - if (_connection.IsClosed) - { - OnConnectionClosed(_connection, SocketError.Success); - return; - } - - ScheduleHeartbeat(0L, 0L); - } - - public TcpConnectionManager(string connectionName, - Guid connectionId, - ITcpDispatcher dispatcher, - IPublisher publisher, - string targetHost, - string[] otherNames, - EndPoint remoteEndPoint, - TcpClientConnector connector, - bool useSsl, - CertificateDelegates.ServerCertificateValidator sslServerCertValidator, - Func sslClientCertificatesSelector, - IPublisher networkSendQueue, - IAuthenticationProvider authProvider, - AuthorizationGateway authorization, - TimeSpan heartbeatInterval, - TimeSpan heartbeatTimeout, - Action onConnectionEstablished, - Action onConnectionClosed) - { - Ensure.NotEmptyGuid(connectionId, "connectionId"); - Ensure.NotNull(dispatcher, "dispatcher"); - Ensure.NotNull(publisher, "publisher"); - Ensure.NotNull(authProvider, "authProvider"); - Ensure.NotNull(authorization, "authorization"); - Ensure.NotNull(remoteEndPoint, "remoteEndPoint"); - Ensure.NotNull(connector, "connector"); - - ConnectionId = connectionId; - ConnectionName = connectionName; - - _tcpEnvelope = new SendOverTcpEnvelope(this, networkSendQueue); - _publisher = publisher; - _dispatcher = dispatcher; - _authProvider = authProvider; - _authorization = authorization; - - _framer = new LengthPrefixMessageFramer(); - _framer.RegisterMessageArrivedCallback(OnMessageArrived); - - _weakThisEnvelope = new SendToWeakThisEnvelope(this); - _heartbeatInterval = heartbeatInterval; - _heartbeatTimeout = heartbeatTimeout; - _connectionPendingSendBytesThreshold = ESConsts.UnrestrictedPendingSendBytes; - _connectionQueueSizeThreshold = ESConsts.MaxConnectionQueueSize; - - _connectionEstablished = onConnectionEstablished; - _connectionClosed = onConnectionClosed; - - RemoteEndPoint = remoteEndPoint; - _connection = useSsl - ? connector.ConnectSslTo(ConnectionId, targetHost, otherNames, remoteEndPoint.ResolveDnsToIPAddress(), ConnectionTimeout, - sslServerCertValidator, sslClientCertificatesSelector, OnConnectionEstablished, OnConnectionFailed) - : connector.ConnectTo(ConnectionId, remoteEndPoint.ResolveDnsToIPAddress(), ConnectionTimeout, OnConnectionEstablished, - OnConnectionFailed); - _connection.ConnectionClosed += OnConnectionClosed; - if (_connection.IsClosed) - { - OnConnectionClosed(_connection, SocketError.Success); - } - } - - private void OnConnectionEstablished(ITcpConnection connection) - { - Log.Information("Connection '{connectionName}' ({connectionId:B}) to [{remoteEndPoint}] established.", - ConnectionName, ConnectionId, connection.RemoteEndPoint); - - ScheduleHeartbeat(0L, 0L); - - var handler = _connectionEstablished; - if (handler != null) - { - handler(this); - } - } - - private void OnConnectionFailed(ITcpConnection connection, SocketError socketError) - { - if (Interlocked.CompareExchange(ref _isClosed, 1, 0) != 0) - { - return; - } - - Log.Information("Connection '{connectionName}' ({connectionId:B}) to [{remoteEndPoint}] failed: {e}.", - ConnectionName, ConnectionId, connection.RemoteEndPoint, socketError); - if (_connectionClosed != null) - { - _connectionClosed(this, socketError); - } - } - - private void OnConnectionClosed(ITcpConnection connection, SocketError socketError) - { - if (Interlocked.CompareExchange(ref _isClosed, 1, 0) != 0) - { - return; - } - - Log.Information( - "Connection '{connectionName}{clientConnectionName}' [{remoteEndPoint}, {connectionId:B}] closed: {e}.", - ConnectionName, ClientConnectionName.IsEmptyString() ? string.Empty : ":" + ClientConnectionName, - connection.RemoteEndPoint, ConnectionId, socketError); - - if (_serviceType == TcpServiceType.Internal && _connection.TotalBytesSent == 0) - { - Log.Warning( - "Unable to connect to Remote EndPoint : {remoteEndPoint}. Connection is potentially blocked. Total bytes sent: {TotalBytesSent}, Total bytes received: {TotalBytesReceived}", - RemoteEndPoint, _connection.TotalBytesSent, _connection.TotalBytesReceived); - } - - if (_connectionClosed != null) - { - _connectionClosed(this, socketError); - } - } - - public void StartReceiving() - { - _connection.ReceiveAsync(OnRawDataReceived); - } - - private void OnRawDataReceived(ITcpConnection connection, IEnumerable> data) - { - try - { - _framer.UnFrameData(data); - } - catch (PackageFramingException exc) - { - SendBadRequestAndClose(Guid.Empty, - string.Format("Invalid TCP frame received. Error: {0}.", exc.Message)); - return; - } - - _connection.ReceiveAsync(OnRawDataReceived); - } - - private void OnMessageArrived(ArraySegment data) - { - TcpPackage package; - try - { - package = TcpPackage.FromArraySegment(data); - } - catch (Exception e) - { - SendBadRequestAndClose(Guid.Empty, string.Format("Received bad network package. Error: {0}", e)); - return; - } - - OnPackageReceived(package); - } - - private void OnPackageReceived(TcpPackage package) - { - try - { - ProcessPackage(package); - } - catch (Exception e) - { - SendBadRequestAndClose(package.CorrelationId, - string.Format("Error while processing package. Error: {0}", e)); - } - } - - public void ProcessPackage(TcpPackage package) - { - if (_serviceType == TcpServiceType.External && (package.Flags & TcpFlags.TrustedWrite) != 0) - { - SendBadRequestAndClose(package.CorrelationId, - "Trusted writes aren't accepted over the external TCP interface"); - return; - } - - switch (package.Command) - { - case TcpCommand.HeartbeatResponseCommand: - break; - case TcpCommand.HeartbeatRequestCommand: - SendPackage(new TcpPackage(TcpCommand.HeartbeatResponseCommand, package.CorrelationId, null)); - break; - case TcpCommand.IdentifyClient: - { - try - { - var message = - (ClientMessage.IdentifyClient)_dispatcher.UnwrapPackage(package, _tcpEnvelope, null, null, - this, _version); - Log.Information( - "Connection '{connectionName}' ({connectionId:B}) identified by client. Client connection name: '{clientConnectionName}', Client version: {clientVersion}.", - ConnectionName, ConnectionId, message.ConnectionName, (ClientVersion)message.Version); - _version = (byte)message.Version; - _clientConnectionName = message.ConnectionName; - _connection.SetClientConnectionName(_clientConnectionName); - SendPackage(new TcpPackage(TcpCommand.ClientIdentified, package.CorrelationId, null)); - } - catch (Exception ex) - { - Log.Error("Error identifying client: {e}", ex); - } - - break; - } - case TcpCommand.BadRequest: - { - var reason = string.Empty; - Helper.EatException(() => - reason = Helper.UTF8NoBom.GetString(package.Data.Array, package.Data.Offset, - package.Data.Count)); - Log.Error( - "Bad request received from '{connectionName}{clientConnectionName}' [{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]. CorrelationId: {correlationId:B}, Error: {e}.", - ConnectionName, - ClientConnectionName.IsEmptyString() ? string.Empty : ":" + ClientConnectionName, - RemoteEndPoint, - LocalEndPoint, ConnectionId, package.CorrelationId, - reason.IsEmptyString() ? "" : reason); - break; - } - case TcpCommand.Authenticate: - { - if ((package.Flags & TcpFlags.Authenticated) == 0) - { - ReplyNotAuthenticated(package.CorrelationId, "No user credentials provided."); - } - else - { - var defaultUser = new UserCredentials(package.Tokens, null); - Interlocked.Exchange(ref _defaultUser, defaultUser); - _authProvider.Authenticate(new TcpDefaultAuthRequest(this, package.CorrelationId, defaultUser)); - } - - break; - } - default: - { - var defaultUser = _defaultUser; - if ((package.Flags & TcpFlags.TrustedWrite) != 0) - { - UnwrapAndPublishPackage(package, UserManagement.SystemAccounts.System, null); - } - else if ((package.Flags & TcpFlags.Authenticated) != 0) - { - _authProvider.Authenticate(new TcpAuthRequest(this, package)); - } - else if (defaultUser != null) - { - if (defaultUser.User != null) - { - UnwrapAndPublishPackage(package, defaultUser.User, defaultUser.Tokens); - } - else - { - // The default credentials aren't authenticated - ReplyNotAuthenticated(package.CorrelationId, "Not Authenticated"); - } - } - else - { - UnwrapAndPublishPackage(package, Anonymous, null); - } - - break; - } - } - } - - private void UnwrapAndPublishPackage(TcpPackage package, ClaimsPrincipal user, IReadOnlyDictionary tokens) - { - Message message = null; - string error = ""; - try - { - message = _dispatcher.UnwrapPackage(package, _tcpEnvelope, user, tokens, this, _version); - } - catch (Exception ex) - { - error = ex.Message; - } - - if (message != null) - { - _authorization.Authorize(message, _publisher); - } - else - { - SendBadRequest(package.CorrelationId, - string.Format("Could not unwrap network package for command {0}.\n{1}", package.Command, error)); - } - } - - private void ReplyNotAuthenticated(Guid correlationId, string description) - { - _tcpEnvelope.ReplyWith(new TcpMessage.NotAuthenticated(correlationId, description)); - } - - private void ReplyNotReady(Guid correlationId, string description) - { - _tcpEnvelope.ReplyWith(new ClientMessage.NotHandled(correlationId, - ClientMessage.NotHandled.Types.NotHandledReason.NotReady, description)); - } - - private void ReplyAuthenticated(Guid correlationId, UserCredentials userCredentials, ClaimsPrincipal user) - { - var authCredentials = new UserCredentials(userCredentials.Tokens, user); - Interlocked.CompareExchange(ref _defaultUser, authCredentials, userCredentials); - _tcpEnvelope.ReplyWith(new TcpMessage.Authenticated(correlationId)); - } - - public void SendBadRequestAndClose(Guid correlationId, string message) - { - Ensure.NotNull(message, "message"); - - SendPackage(new TcpPackage(TcpCommand.BadRequest, correlationId, Helper.UTF8NoBom.GetBytes(message)), - checkQueueSize: false); - Log.Error( - "Closing connection '{connectionName}{clientConnectionName}' [{remoteEndPoint}, L{localEndPoint}, {connectionId:B}] due to error. Reason: {e}", - ConnectionName, ClientConnectionName.IsEmptyString() ? string.Empty : ":" + ClientConnectionName, - RemoteEndPoint, LocalEndPoint, ConnectionId, message); - _connection.Close(message); - } - - public void SendBadRequest(Guid correlationId, string message) - { - Ensure.NotNull(message, "message"); - - SendPackage(new TcpPackage(TcpCommand.BadRequest, correlationId, Helper.UTF8NoBom.GetBytes(message)), - checkQueueSize: false); - } - - public void Stop(string reason = null) - { - Log.Debug( - "Closing connection '{connectionName}{clientConnectionName}' [{remoteEndPoint}, L{localEndPoint}, {connectionId:B}] cleanly.{reason}", - ConnectionName, ClientConnectionName.IsEmptyString() ? string.Empty : ":" + ClientConnectionName, - RemoteEndPoint, LocalEndPoint, ConnectionId, - reason.IsEmpty() ? string.Empty : " Reason: " + reason); - _connection.Close(reason); - } - - public void SendMessage(Message message) - { - var package = _dispatcher.WrapMessage(message, _version); - if (package != null) - { - SendPackage(package.Value); - } - } - - private void SendPackage(TcpPackage package, bool checkQueueSize = true) - { - if (IsClosed) - { - return; - } - - int queueSize; - int queueSendBytes; - if (checkQueueSize) - { - if ((queueSize = _connection.SendQueueSize) > _connectionQueueSizeThreshold) - { - SendBadRequestAndClose(Guid.Empty, - string.Format("Connection queue size is too large: {0}.", queueSize)); - return; - } - - if (_connectionPendingSendBytesThreshold > ESConsts.UnrestrictedPendingSendBytes && - (queueSendBytes = _connection.PendingSendBytes) > _connectionPendingSendBytesThreshold) - { - SendBadRequestAndClose(Guid.Empty, - string.Format("Connection pending send bytes is too large: {0}.", queueSendBytes)); - return; - } - } - - var data = package.AsArraySegment(); - var framed = _framer.FrameData(data); - _connection.EnqueueSend(framed); - } - - public void Handle(TcpMessage.Heartbeat message) - { - if (IsClosed) - { - return; - } - - var receiveProgressIndicator = _receiveProgressIndicator; - var sendProgressIndicator = _sendProgressIndicator; - - /* - * If we have not received any data within the heartbeat interval and we are not waiting for a heartbeat timeout check, then we send a heartbeat request. - */ - if (message.ReceiveProgressIndicator == receiveProgressIndicator && !_awaitingHeartbeatTimeoutCheck) - { - SendPackage(new TcpPackage(TcpCommand.HeartbeatRequestCommand, Guid.NewGuid(), null)); - _awaitingHeartbeatTimeoutCheck = true; - _publisher.Publish(TimerMessage.Schedule.Create(_heartbeatTimeout, _weakThisEnvelope, - new TcpMessage.HeartbeatTimeout(receiveProgressIndicator))); - } - /* - * As a proactive measure, if we have not sent any data to the remote party within the heartbeat interval, - * we also send a heartbeat request just to generate some data for the remote party so that it can clear its heartbeat timeouts. - * This is particularly useful when the remote party's heartbeat request is stuck behind a large (multi-megabyte) TCP message. - */ - else if (message.SendProgressIndicator == sendProgressIndicator) - { - SendPackage(new TcpPackage(TcpCommand.HeartbeatRequestCommand, Guid.NewGuid(), null)); - Log.Verbose( - "Connection '{connectionName}{clientConnectionName}' [{remoteEndPoint}, {connectionId:B}] Proactive heartbeat request sent to the remote party since no data was sent during the last heartbeat interval.", - ConnectionName, ClientConnectionName.IsEmptyString() ? string.Empty : ":" + ClientConnectionName, - _connection.RemoteEndPoint, ConnectionId); - } - - //schedule the next heartbeat regardless of whether or not there's an active heartbeat request - ScheduleHeartbeat(receiveProgressIndicator, sendProgressIndicator); - } - - public void Handle(TcpMessage.HeartbeatTimeout message) - { - _awaitingHeartbeatTimeoutCheck = false; - if (IsClosed) - { - return; - } - - var receiveProgressIndicator = _receiveProgressIndicator; - var sendProgressIndicator = _sendProgressIndicator; - if (message.ReceiveProgressIndicator == receiveProgressIndicator) - { - Stop(string.Format($"HEARTBEAT TIMEOUT at receiveProgressIndicator={receiveProgressIndicator}, sendProgressIndicator={sendProgressIndicator}")); - } - } - - private void ScheduleHeartbeat(long receiveProgressIndicator, long sendProgressIndicator) - { - _publisher.Publish(TimerMessage.Schedule.Create(_heartbeatInterval, _weakThisEnvelope, - new TcpMessage.Heartbeat(receiveProgressIndicator, sendProgressIndicator))); - } - - // Same health warnings as SendToThisEnvelope - private class SendToWeakThisEnvelope : IEnvelope - { - private readonly WeakReference _receiver; - - public SendToWeakThisEnvelope(object receiver) - { - _receiver = new WeakReference(receiver); - } - - public void ReplyWith(T message) where T : Message - { - if (_receiver.Target is IHandle handle) - { - handle.Handle(message); - } - else if (_receiver is IAsyncHandle) - { - throw new Exception($"SendToWeakThisEnvelope does not support asynchronous receivers. Receiver: {_receiver}"); - } - } - } - - private class TcpAuthRequest : AuthenticationRequest - { - private readonly TcpConnectionManager _manager; - private readonly TcpPackage _package; - - public TcpAuthRequest(TcpConnectionManager manager, TcpPackage package) - : base($"(TCP) {manager.RemoteEndPoint}", package.Tokens) - { - _manager = manager; - _package = package; - } - - public override void Unauthorized() - { - _manager.ReplyNotAuthenticated(_package.CorrelationId, "Not Authenticated"); - } - - public override void Authenticated(ClaimsPrincipal principal) - { - _manager.UnwrapAndPublishPackage(_package, principal, _package.Tokens); - } - - public override void Error() - { - _manager.ReplyNotAuthenticated(_package.CorrelationId, "Internal Server Error"); - } - - public override void NotReady() - { - _manager.ReplyNotReady(_package.CorrelationId, "Server not ready"); - } - } - - - private class TcpDefaultAuthRequest : AuthenticationRequest - { - private readonly TcpConnectionManager _manager; - private readonly Guid _correlationId; - private readonly UserCredentials _userCredentials; - - public TcpDefaultAuthRequest(TcpConnectionManager manager, Guid correlationId, - UserCredentials userCredentials) - : base($"(TCP) {manager.RemoteEndPoint}", userCredentials.Tokens) - { - _manager = manager; - _correlationId = correlationId; - _userCredentials = userCredentials; - } - - public override void Unauthorized() - { - _manager.ReplyNotAuthenticated(_correlationId, "Unauthorized"); - } - - public override void Authenticated(ClaimsPrincipal principal) - { - _manager.ReplyAuthenticated(_correlationId, _userCredentials, principal); - } - - public override void Error() - { - _manager.ReplyNotAuthenticated(_correlationId, "Internal Server Error"); - } - - public override void NotReady() - { - _manager.ReplyNotReady(_correlationId, "Server not yet ready"); - } - } - - private class UserCredentials - { - public readonly ClaimsPrincipal User; - public readonly IReadOnlyDictionary Tokens; - - public UserCredentials(IReadOnlyDictionary tokens, ClaimsPrincipal user) - { - Tokens = tokens; - User = user; - } - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/TcpDispatcher.cs b/src/EventStore.Core/Services/Transport/Tcp/TcpDispatcher.cs deleted file mode 100644 index b62ae907d0..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/TcpDispatcher.cs +++ /dev/null @@ -1,134 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Security.Claims; -using EventStore.Core.Messaging; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Services.Transport.Tcp -{ - public abstract class TcpDispatcher : ITcpDispatcher - { - private static readonly ILogger Log = Serilog.Log.ForContext(); - - private readonly Func, TcpConnectionManager, Message>[][] - _unwrappers; - - private readonly IDictionary>[] _wrappers; - - protected TcpDispatcher() - { - _unwrappers = - new Func, TcpConnectionManager, Message>[2][]; - _unwrappers[0] = - new Func, TcpConnectionManager, Message>[255]; - _unwrappers[1] = - new Func, TcpConnectionManager, Message>[255]; - - _wrappers = new IDictionary>[2]; - _wrappers[0] = new Dictionary>(); - _wrappers[1] = new Dictionary>(); - } - - protected void AddWrapper(Func wrapper, ClientVersion version) where T : Message - { - _wrappers[(byte)version][typeof(T)] = x => wrapper((T)x); - } - - protected void AddUnwrapper(TcpCommand command, Func unwrapper, - ClientVersion version) where T : Message - { - _unwrappers[(byte)version][(byte)command] = (pkg, env, user, tokens, conn) => unwrapper(pkg, env); - } - - protected void AddUnwrapper(TcpCommand command, - Func unwrapper, ClientVersion version) where T : Message - { - _unwrappers[(byte)version][(byte)command] = - (pkg, env, user, tokens, conn) => unwrapper(pkg, env, conn); - } - - protected void AddUnwrapper(TcpCommand command, Func unwrapper, - ClientVersion version) where T : Message - { - _unwrappers[(byte)version][(byte)command] = - (pkg, env, user, tokens, conn) => unwrapper(pkg, env, user, conn); - } - - protected void AddUnwrapper(TcpCommand command, Func unwrapper, - ClientVersion version) where T : Message - { - _unwrappers[(byte)version][(byte)command] = - (pkg, env, user, tokens, conn) => unwrapper(pkg, env, user); - } - - protected void AddUnwrapper(TcpCommand command, - Func, T> unwrapper, ClientVersion version) - where T : Message - { - _unwrappers[(byte)version][(byte)command] = (pkg, env, user, tokens, conn) => - unwrapper(pkg, env, user, tokens); - } - - protected void AddUnwrapper(TcpCommand command, - Func, TcpConnectionManager, T> unwrapper, - ClientVersion version) - where T : Message - { - // ReSharper disable RedundantCast - _unwrappers[(byte)version][(byte)command] = - (Func, TcpConnectionManager, Message>)unwrapper; - // ReSharper restore RedundantCast - } - - public TcpPackage? WrapMessage(Message message, byte version) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } - - try - { - if (_wrappers[version].TryGetValue(message.GetType(), out var wrapper)) - { - return wrapper(message); - } - - if (_wrappers[^1].TryGetValue(message.GetType(), out wrapper)) - { - return wrapper(message); - } - } - catch (Exception exc) - { - Log.Error(exc, "Error while wrapping message {message}.", message); - } - - return null; - } - - public Message UnwrapPackage(TcpPackage package, IEnvelope envelope, ClaimsPrincipal user, - IReadOnlyDictionary tokens, TcpConnectionManager connection, byte version) - { - if (envelope == null) - { - throw new ArgumentNullException(nameof(envelope)); - } - - var unwrapper = _unwrappers[version][(byte)package.Command] ?? - _unwrappers[^1][(byte)package.Command]; - - try - { - return unwrapper?.Invoke(package, envelope, user, tokens, connection); - } - catch (Exception exc) - { - Log.Error(exc, "Error while unwrapping TcpPackage with command {command}.", - package.Command); - } - - return null; - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/TcpPackage.cs b/src/EventStore.Core/Services/Transport/Tcp/TcpPackage.cs deleted file mode 100644 index 685f97f957..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/TcpPackage.cs +++ /dev/null @@ -1,236 +0,0 @@ -using System; -using System.Collections.Generic; -using EventStore.Common.Utils; -using EventStore.Core.Authentication; - -namespace EventStore.Core.Services.Transport.Tcp -{ - [Flags] - public enum TcpFlags : byte - { - None = 0x00, - Authenticated = 0x01, - TrustedWrite = 0x02 - } - - public struct TcpPackage - { - private static readonly IReadOnlyDictionary NotAuthenticated = new Dictionary(); - public const int CommandOffset = 0; - public const int FlagsOffset = CommandOffset + 1; - public const int CorrelationOffset = FlagsOffset + 1; - public const int AuthOffset = CorrelationOffset + 16; - - public const int MandatorySize = AuthOffset; - - public const int MaxLoginLength = 127; - public const int MaxPasswordLength = 127; - public const int MaxTokenLength = short.MaxValue - 2; - - public readonly TcpCommand Command; - public readonly TcpFlags Flags; - public readonly Guid CorrelationId; - public readonly ArraySegment Data; - - private readonly string _login; - private readonly string _password; - private readonly string _authToken; - - public IReadOnlyDictionary Tokens => (_login, _authToken) switch - { - (null, null) => NotAuthenticated, - (null, _) => new Dictionary { [AuthenticationTokenKeys.Jwt] = _authToken }, - _ => new Dictionary - { - [AuthenticationTokenKeys.Username] = _login, - [AuthenticationTokenKeys.Password] = _password - } - }; - - public static TcpPackage FromArraySegment(ArraySegment data) - { - if (data.Count < MandatorySize) - { - throw new ArgumentException($"ArraySegment too short, length: {data.Count}", nameof(data)); - } - - var command = (TcpCommand)data.Array[data.Offset + CommandOffset]; - var flags = (TcpFlags)data.Array[data.Offset + FlagsOffset]; - - var guidBytes = new byte[16]; - Buffer.BlockCopy(data.Array, data.Offset + CorrelationOffset, guidBytes, 0, 16); - var correlationId = new Guid(guidBytes); - - var headerSize = MandatorySize; - string login = null; - string pass = null; - if ((flags & TcpFlags.Authenticated) != 0) - { - var firstByte = data.Array[data.Offset + AuthOffset]; - var tokenLength = BitConverter.ToInt16(data.Array, AuthOffset); - if (Math.Sign(tokenLength) == -1) - { - var token = Helper.UTF8NoBom.GetString(data.Array, AuthOffset + 2, -tokenLength); - - headerSize += token.Length + 2; - - return new TcpPackage(command, flags, correlationId, token, - new ArraySegment(data.Array, data.Offset + headerSize, data.Count - headerSize)); - } - - var loginLen = firstByte; - login = Helper.UTF8NoBom.GetString(data.Array, data.Offset + AuthOffset + 1, loginLen); - - var passLen = data.Array[data.Offset + AuthOffset + 1 + loginLen]; - pass = Helper.UTF8NoBom.GetString(data.Array, data.Offset + AuthOffset + 1 + loginLen + 1, passLen); - - headerSize += 1 + loginLen + 1 + passLen; - } - - return new TcpPackage(command, - flags, - correlationId, - login, - pass, - new ArraySegment(data.Array, data.Offset + headerSize, data.Count - headerSize)); - } - - public TcpPackage(TcpCommand command, Guid correlationId, byte[] data) - : this(command, TcpFlags.None, correlationId, null, new ArraySegment(data ?? Empty.ByteArray)) - { - } - - public TcpPackage(TcpCommand command, Guid correlationId, ArraySegment data) - : this(command, TcpFlags.None, correlationId, null, data) - { - } - - public TcpPackage(TcpCommand command, TcpFlags flags, Guid correlationId, string login, string password, - byte[] data) - : this(command, flags, correlationId, login, password, new ArraySegment(data ?? Empty.ByteArray)) - { - } - - public TcpPackage(TcpCommand command, TcpFlags flags, Guid correlationId, string login, string password, - ArraySegment data) : this(command, flags, correlationId, data) - { - if ((flags & TcpFlags.Authenticated) != 0) - { - Ensure.NotNull(login, nameof(login)); - Ensure.NotNull(password, nameof(password)); - if (Helper.UTF8NoBom.GetByteCount(login) > MaxLoginLength) - { - throw new ArgumentException($"Login length must be less than {MaxLoginLength} bytes.", - nameof(login)); - } - - if (Helper.UTF8NoBom.GetByteCount(password) > MaxPasswordLength) - { - throw new ArgumentException($"Password length must be less than {MaxPasswordLength} bytes.", - nameof(password)); - } - } - else - { - if (login != null) - { - throw new ArgumentException("Login provided for non-authorized TcpPackage", nameof(login)); - } - - if (password != null) - { - throw new ArgumentException("Password provided for non-authorized TcpPackage", nameof(password)); - } - } - - _login = login; - _password = password; - } - - public TcpPackage(TcpCommand command, TcpFlags flags, Guid correlationId, string authToken, - ArraySegment data) : this(command, flags, correlationId, data) - { - if ((flags & TcpFlags.Authenticated) != 0) - { - Ensure.NotNull(authToken, nameof(authToken)); - - if (Helper.UTF8NoBom.GetByteCount(authToken) > MaxTokenLength) - { - throw new ArgumentException($"Token length must be smaller than {MaxTokenLength} bytes", - nameof(authToken)); - } - } - - if ((flags & TcpFlags.Authenticated) == 0 && authToken != null) - { - throw new ArgumentException("Token provided for non-authorized TcpPackage", nameof(authToken)); - } - - _authToken = authToken; - } - - - private TcpPackage(TcpCommand command, TcpFlags flags, Guid correlationId, ArraySegment data) - { - Command = command; - Flags = flags; - CorrelationId = correlationId; - Data = data; - _login = null; - _password = null; - _authToken = null; - } - - public byte[] AsByteArray() - { - if ((Flags & TcpFlags.Authenticated) != 0) - { - if (_authToken == null) - { - var loginLen = Helper.UTF8NoBom.GetByteCount(_login); - var passLen = Helper.UTF8NoBom.GetByteCount(_password); - var res = new byte[MandatorySize + 2 + loginLen + passLen + Data.Count]; - res[CommandOffset] = (byte)Command; - res[FlagsOffset] = (byte)Flags; - Buffer.BlockCopy(CorrelationId.ToByteArray(), 0, res, CorrelationOffset, 16); - - res[AuthOffset] = (byte)loginLen; - Helper.UTF8NoBom.GetBytes(_login, 0, _login.Length, res, AuthOffset + 1); - res[AuthOffset + 1 + loginLen] = (byte)passLen; - Helper.UTF8NoBom.GetBytes(_password, 0, _password.Length, res, AuthOffset + 1 + loginLen + 1); - - Buffer.BlockCopy(Data.Array, Data.Offset, res, res.Length - Data.Count, Data.Count); - return res; - } - else - { - var authTokenLength = Helper.UTF8NoBom.GetByteCount(_authToken); - var res = new byte[MandatorySize + 2 + authTokenLength + Data.Count]; - res[CommandOffset] = (byte)Command; - res[FlagsOffset] = (byte)Flags; - Buffer.BlockCopy(CorrelationId.ToByteArray(), 0, res, CorrelationOffset, 16); - - Buffer.BlockCopy(BitConverter.GetBytes(-Convert.ToInt16(authTokenLength)), 0, res, AuthOffset, 2); - Helper.UTF8NoBom.GetBytes(_authToken, 0, _authToken.Length, res, AuthOffset + 2); - - Buffer.BlockCopy(Data.Array, Data.Offset, res, res.Length - Data.Count, Data.Count); - return res; - } - } - else - { - var res = new byte[MandatorySize + Data.Count]; - res[CommandOffset] = (byte)Command; - res[FlagsOffset] = (byte)Flags; - Buffer.BlockCopy(CorrelationId.ToByteArray(), 0, res, CorrelationOffset, 16); - Buffer.BlockCopy(Data.Array, Data.Offset, res, res.Length - Data.Count, Data.Count); - return res; - } - } - - public ArraySegment AsArraySegment() - { - return new ArraySegment(AsByteArray()); - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/TcpService.cs b/src/EventStore.Core/Services/Transport/Tcp/TcpService.cs deleted file mode 100644 index b1749e8949..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/TcpService.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using EventStore.Common.Utils; -using EventStore.Core.Bus; -using EventStore.Core.Messages; -using EventStore.Plugins.Authentication; -using EventStore.Transport.Tcp; -using ILogger = Serilog.ILogger; - -namespace EventStore.Core.Services.Transport.Tcp -{ - public enum TcpServiceType - { - Internal, - External - } - - public enum TcpSecurityType - { - Normal, - Secure - } - - public class TcpService : IHandle, - IHandle, - IHandle - { - private static readonly ILogger Log = Serilog.Log.ForContext(); - - private readonly IPublisher _publisher; - private readonly IPEndPoint _serverEndPoint; - private readonly TcpServerListener _serverListener; - private readonly IPublisher _networkSendQueue; - private readonly TcpServiceType _serviceType; - private readonly TcpSecurityType _securityType; - private readonly Func _dispatcherFactory; - private readonly TimeSpan _heartbeatInterval; - private readonly TimeSpan _heartbeatTimeout; - private readonly IAuthenticationProvider _authProvider; - private readonly Func _certificateSelector; - private readonly Func _intermediatesSelector; - private readonly CertificateDelegates.ClientCertificateValidator _sslClientCertValidator; - private readonly int _connectionPendingSendBytesThreshold; - private readonly int _connectionQueueSizeThreshold; - private readonly AuthorizationGateway _authorizationGateway; - - public TcpService(IPublisher publisher, - IPEndPoint serverEndPoint, - IPublisher networkSendQueue, - TcpServiceType serviceType, - TcpSecurityType securityType, - ITcpDispatcher dispatcher, - TimeSpan heartbeatInterval, - TimeSpan heartbeatTimeout, - IAuthenticationProvider authProvider, - AuthorizationGateway authorizationGateway, - Func certificateSelector, - Func intermediatesSelector, - CertificateDelegates.ClientCertificateValidator sslClientCertValidator, - int connectionPendingSendBytesThreshold, - int connectionQueueSizeThreshold) - : this(publisher, serverEndPoint, networkSendQueue, serviceType, securityType, (_, __) => dispatcher, - heartbeatInterval, heartbeatTimeout, authProvider, authorizationGateway, certificateSelector, intermediatesSelector, sslClientCertValidator, connectionPendingSendBytesThreshold, connectionQueueSizeThreshold) - { - } - - public TcpService(IPublisher publisher, - IPEndPoint serverEndPoint, - IPublisher networkSendQueue, - TcpServiceType serviceType, - TcpSecurityType securityType, - Func dispatcherFactory, - TimeSpan heartbeatInterval, - TimeSpan heartbeatTimeout, - IAuthenticationProvider authProvider, - AuthorizationGateway authorizationGateway, - Func certificateSelector, - Func intermediatesSelector, - CertificateDelegates.ClientCertificateValidator sslClientCertValidator, - int connectionPendingSendBytesThreshold, - int connectionQueueSizeThreshold) - { - Ensure.NotNull(publisher, "publisher"); - Ensure.NotNull(serverEndPoint, "serverEndPoint"); - Ensure.NotNull(networkSendQueue, "networkSendQueue"); - Ensure.NotNull(dispatcherFactory, "dispatcherFactory"); - Ensure.NotNull(authProvider, "authProvider"); - Ensure.NotNull(authorizationGateway, "authorizationGateway"); - - _publisher = publisher; - _serverEndPoint = serverEndPoint; - _serverListener = new TcpServerListener(_serverEndPoint); - _networkSendQueue = networkSendQueue; - _serviceType = serviceType; - _securityType = securityType; - _dispatcherFactory = dispatcherFactory; - _heartbeatInterval = heartbeatInterval; - _heartbeatTimeout = heartbeatTimeout; - _connectionPendingSendBytesThreshold = connectionPendingSendBytesThreshold; - _connectionQueueSizeThreshold = connectionQueueSizeThreshold; - _authProvider = authProvider; - _authorizationGateway = authorizationGateway; - _certificateSelector = certificateSelector; - _intermediatesSelector = intermediatesSelector; - _sslClientCertValidator = sslClientCertValidator; - } - - public void Handle(SystemMessage.SystemInit message) - { - try - { - _serverListener.StartListening(OnConnectionAccepted, _securityType.ToString()); - } - catch (Exception e) - { - Application.Exit(ExitCode.Error, e.Message); - } - } - - public void Handle(SystemMessage.SystemStart message) - { - } - - public void Handle(SystemMessage.BecomeShuttingDown message) - { - _serverListener.Stop(); - } - - private void OnConnectionAccepted(IPEndPoint endPoint, Socket socket) - { - var conn = _securityType == TcpSecurityType.Secure - ? TcpConnectionSsl.CreateServerFromSocket(Guid.NewGuid(), endPoint, socket, _certificateSelector, _intermediatesSelector, _sslClientCertValidator, verbose: true) - : TcpConnection.CreateAcceptedTcpConnection(Guid.NewGuid(), endPoint, socket, verbose: true); - Log.Information( - "{serviceType} TCP connection accepted: [{securityType}, {remoteEndPoint}, L{localEndPoint}, {connectionId:B}].", - _serviceType, _securityType, conn.RemoteEndPoint, conn.LocalEndPoint, conn.ConnectionId); - - var dispatcher = _dispatcherFactory(conn.ConnectionId, _serverEndPoint); - var manager = new TcpConnectionManager( - $"{_serviceType.ToString().ToLower()}-{_securityType.ToString().ToLower()}", - _serviceType, - dispatcher, - _publisher, - conn, - _networkSendQueue, - _authProvider, - _authorizationGateway, - _heartbeatInterval, - _heartbeatTimeout, - (m, e) => _publisher.Publish(new TcpMessage.ConnectionClosed(m, e)), - _connectionPendingSendBytesThreshold, - _connectionQueueSizeThreshold); // TODO AN: race condition - _publisher.Publish(new TcpMessage.ConnectionEstablished(manager)); - manager.StartReceiving(); - } - } -} diff --git a/src/EventStore.Core/Services/Transport/Tcp/UnableToAcquireStreamException.cs b/src/EventStore.Core/Services/Transport/Tcp/UnableToAcquireStreamException.cs deleted file mode 100644 index a8a0457b55..0000000000 --- a/src/EventStore.Core/Services/Transport/Tcp/UnableToAcquireStreamException.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace EventStore.Core.Services.Transport.Tcp -{ - class UnableToAcquireStreamException : Exception - { - } -} diff --git a/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs b/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs index f75bcb77ff..eb126bf830 100644 --- a/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs +++ b/src/EventStore.Core/Services/VNode/ClusterVNodeController.cs @@ -1101,14 +1101,10 @@ private ValueTask ForwardRequest(ClientMessage.WriteRequestMessage msg, Message private void DenyRequestBecauseNotLeader(Guid correlationId, IEnvelope envelope) { LeaderInfoProvider leaderInfoProvider = new LeaderInfoProvider(_node.GossipAdvertiseInfo, _leader); - var endpoints = leaderInfoProvider.GetLeaderInfoEndPoints(); envelope.ReplyWith( new ClientMessage.NotHandled(correlationId, ClientMessage.NotHandled.Types.NotHandledReason.NotLeader, - new ClientMessage.NotHandled.Types.LeaderInfo(endpoints.AdvertisedTcpEndPoint, - endpoints.IsTcpEndPointSecure, - endpoints.AdvertisedHttpEndPoint - ))); + new ClientMessage.NotHandled.Types.LeaderInfo(leaderInfoProvider.GetLeaderInfoEndPoint()))); } private ValueTask HandleAsReadOnlyReplica(ClientMessage.WriteEvents message, CancellationToken token) @@ -1217,14 +1213,10 @@ private ValueTask HandleAsReadOnlyReplica(ClientMessage.DeleteStream message, Ca private void DenyRequestBecauseReadOnly(Guid correlationId, IEnvelope envelope) { LeaderInfoProvider leaderInfoProvider = new LeaderInfoProvider(_node.GossipAdvertiseInfo, _leader); - var endpoints = leaderInfoProvider.GetLeaderInfoEndPoints(); envelope.ReplyWith( new ClientMessage.NotHandled(correlationId, ClientMessage.NotHandled.Types.NotHandledReason.IsReadOnly, - new ClientMessage.NotHandled.Types.LeaderInfo(endpoints.AdvertisedTcpEndPoint, - endpoints.IsTcpEndPointSecure, - endpoints.AdvertisedHttpEndPoint - ))); + new ClientMessage.NotHandled.Types.LeaderInfo(leaderInfoProvider.GetLeaderInfoEndPoint()))); } private void DenyRequestBecauseNotReady(IEnvelope envelope, Guid correlationId) @@ -1536,11 +1528,8 @@ private async ValueTask Handle(ReplicationMessage.FollowerAssignment message, Ca if (IsLegitimateReplicationMessage(message)) { Log.Information( - "========== [{httpEndPoint}] FOLLOWER ASSIGNMENT RECEIVED FROM [{internalTcp},{internalSecureTcp},{leaderId:B}].", - _nodeInfo.HttpEndPoint, - _leader.InternalTcpEndPoint == null ? "n/a" : _leader.InternalTcpEndPoint.ToString(), - _leader.InternalSecureTcpEndPoint == null ? "n/a" : _leader.InternalSecureTcpEndPoint.ToString(), - message.LeaderId); + "========== [{httpEndPoint}] FOLLOWER ASSIGNMENT RECEIVED FROM [{leaderEndPoint},{leaderId:B}].", + _nodeInfo.HttpEndPoint, _leader.HttpEndPoint, message.LeaderId); await _outputBus.DispatchAsync(message, token); await _fsm.HandleAsync(new SystemMessage.BecomeFollower(_stateCorrelationId, _leader), token); } @@ -1551,11 +1540,8 @@ private async ValueTask Handle(ReplicationMessage.CloneAssignment message, Cance if (IsLegitimateReplicationMessage(message)) { Log.Information( - "========== [{httpEndPoint}] CLONE ASSIGNMENT RECEIVED FROM [{internalTcp},{internalSecureTcp},{leaderId:B}].", - _nodeInfo.HttpEndPoint, - _leader.InternalTcpEndPoint == null ? "n/a" : _leader.InternalTcpEndPoint.ToString(), - _leader.InternalSecureTcpEndPoint == null ? "n/a" : _leader.InternalSecureTcpEndPoint.ToString(), - message.LeaderId); + "========== [{httpEndPoint}] CLONE ASSIGNMENT RECEIVED FROM [{leaderEndPoint},{leaderId:B}].", + _nodeInfo.HttpEndPoint, _leader.HttpEndPoint, message.LeaderId); await _outputBus.DispatchAsync(message, token); await _fsm.HandleAsync(new SystemMessage.BecomeClone(_stateCorrelationId, _leader), token); } @@ -1569,11 +1555,8 @@ private ValueTask Handle(ReplicationMessage.DropSubscription message, Cancellati if (IsLegitimateReplicationMessage(message)) { Log.Information( - "========== [{httpEndPoint}] DROP SUBSCRIPTION REQUEST RECEIVED FROM [{internalTcp},{internalSecureTcp},{leaderId:B}]. THIS MEANS THAT THERE IS A SURPLUS OF NODES IN THE CLUSTER, SHUTTING DOWN.", - _nodeInfo.HttpEndPoint, - _leader.InternalTcpEndPoint == null ? "n/a" : _leader.InternalTcpEndPoint.ToString(), - _leader.InternalSecureTcpEndPoint == null ? "n/a" : _leader.InternalSecureTcpEndPoint.ToString(), - message.LeaderId); + "========== [{httpEndPoint}] DROP SUBSCRIPTION REQUEST RECEIVED FROM [{leaderEndPoint},{leaderId:B}]. THIS MEANS THAT THERE IS A SURPLUS OF NODES IN THE CLUSTER, SHUTTING DOWN.", + _nodeInfo.HttpEndPoint, _leader.HttpEndPoint, message.LeaderId); task = _outputBus.DispatchAsync( new ClientMessage.RequestShutdown(exitProcess: true, shutdownHttp: true), token); } diff --git a/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs b/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs index 8189b16503..f4a6e75e4b 100644 --- a/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs +++ b/src/EventStore.Core/Services/VNode/LeaderInfoProvider.cs @@ -20,38 +20,21 @@ public LeaderInfoProvider(GossipAdvertiseInfo gossipInfo, Cluster.MemberInfo lea _leaderInfo = leaderInfo; } - public (EndPoint AdvertisedTcpEndPoint, bool IsTcpEndPointSecure, EndPoint AdvertisedHttpEndPoint) - GetLeaderInfoEndPoints() + public EndPoint GetLeaderInfoEndPoint() { - var endpoints = _leaderInfo != null - ? (TcpEndPoint: _leaderInfo.ExternalTcpEndPoint ?? _leaderInfo.ExternalSecureTcpEndPoint, - IsTcpEndPointSecure: _leaderInfo.ExternalSecureTcpEndPoint != null, - HttpEndPoint: _leaderInfo.HttpEndPoint, + ? (HttpEndPoint: _leaderInfo.HttpEndPoint, AdvertiseHost: _leaderInfo.AdvertiseHostToClientAs, - AdvertiseHttpPort: _leaderInfo.AdvertiseHttpPortToClientAs, - AdvertiseTcpPort: _leaderInfo.AdvertiseTcpPortToClientAs) - : (TcpEndPoint: _gossipInfo.ExternalTcp ?? _gossipInfo.ExternalSecureTcp, - IsTcpEndPointSecure: _gossipInfo.ExternalSecureTcp != null, - HttpEndPoint: _gossipInfo.HttpEndPoint, + AdvertiseHttpPort: _leaderInfo.AdvertiseHttpPortToClientAs) + : (HttpEndPoint: (EndPoint)_gossipInfo.HttpEndPoint, AdvertiseHost: _gossipInfo.AdvertiseHostToClientAs, - AdvertiseHttpPort: _gossipInfo.AdvertiseHttpPortToClientAs, - AdvertiseTcpPort: _gossipInfo.AdvertiseTcpPortToClientAs); + AdvertiseHttpPort: _gossipInfo.AdvertiseHttpPortToClientAs); - var advertisedTcpEndPoint = endpoints.TcpEndPoint == null - ? null - : new DnsEndPoint( - string.IsNullOrEmpty(endpoints.AdvertiseHost) - ? endpoints.TcpEndPoint.GetHost() - : endpoints.AdvertiseHost, - endpoints.AdvertiseTcpPort == 0 ? endpoints.TcpEndPoint.GetPort() : endpoints.AdvertiseTcpPort); - var advertisedHttpEndPoint = new DnsEndPoint( + return new DnsEndPoint( string.IsNullOrEmpty(endpoints.AdvertiseHost) ? endpoints.HttpEndPoint.GetHost() : endpoints.AdvertiseHost, endpoints.AdvertiseHttpPort == 0 ? endpoints.HttpEndPoint.GetPort() : endpoints.AdvertiseHttpPort); - - return (advertisedTcpEndPoint, endpoints.IsTcpEndPointSecure, advertisedHttpEndPoint); } } } diff --git a/src/EventStore.Core/Settings/VNodeSettings.cs b/src/EventStore.Core/Settings/VNodeSettings.cs deleted file mode 100644 index 1e1d7b149c..0000000000 --- a/src/EventStore.Core/Settings/VNodeSettings.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Net; -using System.Security.Cryptography.X509Certificates; -using EventStore.Common.Utils; -using EventStore.Core.Services.Monitoring; - -namespace EventStore.Core.Settings -{ - public class SingleVNodeSettings - { - public readonly IPEndPoint ExternalTcpEndPoint; - public readonly IPEndPoint ExternalSecureTcpEndPoint; - public readonly IPEndPoint HttpEndPoint; - public readonly string[] HttpPrefixes; - public readonly bool EnableTrustedAuth; - public readonly X509Certificate2 Certificate; - public readonly int WorkerThreads; - - public readonly TimeSpan MinFlushDelay; - public readonly TimeSpan PrepareTimeout; - public readonly TimeSpan CommitTimeout; - - public readonly bool DisableScavengeMerging; - - public readonly TimeSpan StatsPeriod; - public readonly StatsStorage StatsStorage; - - public readonly bool SkipInitializeStandardUsersCheck; - - public readonly TimeSpan TcpTimeout; - - public SingleVNodeSettings(IPEndPoint externalTcpEndPoint, - IPEndPoint externalSecureTcpEndPoint, - IPEndPoint httpEndPoint, - string[] httpPrefixes, - bool enableTrustedAuth, - X509Certificate2 certificate, - int workerThreads, - TimeSpan minFlushDelay, - TimeSpan prepareTimeout, - TimeSpan commitTimeout, - TimeSpan statsPeriod, - TimeSpan tcpTimeout, - StatsStorage statsStorage = StatsStorage.StreamAndFile, - bool skipInitializeStandardUsersCheck = false, - bool disableScavengeMerging = false) - { - Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); - Ensure.NotNull(httpPrefixes, "httpPrefixes"); - if (externalSecureTcpEndPoint != null) - { - Ensure.NotNull(certificate, "certificate"); - } - - Ensure.Positive(workerThreads, "workerThreads"); - - ExternalTcpEndPoint = externalTcpEndPoint; - ExternalSecureTcpEndPoint = externalSecureTcpEndPoint; - HttpEndPoint = httpEndPoint; - HttpPrefixes = httpPrefixes; - EnableTrustedAuth = enableTrustedAuth; - Certificate = certificate; - WorkerThreads = workerThreads; - - MinFlushDelay = minFlushDelay; - PrepareTimeout = prepareTimeout; - CommitTimeout = commitTimeout; - - StatsPeriod = statsPeriod; - StatsStorage = statsStorage; - - SkipInitializeStandardUsersCheck = skipInitializeStandardUsersCheck; - DisableScavengeMerging = disableScavengeMerging; - TcpTimeout = tcpTimeout; - } - - public override string ToString() - { - return string.Format("ExternalTcpEndPoint: {0},\n" - + "ExternalSecureTcpEndPoint: {1},\n" - + "HttpEndPoint: {2},\n" - + "HttpPrefixes: {3},\n" - + "EnableTrustedAuth: {4},\n" - + "Certificate: {5},\n" - + "WorkerThreads: {6}\n" - + "MinFlushDelay: {7}\n" - + "PrepareTimeout: {8}\n" - + "CommitTimeout: {9}\n" - + "StatsPeriod: {10}\n" - + "StatsStorage: {11}", - ExternalTcpEndPoint == null ? "n/a" : ExternalTcpEndPoint.ToString(), - ExternalSecureTcpEndPoint == null ? "n/a" : ExternalSecureTcpEndPoint.ToString(), - HttpEndPoint, - string.Join(", ", HttpPrefixes), - EnableTrustedAuth, - Certificate == null ? "n/a" : Certificate.ToString(verbose: true), - WorkerThreads, - MinFlushDelay, - PrepareTimeout, - CommitTimeout, - StatsPeriod, - StatsStorage); - } - } -} diff --git a/src/EventStore.Core/Util/Opts.cs b/src/EventStore.Core/Util/Opts.cs index 358400fa67..dac39b9cf6 100644 --- a/src/EventStore.Core/Util/Opts.cs +++ b/src/EventStore.Core/Util/Opts.cs @@ -1,16 +1,7 @@ -using System; -using System.Net; -using EventStore.Common.Options; -using EventStore.Core.TransactionLog.Chunks; - namespace EventStore.Core.Util { public static class Opts { - public const int ConnectionPendingSendBytesThresholdDefault = 10 * 1024 * 1024; - - public const int ConnectionQueueSizeThresholdDefault = 50000; - public const int HashCollisionReadLimitDefault = 100; public const bool FaultOutOfOrderProjectionsDefault = false; diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/Cluster/specification_with_standard_projections_runnning.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/Cluster/specification_with_standard_projections_runnning.cs deleted file mode 100644 index 68e0e66259..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/Cluster/specification_with_standard_projections_runnning.cs +++ /dev/null @@ -1,357 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Common.Options; -using EventStore.Core.Data; -using EventStore.Core.Tests; -using EventStore.Core.Tests.Helpers; -using EventStore.Core.Util; -using EventStore.Projections.Core.Services.Processing; -using NUnit.Framework; -using ExpectedVersion = EventStore.ClientAPI.ExpectedVersion; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; - -namespace EventStore.Projections.Core.Tests.ClientAPI.Cluster; - -[Category("ClientAPI")] -public abstract class specification_with_standard_projections_runnning : SpecificationWithDirectoryPerTestFixture -{ - protected MiniClusterNode[] _nodes = new MiniClusterNode[3]; - protected Endpoints[] _nodeEndpoints = new Endpoints[3]; - protected IEventStoreConnection _conn; - private readonly ProjectionsSubsystem[] _projections = new ProjectionsSubsystem[3]; - protected UserCredentials _admin = DefaultData.AdminCredentials; - private protected ProjectionManagementTestClient ProjectionClient; - - protected class Endpoints - { - public readonly IPEndPoint InternalTcp; - public readonly IPEndPoint ExternalTcp; - public readonly IPEndPoint HttpEndPoint; - private readonly int[] _ports; - - public Endpoints(int internalTcp, int externalTcp, int httpPort) - { - var testIp = Environment.GetEnvironmentVariable("ES-TESTIP"); - - var address = string.IsNullOrEmpty(testIp) ? IPAddress.Loopback : IPAddress.Parse(testIp); - InternalTcp = new IPEndPoint(address, internalTcp); - ExternalTcp = new IPEndPoint(address, externalTcp); - HttpEndPoint = new IPEndPoint(address, httpPort); - - _ports = new[] { internalTcp, httpPort, externalTcp }; - } - - public IEnumerable Ports => _ports; - } - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); -#if (!DEBUG) - Assert.Ignore("These tests require DEBUG conditional"); -#else - _nodeEndpoints[0] = new Endpoints( - PortsHelper.GetAvailablePort(IPAddress.Loopback), - PortsHelper.GetAvailablePort(IPAddress.Loopback), - PortsHelper.GetAvailablePort(IPAddress.Loopback)); - _nodeEndpoints[1] = new Endpoints( - PortsHelper.GetAvailablePort(IPAddress.Loopback), - PortsHelper.GetAvailablePort(IPAddress.Loopback), - PortsHelper.GetAvailablePort(IPAddress.Loopback)); - _nodeEndpoints[2] = new Endpoints( - PortsHelper.GetAvailablePort(IPAddress.Loopback), - PortsHelper.GetAvailablePort(IPAddress.Loopback), - PortsHelper.GetAvailablePort(IPAddress.Loopback)); - - _nodes[0] = CreateNode(0, - _nodeEndpoints[0], new[] { _nodeEndpoints[0].HttpEndPoint }); - _nodes[1] = CreateNode(1, - _nodeEndpoints[1], new[] { _nodeEndpoints[1].HttpEndPoint }); - _nodes[2] = CreateNode(2, - _nodeEndpoints[2], new[] { _nodeEndpoints[2].HttpEndPoint }); - WaitIdle(); - - var projectionsStarted = _projections.Select(p => SystemProjections.Created(p.LeaderInputBus)).ToArray(); - - foreach (var node in _nodes) - { - node.Start(); - node.WaitIdle(); - } - - await Task.WhenAll(_nodes.Select(x => x.Started)).WithTimeout(TimeSpan.FromSeconds(30)); - - _conn = EventStoreConnection.Create(_nodes[0].ExternalTcpEndPoint); - await _conn.ConnectAsync().WithTimeout(); - - var leader = _nodes.Single(x => x.NodeState == VNodeState.Leader); - ProjectionClient = new ProjectionManagementTestClient(leader.HttpEndPoint, leader.CreateHttpClient()); - - if (GivenStandardProjectionsRunning()) - { - await Task.WhenAny(projectionsStarted).WithTimeout(TimeSpan.FromSeconds(10)); - await EnableStandardProjections().WithTimeout(TimeSpan.FromMinutes(2)); - } - - WaitIdle(); - - try - { - await Given().WithTimeout(); - } - catch (Exception ex) - { - throw new Exception("Given Failed", ex); - } - - try - { - await When().WithTimeout(); - } - catch (Exception ex) - { - throw new Exception("When Failed", ex); - } -#endif - } - - private MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds) - { - _projections[index] = new ProjectionsSubsystem(new ProjectionSubsystemOptions(1, ProjectionType.All, false, TimeSpan.FromMinutes(Opts.ProjectionsQueryExpiryDefault), Opts.FaultOutOfOrderProjectionsDefault, 500, 250)); - var node = new MiniClusterNode( - PathName, index, endpoints.InternalTcp, - endpoints.ExternalTcp, endpoints.HttpEndPoint, - subsystems: [_projections[index]], gossipSeeds: gossipSeeds); - return node; - } - - [TearDown] - public async Task PostTestAsserts() - { - var all = await ProjectionClient.StatisticsAll(); - if (all.Any(p => p.Status == "Faulted")) - { - Assert.Fail("Projections faulted while running the test" + "\r\n" + string.Join("\r\n", all)); - } - } - - protected async Task EnableStandardProjections() - { - await EnableProjection(ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection); - await EnableProjection(ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection); - await EnableProjection(ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection); - await EnableProjection(ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection); - } - - protected async Task DisableStandardProjections() - { - await DisableProjection(ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection); - await DisableProjection(ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection); - await DisableProjection(ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection); - await DisableProjection(ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection); - } - - protected virtual bool GivenStandardProjectionsRunning() - { - return true; - } - - protected async Task EnableProjection(string name) - { - for (int i = 1; i <= 10; i++) - { - try - { - await ProjectionClient.Enable(name); - } - catch (Exception) - { - if (i == 10) - { - throw; - } - - await Task.Delay(5000); - } - } - - await Task.Delay(1000); /* workaround for race condition when multiple projections are being enabled simultaneously */ - } - - protected Task DisableProjection(string name) - { - return ProjectionClient.Disable(name); - } - - protected Task AbortProjection(string name) - { - return ProjectionClient.Abort(name); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - ProjectionClient?.Dispose(); - _conn.Close(); - await Task.WhenAll( - _nodes[0].Shutdown(), - _nodes[1].Shutdown(), - _nodes[2].Shutdown()); - await base.TestFixtureTearDown(); - } - - protected virtual Task When() => Task.CompletedTask; - - protected virtual Task Given() => Task.CompletedTask; - - protected Task PostEvent(string stream, string eventType, string data) - { - return _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, new[] { CreateEvent(eventType, data) }); - } - - protected Task HardDeleteStream(string stream) - { - return _conn.DeleteStreamAsync(stream, ExpectedVersion.Any, true, _admin); - } - - protected Task SoftDeleteStream(string stream) - { - return _conn.DeleteStreamAsync(stream, ExpectedVersion.Any, false, _admin); - } - - protected static EventData CreateEvent(string type, string data) - { - return new EventData(Guid.NewGuid(), type, true, Encoding.UTF8.GetBytes(data), Array.Empty()); - } - - protected void WaitIdle() - { -#if DEBUG - _nodes[0].WaitIdle(); - _nodes[1].WaitIdle(); - _nodes[2].WaitIdle(); -#endif - } - -#pragma warning disable 1998 - protected async Task AssertStreamTailAsync(string streamId, params string[] events) - { -#pragma warning restore 1998 -#if DEBUG - var result = await _conn.ReadStreamEventsBackwardAsync(streamId, -1, events.Length, true, _admin); - switch (result.Status) - { - case SliceReadStatus.StreamDeleted: - Assert.Fail("Stream '{0}' is deleted", streamId); - break; - case SliceReadStatus.StreamNotFound: - Assert.Fail("Stream '{0}' does not exist", streamId); - break; - case SliceReadStatus.Success: - var resultEventsReversed = result.Events.Reverse().ToArray(); - if (resultEventsReversed.Length < events.Length) - { - DumpFailed("Stream does not contain enough events", streamId, events, result.Events); - } - else - { - for (var index = 0; index < events.Length; index++) - { - var parts = events[index].Split(new char[] { ':' }, 2); - var eventType = parts[0]; - var eventData = parts[1]; - - if (resultEventsReversed[index].Event.EventType != eventType) - { - DumpFailed("Invalid event type", streamId, events, resultEventsReversed); - } - else if (resultEventsReversed[index].Event.DebugDataView() != eventData) - { - DumpFailed("Invalid event body", streamId, events, resultEventsReversed); - } - } - } - - break; - } -#endif - } - -#pragma warning disable 1998 - protected async Task DumpStreamAsync(string streamId) - { -#pragma warning restore 1998 -#if DEBUG - var result = await _conn.ReadStreamEventsBackwardAsync(streamId, -1, 100, true, _admin); - switch (result.Status) - { - case SliceReadStatus.StreamDeleted: - Assert.Fail("Stream '{0}' is deleted", streamId); - break; - case SliceReadStatus.StreamNotFound: - Assert.Fail("Stream '{0}' does not exist", streamId); - break; - case SliceReadStatus.Success: - Dump("Dumping..", streamId, result.Events.Reverse().ToArray()); - break; - } -#endif - } - -#if DEBUG - private void DumpFailed(string message, string streamId, string[] events, ResolvedEvent[] resultEvents) - { - var expected = events.Aggregate("", (a, v) => a + ", " + v); - var actual = resultEvents.Aggregate( - "", (a, v) => a + ", " + v.Event.EventType + ":" + v.Event.DebugDataView()); - - var actualMeta = resultEvents.Aggregate( - "", (a, v) => a + "\r\n" + v.Event.EventType + ":" + v.Event.DebugMetadataView()); - - - Assert.Fail( - "Stream: '{0}'\r\n{1}\r\n\r\nExisting events: \r\n{2}\r\n Expected events: \r\n{3}\r\n\r\nActual metas:{4}", - streamId, - message, actual, expected, actualMeta); - } - - private void Dump(string message, string streamId, ResolvedEvent[] resultEvents) - { - var actual = resultEvents.Aggregate( - "", (a, v) => a + ", " + v.OriginalEvent.EventType + ":" + v.OriginalEvent.DebugDataView()); - - var actualMeta = resultEvents.Aggregate( - "", (a, v) => a + "\r\n" + v.OriginalEvent.EventType + ":" + v.OriginalEvent.DebugMetadataView()); - - Debug.WriteLine( - "Stream: '{0}'\r\n{1}\r\n\r\nExisting events: \r\n{2}\r\n \r\nActual metas:{3}", streamId, - message, actual, actualMeta); - } -#endif - - protected async Task PostProjection(string query) - { - await ProjectionClient.CreateContinuous("test-projection", query); - WaitIdle(); - } -} - -[Explicit] -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class vnode_cluster_specification : specification_with_standard_projections_runnning -{ - [Test, Explicit] - public async Task vnode_cluster_starts() - { - await PostProjection(@"fromStream('$user-admin').outputState()"); - await AssertStreamTailAsync("$projections-test-projection-result", "Result:{}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/RecordedEventExtensions.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/RecordedEventExtensions.cs deleted file mode 100644 index ae26152b90..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/RecordedEventExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text; -using EventStore.ClientAPI; - -namespace EventStore.Projections.Core.Tests.ClientAPI; - -internal static class RecordedEventExtensions -{ - public static string DebugDataView(this RecordedEvent source) => Encoding.UTF8.GetString(source.Data); - public static string DebugMetadataView(this RecordedEvent source) => Encoding.UTF8.GetString(source.Metadata); -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index.cs deleted file mode 100644 index e14b922726..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI -{ - namespace event_by_type_index - { - public abstract class with_existing_events : specification_with_standard_projections_runnning - { - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream1", "type1", "{}"); - await PostEvent("stream1", "type2", "{}"); - await PostEvent("stream1", "type3", "{}"); - await PostEvent("stream2", "type1", "{}"); - await PostEvent("stream2", "type2", "{}"); - await PostEvent("stream2", "type3", "{}"); - } - } - - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class when_creating : with_existing_events - { - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().when({ - $init: function(){ - return {c: 0}; - }, - type1: count, - type2: count -}).outputState() - -function count(s,e) { - return {c: s.c + 1}; -} -"); - } - - [Test, Category("Network")] - public async Task result_is_correct() - { - await AssertStreamTail("$projections-test-projection-result", "Result:{\"c\":4}"); - } - } - - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class when_posting_more_events : with_existing_events - { - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().when({ - $init: function(){ - return {c: 0}; - }, - type1: count, - type2: count -}).outputState() - -function count(s,e) { - return {c: s.c + 1}; -} -"); - await PostEvent("stream3", "type2", "{}"); - await PostEvent("stream3", "type3", "{}"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task result_is_correct() - { - await AssertStreamTail("$projections-test-projection-result", "Result:{\"c\":5}"); - } - } - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index/when_reverting_after_index_catches_up.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index/when_reverting_after_index_catches_up.cs deleted file mode 100644 index 182ceb53d1..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/event_by_type_index/when_reverting_after_index_catches_up.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading; -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.event_by_type_index; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -[NonParallelizable] -public class when_reverting_after_index_catches_up : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - await AbortProjection("test-projection"); - WaitIdle(); - - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":1}", "Result:{\"a\":2}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":1}", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/list_projections.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/list_projections.cs deleted file mode 100644 index 50a04bae3c..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/list_projections.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class list_projections : specification_with_standard_projections_runnning -{ - const string TestProjection = - "fromAll().when({$init: function (state, ev) {return {};},ConversationStarted: function (state, ev) {state.lastBatchSent = ev;return state;}});"; - - [Test] - public async Task list_all_projections_works() - { - var x = await ProjectionClient.StatisticsAll(); - Assert.AreEqual(true, x.Any()); - Assert.IsTrue(x.Any(p => p.Name == "$streams")); - } - - [Test] - public async Task list_oneTime_projections_works() - { - await ProjectionClient.CreateOneTime(TestProjection); - var x = await ProjectionClient.StatisticsOneTime(); - Assert.AreEqual(true, x.Any(p => p.Mode == "OneTime")); - } - - [Test] - public async Task list_continuous_projections_works() - { - var nameToTest = Guid.NewGuid().ToString(); - await ProjectionClient.CreateContinuous(nameToTest, TestProjection); - var x = await ProjectionClient.StatisticsContinuous(); - Assert.AreEqual(true, x.Any(p => p.Name == nameToTest)); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/specification_with_standard_projections_runnning.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/specification_with_standard_projections_runnning.cs deleted file mode 100644 index 2bad847175..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/specification_with_standard_projections_runnning.cs +++ /dev/null @@ -1,325 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; -using EventStore.Common.Options; -using EventStore.Core.Tests; -using EventStore.Core.Tests.ClientAPI.Helpers; -using EventStore.Core.Tests.Helpers; -using EventStore.Core.Util; -using EventStore.Projections.Core.Services.Processing; -using NUnit.Framework; -using ResolvedEvent = EventStore.ClientAPI.ResolvedEvent; - -namespace EventStore.Projections.Core.Tests.ClientAPI; - -[Category("ClientAPI")] -public abstract class specification_with_standard_projections_runnning : SpecificationWithDirectoryPerTestFixture -{ - protected IEventStoreConnection _conn; - protected UserCredentials _admin = DefaultData.AdminCredentials; - private protected ProjectionManagementTestClient ProjectionClient; - protected virtual TimeSpan StartupTimeout => TimeSpan.FromMinutes(5); - - private Task _projectionsCreated; - private ProjectionsSubsystem _projections; - private MiniNode _node; - - [OneTimeSetUp] - public override async Task TestFixtureSetUp() - { - await base.TestFixtureSetUp(); - - var projectionWorkerThreadCount = GivenWorkerThreadCount(); - var configuration = new ProjectionSubsystemOptions( - projectionWorkerThreadCount, - ProjectionType.All, - false, - TimeSpan.FromMinutes(Opts.ProjectionsQueryExpiryDefault), - Opts.FaultOutOfOrderProjectionsDefault, - 500, - 250); - _projections = new ProjectionsSubsystem(configuration); - _node = new MiniNode( - PathName, - subsystems: [_projections]); - _projectionsCreated = SystemProjections.Created(_projections.LeaderInputBus); - - await _node.Start(StartupTimeout); - await _node.WaitForTcpEndPoint().WithTimeout(StartupTimeout); - _conn = await TestConnectionLifecycle.ReconnectUntilReady( - CreateConnection, - connection => connection.ReadAllEventsForwardAsync(Position.Start, 1, false, _admin), - StartupTimeout); - - ProjectionClient = new ProjectionManagementTestClient(_node.HttpEndPoint, _node.HttpMessageHandler); - - WaitIdle(); - - if (GivenStandardProjectionsRunning()) - { - await EnableStandardProjections(); - } - - WaitIdle(); - try - { - await Given().WithTimeout(TimeSpan.FromSeconds(10)); - } - catch (Exception ex) - { - throw new Exception("Given Failed", ex); - } - - try - { - await When().WithTimeout(TimeSpan.FromSeconds(10)); - } - catch (Exception ex) - { - throw new Exception("When Failed", ex); - } - } - - protected virtual int GivenWorkerThreadCount() - { - return 1; - } - - [TearDown] - public async Task PostTestAsserts() - { - var all = await ProjectionClient.StatisticsAll(); - if (all.Any(p => p.Status == "Faulted")) - { - Assert.Fail("Projections faulted while running the test" + "\r\n" + string.Join("\r\n", all)); - } - } - - protected async Task EnableStandardProjections() - { - await _projectionsCreated; - await EnableProjection(ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection); - await EnableProjection(ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection); - await EnableProjection(ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection); - await EnableProjection(ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection); - } - - protected async Task DisableStandardProjections() - { - await DisableProjection(ProjectionNamesBuilder.StandardProjections.EventByCategoryStandardProjection); - await DisableProjection(ProjectionNamesBuilder.StandardProjections.EventByTypeStandardProjection); - await DisableProjection(ProjectionNamesBuilder.StandardProjections.StreamByCategoryStandardProjection); - await DisableProjection(ProjectionNamesBuilder.StandardProjections.StreamsStandardProjection); - } - - protected virtual bool GivenStandardProjectionsRunning() - { - return true; - } - - protected Task EnableProjection(string name) - { - return ProjectionClient.Enable(name); - } - - protected Task DisableProjection(string name) - { - return ProjectionClient.Disable(name); - } - - protected Task AbortProjection(string name) - { - return ProjectionClient.Abort(name); - } - - protected Task CreateContinuousProjection(string name, string query) - { - return ProjectionClient.CreateContinuous(name, query); - } - - protected Task CreateTransientProjection(string name, string query) - { - return ProjectionClient.CreateTransient(name, query); - } - - [OneTimeTearDown] - public override async Task TestFixtureTearDown() - { - if (_conn != null) - { - try - { - await TestConnectionLifecycle.CloseConnectionAndWait(_conn, TimeSpan.FromSeconds(20)); - } - catch - { - TestConnectionLifecycle.TryCloseConnection(_conn); - } - finally - { - TestConnectionLifecycle.DisposeIfNeeded(_conn); - } - } - - ProjectionClient?.Dispose(); - - if (_node != null) - { - await _node.Shutdown(); - } - - await Task.Delay(1000); - - await base.TestFixtureTearDown(); - } - - protected virtual Task When() => Task.CompletedTask; - - protected virtual Task Given() => Task.CompletedTask; - - protected Task PostEvent(string stream, string eventType, string data) - { - return _conn.AppendToStreamAsync(stream, ExpectedVersion.Any, CreateEvent(eventType, data)); - } - - protected Task HardDeleteStream(string stream) - { - return _conn.DeleteStreamAsync(stream, ExpectedVersion.Any, true, _admin); - } - - protected Task SoftDeleteStream(string stream) - { - return _conn.DeleteStreamAsync(stream, ExpectedVersion.Any, false, _admin); - } - - protected static EventData CreateEvent(string type, string data) - { - return new EventData(Guid.NewGuid(), type, true, Encoding.UTF8.GetBytes(data), Array.Empty()); - } - - private IEventStoreConnection CreateConnection() - { - return TestConnection.CreateMiniNodeClient(_node.TcpEndPoint); - } - - protected void WaitIdle(int multiplier = 1) - { -#if DEBUG - _node.WaitIdle(); -#endif - } - -#pragma warning disable 1998 - protected async Task AssertStreamTail(string streamId, params string[] events) - { -#pragma warning restore 1998 -#if DEBUG - await Task.Delay(TimeSpan.FromMilliseconds(500)); - var result = await _conn.ReadStreamEventsBackwardAsync(streamId, -1, events.Length, true, _admin); - switch (result.Status) - { - case SliceReadStatus.StreamDeleted: - Assert.Fail("Stream '{0}' is deleted", streamId); - break; - case SliceReadStatus.StreamNotFound: - Assert.Fail("Stream '{0}' does not exist", streamId); - break; - case SliceReadStatus.Success: - var resultEventsReversed = result.Events.Reverse().ToArray(); - if (resultEventsReversed.Length < events.Length) - { - DumpFailed("Stream does not contain enough events", streamId, events, result.Events); - } - else - { - for (var index = 0; index < events.Length; index++) - { - var parts = events[index].Split(new char[] { ':' }, 2); - var eventType = parts[0]; - var eventData = parts[1]; - - if (resultEventsReversed[index].Event.EventType != eventType) - { - DumpFailed("Invalid event type", streamId, events, resultEventsReversed); - } - else if (resultEventsReversed[index].Event.DebugDataView() != eventData) - { - DumpFailed("Invalid event body", streamId, events, resultEventsReversed); - } - } - } - - break; - } -#endif - } - -#pragma warning disable 1998 - protected async Task DumpStream(string streamId) - { -#pragma warning restore 1998 -#if DEBUG - var result = await _conn.ReadStreamEventsBackwardAsync(streamId, -1, 100, true, _admin); - switch (result.Status) - { - case SliceReadStatus.StreamDeleted: - Assert.Fail("Stream '{0}' is deleted", streamId); - break; - case SliceReadStatus.StreamNotFound: - Assert.Fail("Stream '{0}' does not exist", streamId); - break; - case SliceReadStatus.Success: - Dump("Dumping..", streamId, result.Events.Reverse().ToArray()); - break; - } -#endif - } - -#if DEBUG - private void DumpFailed(string message, string streamId, string[] events, ResolvedEvent[] resultEvents) - { - var expected = events.Aggregate("", (a, v) => a + ", " + v); - var actual = resultEvents.Aggregate( - "", (a, v) => a + ", " + v.Event.EventType + ":" + v.Event.DebugDataView()); - - var actualMeta = resultEvents.Aggregate( - "", (a, v) => a + "\r\n" + v.Event.EventType + ":" + v.Event.DebugMetadataView()); - - - Assert.Fail( - "Stream: '{0}'\r\n{1}\r\n\r\nExisting events: \r\n{2}\r\n Expected events: \r\n{3}\r\n\r\nActual metas:{4}", - streamId, - message, actual, expected, actualMeta); - } - - protected void Dump(string message, string streamId, ResolvedEvent[] resultEvents) - { - var actual = resultEvents.Aggregate( - "", (a, v) => a + ", " + v.OriginalEvent.EventType + ":" + v.OriginalEvent.DebugDataView()); - - var actualMeta = resultEvents.Aggregate( - "", (a, v) => a + "\r\n" + v.OriginalEvent.EventType + ":" + v.OriginalEvent.DebugMetadataView()); - - - Debug.WriteLine( - "Stream: '{0}'\r\n{1}\r\n\r\nExisting events: \r\n{2}\r\n \r\nActual metas:{3}", streamId, - message, actual, actualMeta); - } -#endif - - protected async Task PostProjection(string query) - { - await CreateContinuousProjection("test-projection", query); - WaitIdle(); - } - - protected async Task PostQuery(string query) - { - await CreateTransientProjection("query", query); - WaitIdle(); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs deleted file mode 100644 index f27cc95a2c..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_created.with_from_all_any_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_posted - : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e) {s.a++;}, - $created: function(s,e){s.a++;}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_created_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":3}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":3}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs deleted file mode 100644 index ac1abcceda..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_created.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed - : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++;}, - type2: function(s,e){s.a++;}, - $created: function(s,e){s.a++;}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":3}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":3}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs deleted file mode 100644 index 85a10ed533..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_created/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_created.with_from_category_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++;}, - type2: function(s,e){s.a++;}, - $created: function(s,e){s.a++;}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":3}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":3}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_a_stream_gets_deleted_before_recovery.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_a_stream_gets_deleted_before_recovery.cs deleted file mode 100644 index 490457acaa..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_a_stream_gets_deleted_before_recovery.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection. - recovery; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - when_running_and_a_stream_gets_deleted_before_recovery : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - WaitIdle(); - await AbortProjection("test-projection"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_events_are_posted.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_events_are_posted.cs deleted file mode 100644 index b504fd9133..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/recovery/when_running_and_events_are_posted.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection. - recovery; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_posted : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await AbortProjection("test-projection"); - WaitIdle(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs deleted file mode 100644 index 4145679908..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_posted : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_a_stream_and_tombstone_postponed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_a_stream_and_tombstone_postponed.cs deleted file mode 100644 index 95c901e8b3..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_a_stream_and_tombstone_postponed.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_posted_but_a_stream_and_tombstone_postponed : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_tombstone.cs deleted file mode 100644 index 814b9fdf36..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_but_tombstone.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_posted_but_tombstone : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - } - - protected override async Task When() - { - await base.When(); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_including_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_including_tombstone.cs deleted file mode 100644 index 8a6a43d102..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_events_are_posted_including_tombstone.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_posted_including_tombstone : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await HardDeleteStream("stream-1"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs deleted file mode 100644 index 92cbdc9750..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_any_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_any_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - when_running_and_then_other_events_tombstone_ant_other_events : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - $any: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - } - - protected override async Task When() - { - await base.When(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await PostEvent("stream-3", "type1", "{}"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail( - "$projections-test-projection-stream-1-result", "Result:{\"a\":2}", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":4}"); - await AssertStreamTail("$projections-test-projection-stream-3-result", "Result:{\"a\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_are_indexed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_are_indexed.cs deleted file mode 100644 index 78a6e908d1..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_are_indexed.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection.recovery; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await AbortProjection("test-projection"); - WaitIdle(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs deleted file mode 100644 index 007be1cfb5..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection.recovery; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - when_running_and_events_get_indexed_before_recovery : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await AbortProjection("test-projection"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs deleted file mode 100644 index 5ae721007f..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - - // required to flush index checkpoint - { - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - } - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {}}, - type1: function(s,e){s.a=(s.a||0) + 1}, - type2: function(s,e){s.a=(s.a||0) + 1}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone.cs deleted file mode 100644 index 24bada3fc9..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed_but_a_stream_and_tombstone : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - - // required to flush index checkpoint - { - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - } - - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_more_events_and_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_more_events_and_tombstone.cs deleted file mode 100644 index 805d511110..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_more_events_and_tombstone.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed_but_more_events_and_tombstone : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - ; - WaitIdle(); - - // required to flush index checkpoint - { - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - } - - await PostEvent("stream-1", "type2", "{}"); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":0,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs deleted file mode 100644 index 1d79a0268f..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed_but_tombstone : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - - // required to flush index checkpoint - { - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - } - - - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {}}, - type1: function(s,e){s.a=1}, - type2: function(s,e){s.a=1}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing.cs deleted file mode 100644 index bce6005c7c..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_no_indexing : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {}}, - type1: function(s,e){s.a=1}, - type2: function(s,e){s.a=1}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - } - - protected override async Task When() - { - await base.When(); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail( - "$projections-test-projection-stream-1-result", "Result:{\"a\":1}", "Result:{\"a\":1,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing_and_other_events.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing_and_other_events.cs deleted file mode 100644 index ee8600e626..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_all_foreach_projection/when_running_and_no_indexing_and_other_events.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_all_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_no_indexing_and_other_events : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await PostProjection(@" -fromAll().foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - } - - protected override async Task When() - { - await base.When(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await PostEvent("stream-3", "type1", "{}"); - WaitIdle(); - } - - [Test, Category("Network")] - public async Task receives_deleted_notification() - { - await AssertStreamTail( - "$projections-test-projection-stream-1-result", "Result:{\"a\":2}", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":4}"); - await AssertStreamTail("$projections-test-projection-stream-3-result", "Result:{\"a\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_are_indexed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_are_indexed.cs deleted file mode 100644 index 29bad35c98..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_are_indexed.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection. - recovery; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -// ReSharper disable once InconsistentNaming -public class when_running_and_events_are_indexed - : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - //todo replace with a deterministic fix rather than just covering the potential race condition #2236 - await PostEvent("stream-3", "type1", "{}"); - await PostEvent("stream-3", "type2", "{}"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await AbortProjection("test-projection"); - WaitIdle(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs deleted file mode 100644 index 4f745d8317..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/recovery/when_running_and_events_get_indexed_before_recovery.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection. - recovery; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - when_running_and_events_get_indexed_before_recovery - : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await AbortProjection("test-projection"); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await EnableProjection("test-projection"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs deleted file mode 100644 index b3d2878734..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await HardDeleteStream("stream-1"); - WaitIdle(); - await EnableStandardProjections(); - } - - protected override async Task When() - { - await base.When(); - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {}}, - type1: function(s,e){s.a=(s.a||0) + 1}, - type2: function(s,e){s.a=(s.a||0) + 1}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":2}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone_postponed.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone_postponed.cs deleted file mode 100644 index 1d66579cf6..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_a_stream_and_tombstone_postponed.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -// ReSharper disable once InconsistentNaming -public class when_running_and_events_are_indexed_but_a_stream_and_tombstone_postponed : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await DisableStandardProjections(); - WaitIdle(); - - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - // SUT projection must have been joined heading reader - await EnableStandardProjections(); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-3", "type1", "{}"); - await PostEvent("stream-3", "type2", "{}"); - WaitIdle(); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await PostEvent("stream-3", "type1", "{}"); - await PostEvent("stream-3", "type2", "{}"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs deleted file mode 100644 index 5555dfa9a1..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_but_tombstone.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed_but_tombstone : specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - } - - protected override async Task When() - { - await base.When(); - await HardDeleteStream("stream-1"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":2,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_including_tombstone.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_including_tombstone.cs deleted file mode 100644 index 05026ca479..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_events_are_indexed_including_tombstone.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class when_running_and_events_are_indexed_including_tombstone : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await HardDeleteStream("stream-1"); - WaitIdle(); - await EnableStandardProjections(); - WaitIdle(); - } - - protected override async Task When() - { - await base.When(); - - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1}, -}).outputState(); -"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await DumpStream("$ce-stream"); - await AssertStreamTail("$projections-test-projection-stream-1-result", "Result:{\"a\":0,\"deleted\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs deleted file mode 100644 index d8087d6be7..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/when_handling_deleted/with_from_category_foreach_projection/when_running_and_then_other_events_tombstone_ant_other_events.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Threading.Tasks; -using EventStore.Core.Tests; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI.when_handling_deleted.with_from_category_foreach_projection; - -[TestFixture(typeof(LogFormat.V2), typeof(string))] -public class - when_running_and_then_other_events_tombstone_ant_other_events : - specification_with_standard_projections_runnning -{ - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - - protected override async Task Given() - { - await base.Given(); - await PostProjection(@" -fromCategory('stream').foreachStream().when({ - $init: function(){return {a:0}}, - type1: function(s,e){s.a++}, - type2: function(s,e){s.a++}, - $deleted: function(s,e){s.deleted=1;}, -}).outputState(); -"); - WaitIdle(); - await EnableStandardProjections(); - } - - protected override async Task When() - { - await base.When(); - await PostEvent("stream-1", "type1", "{}"); - await PostEvent("stream-1", "type2", "{}"); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - WaitIdle(); - await HardDeleteStream("stream-1"); - WaitIdle(); - await PostEvent("stream-2", "type1", "{}"); - await PostEvent("stream-2", "type2", "{}"); - await PostEvent("stream-3", "type1", "{}"); - WaitIdle(); - } - - [Test, Category("Network")] - [Ignore("Regression")] - public async Task receives_deleted_notification() - { - await AssertStreamTail( - "$projections-test-projection-stream-1-result", "Result:{\"a\":2}", "Result:{\"a\":2,\"deleted\":1}"); - await AssertStreamTail("$projections-test-projection-stream-2-result", "Result:{\"a\":4}"); - await AssertStreamTail("$projections-test-projection-stream-3-result", "Result:{\"a\":1}"); - } -} diff --git a/src/EventStore.Projections.Core.Tests/ClientAPI/with_standard_projections_running.cs b/src/EventStore.Projections.Core.Tests/ClientAPI/with_standard_projections_running.cs deleted file mode 100644 index 6027793c96..0000000000 --- a/src/EventStore.Projections.Core.Tests/ClientAPI/with_standard_projections_running.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System; -using System.Text; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Core.Bus; -using EventStore.Core.Tests; -using EventStore.Projections.Core.Services.Processing; -using EventStore.Projections.Core.Services.Processing.Checkpointing; -using Newtonsoft.Json.Linq; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.ClientAPI -{ - namespace with_standard_projections_running - { - public abstract class when_deleting_stream_base - : specification_with_standard_projections_runnning - { - [Test, Category("Network")] - public async Task streams_stream_exists() - { - Assert.AreEqual( - SliceReadStatus.Success, - (await _conn.ReadStreamEventsForwardAsync("$streams", 0, 10, false, _admin)).Status); - } - - [Test, Category("Network")] - public async Task deleted_stream_events_are_indexed() - { - await Task.Delay(500); //give the projection time to catchup... - var slice = await _conn.ReadStreamEventsForwardAsync("$ce-cat", 0, 10, true, _admin); - Assert.AreEqual(SliceReadStatus.Success, slice.Status); - - Assert.AreEqual(3, slice.Events.Length); - var deletedLinkMetadata = slice.Events[2].Link.Metadata; - Assert.IsNotNull(deletedLinkMetadata); - - var checkpointTag = Encoding.UTF8.GetString(deletedLinkMetadata).ParseCheckpointExtraJson(); - Assert.IsTrue(checkpointTag.TryGetValue("$deleted", out _)); - Assert.IsTrue(checkpointTag.TryGetValue("$o", out var originalStream)); - Assert.AreEqual("cat-1", ((JValue)originalStream).Value); - } - - [Test, Category("Network")] - public async Task deleted_stream_events_are_indexed_as_deleted() - { - var slice = await _conn.ReadStreamEventsForwardAsync("$et-$deleted", 0, 10, true, _admin); - Assert.AreEqual(SliceReadStatus.Success, slice.Status); - - Assert.AreEqual(1, slice.Events.Length); - } - - protected override async Task When() - { - await base.When(); - var r1 = await _conn.AppendToStreamAsync( - "cat-1", ExpectedVersion.NoStream, _admin, - new EventData(Guid.NewGuid(), "type1", true, Encoding.UTF8.GetBytes("{}"), null)) - ; - - var r2 = await _conn.AppendToStreamAsync( - "cat-1", r1.NextExpectedVersion, _admin, - new EventData(Guid.NewGuid(), "type1", true, Encoding.UTF8.GetBytes("{}"), null)); - - await _conn.DeleteStreamAsync("cat-1", r2.NextExpectedVersion, GivenDeleteHardDeleteStreamMode(), - _admin) - ; - WaitIdle(); - if (!GivenStandardProjectionsRunning()) - { - await EnableStandardProjections(); - WaitIdle(); - } - } - - protected abstract bool GivenDeleteHardDeleteStreamMode(); - } - - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class when_hard_deleting_stream : when_deleting_stream_base - { - protected override bool GivenDeleteHardDeleteStreamMode() - { - return true; - } - } - - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class when_soft_deleting_stream : when_deleting_stream_base - { - protected override bool GivenDeleteHardDeleteStreamMode() - { - return false; - } - } - - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class when_hard_deleting_stream_and_starting_standard_projections : when_deleting_stream_base - { - protected override bool GivenDeleteHardDeleteStreamMode() - { - return true; - } - - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - } - - [TestFixture(typeof(LogFormat.V2), typeof(string))] - public class when_soft_deleting_stream_and_starting_standard_projections : when_deleting_stream_base - { - protected override bool GivenDeleteHardDeleteStreamMode() - { - return false; - } - - protected override bool GivenStandardProjectionsRunning() - { - return false; - } - } - } -} diff --git a/src/EventStore.Projections.Core.Tests/EventStore.Projections.Core.Tests.csproj b/src/EventStore.Projections.Core.Tests/EventStore.Projections.Core.Tests.csproj index 8cdd560ea4..93ac1fdb56 100644 --- a/src/EventStore.Projections.Core.Tests/EventStore.Projections.Core.Tests.csproj +++ b/src/EventStore.Projections.Core.Tests/EventStore.Projections.Core.Tests.csproj @@ -8,8 +8,6 @@ - - diff --git a/src/EventStore.Projections.Core.Tests/Playground/Launchpad.cs b/src/EventStore.Projections.Core.Tests/Playground/Launchpad.cs deleted file mode 100644 index 0441b45067..0000000000 --- a/src/EventStore.Projections.Core.Tests/Playground/Launchpad.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using EventStore.Common.Utils; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.Playground { - [TestFixture, Explicit, Category("Manual")] - public class Launchpad : LaunchpadBase { - private IDisposable _vnodeProcess; - private IDisposable _managerProcess; - private IDisposable _clientProcess; - private IDisposable _projectionsProcess; - - private string _binFolder; - private Dictionary _environment; - private string _dbPath; - - [SetUp] - public void Setup() { - if (!OS.IsUnix) - AllocConsole(); // this is required to keep console open after executeassemly has exited - - _binFolder = AppDomain.CurrentDomain.BaseDirectory; - _dbPath = Path.Combine(_binFolder, DateTime.UtcNow.Ticks.ToString()); - _environment = new Dictionary {{"EVENTSTORE_LOGSDIR", _dbPath}}; - - var vnodeExecutable = Path.Combine(_binFolder, @"EventStore.ClusterNode.exe"); - var managerExecutable = Path.Combine(_binFolder, @"EventStore.Manager.exe"); - - - string vnodeCommandLine = - string.Format( - @"--ip=127.0.0.1 --db={0} --int-tcp-port=3111 --ext-tcp-port=1111 --http-port=2111 --manager-ip=127.0.0.1 --manager-port=30777 --nodes-count=1 --fake-dns --prepare-count=1 --commit-count=1", - _dbPath); - string managerCommandLine = @"--ip=127.0.0.1 --port=30777 --nodes-count=1 --fake-dns"; - - _managerProcess = _launch(managerExecutable, managerCommandLine, _environment); - Thread.Sleep(500); - _vnodeProcess = _launch(vnodeExecutable, vnodeCommandLine, _environment); - } - - [TearDown] - public void Teardown() { - if (_managerProcess != null) _managerProcess.Dispose(); - if (_vnodeProcess != null) _vnodeProcess.Dispose(); - if (_clientProcess != null) _clientProcess.Dispose(); - if (_projectionsProcess != null) _projectionsProcess.Dispose(); - } - - public void LaunchFlood() { - var clientExecutable = Path.Combine(_binFolder, @"EventStore.Client.exe"); - string clientCommandLine = @"-i 127.0.0.1 -t 1111 -h 2111 WRFL 1 100"; - _clientProcess = _launch(clientExecutable, clientCommandLine, _environment); - } - - public void LaunchProjections() { - var clientExecutable = Path.Combine(_binFolder, @"EventStore.Projections.Worker.exe"); - string clientCommandLine = string.Format(@"--ip 127.0.0.1 -t 1111 -h 2111 --db {0}", _dbPath); - _projectionsProcess = _launch(clientExecutable, clientCommandLine, _environment); - } - - [Test] - public void WriteFloodAndProjections() { - Thread.Sleep(5000); - LaunchFlood(); - Thread.Sleep(5000); - LaunchProjections(); - Thread.Sleep(500); - LaunchFlood(); - Thread.Sleep(160000); - } - - [Test] - public void JustProjections() { - Thread.Sleep(3500); - LaunchProjections(); - Thread.Sleep(50000); - } - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool AllocConsole(); - } -} diff --git a/src/EventStore.Projections.Core.Tests/Playground/Launchpad2.cs b/src/EventStore.Projections.Core.Tests/Playground/Launchpad2.cs deleted file mode 100644 index 0718dfeb09..0000000000 --- a/src/EventStore.Projections.Core.Tests/Playground/Launchpad2.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using EventStore.Common.Utils; -using NUnit.Framework; - -namespace EventStore.Projections.Core.Tests.Playground { - [TestFixture, Explicit, Category("Manual")] - public class Launchpad2 : LaunchpadBase { - private IDisposable _vnodeProcess; - private IDisposable _clientProcess; - - private string _binFolder; - private Dictionary _environment; - private string _dbPath; - - [SetUp] - public void Setup() { - if (!OS.IsUnix) - AllocConsole(); // this is required to keep console open after executeassemly has exited - - _binFolder = AppDomain.CurrentDomain.BaseDirectory; - _dbPath = Path.Combine(_binFolder, DateTime.UtcNow.Ticks.ToString()); - _environment = new Dictionary {{"EVENTSTORE_LOGSDIR", _dbPath}}; - - var vnodeExecutable = Path.Combine(_binFolder, @"EventStore.Projections.Worker.exe"); - - - string vnodeCommandLine = - string.Format( - @"--ip=127.0.0.1 --db={0} --stats-frequency-sec=10 --int-tcp-port=3111 --ext-tcp-port=1111 --http-port=2111", - _dbPath); - - _vnodeProcess = _launch(vnodeExecutable, vnodeCommandLine, _environment); - } - - [TearDown] - public void Teardown() { - if (_vnodeProcess != null) _vnodeProcess.Dispose(); - if (_clientProcess != null) _clientProcess.Dispose(); - } - - public void LaunchFlood() { - var clientExecutable = Path.Combine(_binFolder, @"EventStore.Client.exe"); - string clientCommandLine = @"--ip 127.0.0.1 --tcp-port 1111 --http-port 2111 WRFL 1 100"; - _clientProcess = _launch(clientExecutable, clientCommandLine, _environment); - } - - [Test] - public void RunSingle() { - Thread.Sleep(60000); - } - - [Test] - public void RunSingleAndFlood() { - Thread.Sleep(4000); - LaunchFlood(); - Thread.Sleep(10000); - LaunchFlood(); - Thread.Sleep(20000); - } - - [DllImport("kernel32.dll", SetLastError = true)] - private static extern bool AllocConsole(); - } -} diff --git a/src/EventStore.Projections.Core.Tests/Services/SpecificationWithEmittedStreamsTrackerAndDeleter.cs b/src/EventStore.Projections.Core.Tests/Services/SpecificationWithEmittedStreamsTrackerAndDeleter.cs index 22ae22be66..a804f14d7b 100644 --- a/src/EventStore.Projections.Core.Tests/Services/SpecificationWithEmittedStreamsTrackerAndDeleter.cs +++ b/src/EventStore.Projections.Core.Tests/Services/SpecificationWithEmittedStreamsTrackerAndDeleter.cs @@ -1,15 +1,33 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; using System.Threading.Tasks; +using EventStore.Client.Streams; using EventStore.Core.Helpers; using EventStore.Core.Messages; -using EventStore.Core.Tests.ClientAPI; +using EventStore.Core.Services.Transport.Grpc; +using EventStore.Core.Tests; +using EventStore.Core.Tests.Helpers; using EventStore.Projections.Core.Services; using EventStore.Projections.Core.Services.Processing; using EventStore.Projections.Core.Services.Processing.Emitting; +using Google.Protobuf; +using Grpc.Core; +using Grpc.Net.Client; +using NUnit.Framework; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; +using ReadEvent = EventStore.Client.Streams.ReadResp.Types.ReadEvent; +using StreamsClient = EventStore.Client.Streams.Streams.StreamsClient; namespace EventStore.Projections.Core.Tests.Services; -public abstract class SpecificationWithEmittedStreamsTrackerAndDeleter : SpecificationWithMiniNode +public abstract class SpecificationWithEmittedStreamsTrackerAndDeleter : SpecificationWithDirectoryPerTestFixture { + private GrpcChannel _channel; + protected MiniNode _node; + protected StreamsClient _client; protected IEmittedStreamsTracker _emittedStreamsTracker; protected IEmittedStreamsDeleter _emittedStreamsDeleter; protected ProjectionNamesBuilder _projectionNamesBuilder; @@ -17,8 +35,32 @@ public abstract class SpecificationWithEmittedStreamsTrackerAndDeleter(PathName); + await _node.Start(); + _channel = GrpcChannel.ForAddress(new UriBuilder { Scheme = Uri.UriSchemeHttps }.Uri, + new GrpcChannelOptions { HttpClient = _node.HttpClient, DisposeHttpClient = false }); + _client = new StreamsClient(_channel); + await Given().WithTimeout(Timeout); + await When().WithTimeout(Timeout); + } + + [OneTimeTearDown] + public override async Task TestFixtureTearDown() + { + _channel?.Dispose(); + await _node.Shutdown(); + await base.TestFixtureTearDown(); + } + + protected virtual Task Given() { _ioDispatcher = new IODispatcher(_node.Node.MainQueue, _node.Node.MainQueue, true); _node.Node.MainBus.Subscribe(_ioDispatcher.BackwardReader); @@ -38,4 +80,79 @@ protected override Task Given() _projectionNamesBuilder.GetEmittedStreamsCheckpointName()); return Task.CompletedTask; } + + protected async Task AppendEvent(string stream, string eventType, byte[] data) + { + using var call = _client.Append(AdminCallOptions()); + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) } + } + }); + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Uuid.NewUuid().ToDto(), + Data = ByteString.CopyFrom(data), + CustomMetadata = ByteString.Empty, + Metadata = { + { GrpcMetadata.Type, eventType }, + { GrpcMetadata.ContentType, GrpcMetadata.ContentTypes.ApplicationJson } + } + } + }); + await call.RequestStream.CompleteAsync(); + await call.ResponseAsync; + } + + protected async Task ReadEvents(string stream, int count) + { + using var call = _client.Read(new ReadReq + { + Options = new() + { + Stream = new() + { + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) }, + Start = new() + }, + ReadDirection = ReadReq.Types.Options.Types.ReadDirection.Forwards, + Count = (ulong)count, + NoFilter = new(), + UuidOption = new() { Structured = new() } + } + }, AdminCallOptions()); + var events = new List(); + while (await call.ResponseStream.MoveNext(default)) + if (call.ResponseStream.Current.Event is { } resolvedEvent) + events.Add(resolvedEvent); + return events.ToArray(); + } + + protected async Task WaitForEvents(string stream, int count) + { + var deadline = DateTime.UtcNow + Timeout; + ReadEvent[] events; + do + { + events = await ReadEvents(stream, count); + if (events.Length >= count) + return events; + await Task.Delay(50); + } while (DateTime.UtcNow < deadline); + + return events; + } + + private static CallOptions AdminCallOptions() => new( + credentials: CallCredentials.FromInterceptor((_, metadata) => + { + metadata.Add("authorization", + $"Basic {Convert.ToBase64String(Encoding.ASCII.GetBytes("admin:changeit"))}"); + return Task.CompletedTask; + })); } diff --git a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_an_existing_emitted_streams_stream.cs b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_an_existing_emitted_streams_stream.cs index 2db800c545..8802e0db6c 100644 --- a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_an_existing_emitted_streams_stream.cs +++ b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_an_existing_emitted_streams_stream.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI; using EventStore.Core.Tests; using EventStore.Projections.Core.Services.Processing; using EventStore.Projections.Core.Services.Processing.Checkpointing; @@ -18,19 +17,12 @@ public class with_an_existing_emitted_streams_stream : Sp protected ManualResetEvent _resetEvent = new ManualResetEvent(false); private string _testStreamName = "test_stream"; private ManualResetEvent _eventAppeared = new ManualResetEvent(false); - private EventStore.ClientAPI.SystemData.UserCredentials _credentials; protected override async Task Given() { - _credentials = new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit"); _onDeleteStreamCompleted = () => { _resetEvent.Set(); }; await base.Given(); - var sub = await _conn.SubscribeToStreamAsync(_projectionNamesBuilder.GetEmittedStreamsName(), true, (s, evnt) => - { - _eventAppeared.Set(); - return Task.CompletedTask; - }, userCredentials: _credentials); _emittedStreamsTracker.TrackEmittedStream(new EmittedEvent[] { new EmittedDataEvent( @@ -38,18 +30,12 @@ protected override async Task Given() "data", null, CheckpointTag.FromPosition(0, 100, 50), null), }); - if (!_eventAppeared.WaitOne(TimeSpan.FromSeconds(5))) + var events = await WaitForEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 1); + if (events.Length != 1) { Assert.Fail("Timed out waiting for emitted stream event"); } - - sub.Unsubscribe(); - - var emittedStreamResult = - await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, 1, false, - _credentials); - Assert.AreEqual(1, emittedStreamResult.Events.Length); - Assert.AreEqual(SliceReadStatus.Success, emittedStreamResult.Status); + _eventAppeared.Set(); } protected override Task When() @@ -66,25 +52,22 @@ protected override Task When() [Test] public async Task should_have_deleted_the_tracked_emitted_stream() { - var result = await _conn.ReadStreamEventsForwardAsync(_testStreamName, 0, 1, false, - new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit")); - Assert.AreEqual(SliceReadStatus.StreamNotFound, result.Status); + var events = await ReadEvents(_testStreamName, 1); + Assert.AreEqual(0, events.Length); } [Test] public async Task should_have_deleted_the_checkpoint_stream() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsCheckpointName(), - 0, 1, false, new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit")); - Assert.AreEqual(SliceReadStatus.StreamNotFound, result.Status); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsCheckpointName(), 1); + Assert.AreEqual(0, events.Length); } [Test] public async Task should_have_deleted_the_emitted_streams_stream() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, 1, - false, new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit")); - Assert.AreEqual(SliceReadStatus.StreamNotFound, result.Status); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 1); + Assert.AreEqual(0, events.Length); } } diff --git a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_multiple_tracked_streams.cs b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_multiple_tracked_streams.cs index 68fbff7581..e984a4d928 100644 --- a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_multiple_tracked_streams.cs +++ b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_deleter/when_deleting/with_multiple_tracked_streams.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI; using EventStore.Common.Utils; using EventStore.Core.Tests; using EventStore.Projections.Core.Services.Processing; @@ -20,25 +19,16 @@ public class with_multiple_tracked_streams : Specificatio protected CountdownEvent _eventAppeared; private int _numberOfTrackedEvents = 50; private string _testStreamFormat = "test_stream_{0}"; - private EventStore.ClientAPI.SystemData.UserCredentials _credentials; protected override async Task Given() { - _credentials = new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit"); _eventAppeared = new CountdownEvent(_numberOfTrackedEvents); _onDeleteStreamCompleted = () => { _resetEvent.Set(); }; await base.Given(); - var sub = await _conn.SubscribeToStreamAsync(_projectionNamesBuilder.GetEmittedStreamsName(), true, (s, evnt) => - { - _eventAppeared.Signal(); - return Task.CompletedTask; - }, userCredentials: _credentials); - for (int i = 0; i < _numberOfTrackedEvents; i++) { - await _conn.AppendToStreamAsync(String.Format(_testStreamFormat, i), ExpectedVersion.Any, - new EventData(Guid.NewGuid(), "type1", true, Helper.UTF8NoBom.GetBytes("data"), null)); + await AppendEvent(String.Format(_testStreamFormat, i), "type1", Helper.UTF8NoBom.GetBytes("data")); _emittedStreamsTracker.TrackEmittedStream(new EmittedEvent[] { new EmittedDataEvent( String.Format(_testStreamFormat, i), Guid.NewGuid(), "type1", true, @@ -46,16 +36,13 @@ await _conn.AppendToStreamAsync(String.Format(_testStreamFormat, i), ExpectedVer }); } - if (!_eventAppeared.Wait(TimeSpan.FromSeconds(10))) + var events = await WaitForEvents(_projectionNamesBuilder.GetEmittedStreamsName(), _numberOfTrackedEvents); + if (events.Length != _numberOfTrackedEvents) { Assert.Fail("Timed out waiting for emitted streams"); } - - var emittedStreamResult = - await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, - _numberOfTrackedEvents, false, _credentials); - Assert.AreEqual(_numberOfTrackedEvents, emittedStreamResult.Events.Length); - Assert.AreEqual(SliceReadStatus.Success, emittedStreamResult.Status); + while (_eventAppeared.CurrentCount > 0) + _eventAppeared.Signal(); } protected override Task When() @@ -74,9 +61,8 @@ public async Task should_have_deleted_the_tracked_emitted_streams() { for (int i = 0; i < _numberOfTrackedEvents; i++) { - var result = await _conn.ReadStreamEventsForwardAsync(String.Format(_testStreamFormat, i), 0, 1, false, - new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit")); - Assert.AreEqual(SliceReadStatus.StreamNotFound, result.Status); + var events = await ReadEvents(String.Format(_testStreamFormat, i), 1); + Assert.AreEqual(0, events.Length); } } @@ -84,16 +70,14 @@ public async Task should_have_deleted_the_tracked_emitted_streams() [Test] public async Task should_have_deleted_the_checkpoint_stream() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsCheckpointName(), - 0, 1, false, new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit")); - Assert.AreEqual(SliceReadStatus.StreamNotFound, result.Status); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsCheckpointName(), 1); + Assert.AreEqual(0, events.Length); } [Test] public async Task should_have_deleted_the_emitted_streams_stream() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, 1, - false, new EventStore.ClientAPI.SystemData.UserCredentials("admin", "changeit")); - Assert.AreEqual(SliceReadStatus.StreamNotFound, result.Status); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 1); + Assert.AreEqual(0, events.Length); } } diff --git a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_disabled.cs b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_disabled.cs index 9bc197b48e..4bc79bcc51 100644 --- a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_disabled.cs +++ b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_disabled.cs @@ -1,7 +1,6 @@ using System; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI.SystemData; using EventStore.Core.Tests; using EventStore.Projections.Core.Services.Processing; using EventStore.Projections.Core.Services.Processing.Checkpointing; @@ -15,7 +14,6 @@ namespace EventStore.Projections.Core.Tests.Services.emitted_streams_tracker.whe public class with_tracking_disabled : SpecificationWithEmittedStreamsTrackerAndDeleter { private CountdownEvent _eventAppeared = new CountdownEvent(1); - private UserCredentials _credentials = new UserCredentials("admin", "changeit"); protected override TimeSpan Timeout { get; } = TimeSpan.FromSeconds(10); @@ -27,28 +25,20 @@ protected override Task Given() protected override async Task When() { - var sub = await _conn.SubscribeToStreamAsync(_projectionNamesBuilder.GetEmittedStreamsName(), true, (s, evnt) => - { - _eventAppeared.Signal(); - return Task.CompletedTask; - }, userCredentials: _credentials); - _emittedStreamsTracker.TrackEmittedStream(new EmittedEvent[] { new EmittedDataEvent( "test_stream", Guid.NewGuid(), "type1", true, "data", null, CheckpointTag.FromPosition(0, 100, 50), null, null) }); - _eventAppeared.Wait(TimeSpan.FromSeconds(5)); - sub.Unsubscribe(); + await Task.Delay(100); } [Test] public async Task should_write_a_stream_tracked_event() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, 200, - false, _credentials); - Assert.AreEqual(0, result.Events.Length); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 200); + Assert.AreEqual(0, events.Length); Assert.AreEqual(1, _eventAppeared.CurrentCount); //no event appeared should get through } } diff --git a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled.cs b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled.cs index aaabcce695..97ef20b960 100644 --- a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled.cs +++ b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled.cs @@ -1,8 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI.Common.Utils; -using EventStore.ClientAPI.SystemData; +using EventStore.Common.Utils; using EventStore.Core.Tests; using EventStore.Projections.Core.Services.Processing; using EventStore.Projections.Core.Services.Processing.Checkpointing; @@ -16,33 +15,26 @@ namespace EventStore.Projections.Core.Tests.Services.emitted_streams_tracker.whe public class with_tracking_enabled : SpecificationWithEmittedStreamsTrackerAndDeleter { private CountdownEvent _eventAppeared = new CountdownEvent(1); - private UserCredentials _credentials = new UserCredentials("admin", "changeit"); protected override async Task When() { - var sub = await _conn.SubscribeToStreamAsync(_projectionNamesBuilder.GetEmittedStreamsName(), true, (s, evnt) => - { - _eventAppeared.Signal(); - return Task.CompletedTask; - }, userCredentials: _credentials); - _emittedStreamsTracker.TrackEmittedStream(new EmittedEvent[] { new EmittedDataEvent( "test_stream", Guid.NewGuid(), "type1", true, "data", null, CheckpointTag.FromPosition(0, 100, 50), null, null) }); - _eventAppeared.Wait(TimeSpan.FromSeconds(5)); - sub.Unsubscribe(); + var events = await WaitForEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 1); + if (events.Length == 1) + _eventAppeared.Signal(); } [Test] public async Task should_write_a_stream_tracked_event() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, 200, - false, _credentials); - Assert.AreEqual(1, result.Events.Length); - Assert.AreEqual("test_stream", Helper.UTF8NoBom.GetString(result.Events[0].Event.Data)); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 200); + Assert.AreEqual(1, events.Length); + Assert.AreEqual("test_stream", Helper.UTF8NoBom.GetString(events[0].Event.Data.ToByteArray())); Assert.AreEqual(0, _eventAppeared.CurrentCount); } } diff --git a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled_with_duplicate_event_streams.cs b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled_with_duplicate_event_streams.cs index f83e80b5ca..49e5817a99 100644 --- a/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled_with_duplicate_event_streams.cs +++ b/src/EventStore.Projections.Core.Tests/Services/emitted_streams_tracker/when_tracking/with_tracking_enabled_with_duplicate_event_streams.cs @@ -1,8 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using EventStore.ClientAPI.Common.Utils; -using EventStore.ClientAPI.SystemData; +using EventStore.Common.Utils; using EventStore.Core.Tests; using EventStore.Projections.Core.Services.Processing; using EventStore.Projections.Core.Services.Processing.Checkpointing; @@ -16,18 +15,11 @@ namespace EventStore.Projections.Core.Tests.Services.emitted_stream_manager.when public class with_tracking_enabled_with_duplicate_event_streams : SpecificationWithEmittedStreamsTrackerAndDeleter { private CountdownEvent _eventAppeared = new CountdownEvent(2); - private UserCredentials _credentials = new UserCredentials("admin", "changeit"); protected override TimeSpan Timeout { get; } = TimeSpan.FromSeconds(10); protected override async Task When() { - var sub = await _conn.SubscribeToStreamAsync(_projectionNamesBuilder.GetEmittedStreamsName(), true, (s, evnt) => - { - _eventAppeared.Signal(); - return Task.CompletedTask; - }, userCredentials: _credentials); - _emittedStreamsTracker.TrackEmittedStream(new EmittedEvent[] { new EmittedDataEvent( "test_stream", Guid.NewGuid(), "type1", true, @@ -37,17 +29,17 @@ protected override async Task When() "data", null, CheckpointTag.FromPosition(0, 100, 50), null, null) }); - _eventAppeared.Wait(TimeSpan.FromSeconds(5)); - sub.Unsubscribe(); + var events = await WaitForEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 1); + if (events.Length == 1) + _eventAppeared.Signal(); } [Test] public async Task should_at_best_attempt_to_track_a_unique_list_of_streams() { - var result = await _conn.ReadStreamEventsForwardAsync(_projectionNamesBuilder.GetEmittedStreamsName(), 0, 200, - false, _credentials); - Assert.AreEqual(1, result.Events.Length); - Assert.AreEqual("test_stream", Helper.UTF8NoBom.GetString(result.Events[0].Event.Data)); + var events = await ReadEvents(_projectionNamesBuilder.GetEmittedStreamsName(), 200); + Assert.AreEqual(1, events.Length); + Assert.AreEqual("test_stream", Helper.UTF8NoBom.GetString(events[0].Event.Data.ToByteArray())); Assert.AreEqual(1, _eventAppeared.CurrentCount); //only 1 event appeared should get through } } diff --git a/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_event_filter.cs b/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_event_filter.cs index d463501bcd..9cb6586c68 100644 --- a/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_event_filter.cs +++ b/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_event_filter.cs @@ -1,4 +1,4 @@ -using EventStore.ClientAPI.Common; +using EventStore.Core.Services; using NUnit.Framework; namespace EventStore.Projections.Core.Tests.Services.event_filter; diff --git a/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_handling_deleted_notifications_event_filter.cs b/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_handling_deleted_notifications_event_filter.cs index 4f387d57be..255ef701ee 100644 --- a/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_handling_deleted_notifications_event_filter.cs +++ b/src/EventStore.Projections.Core.Tests/Services/event_filter/include_everything_handling_deleted_notifications_event_filter.cs @@ -1,4 +1,4 @@ -using EventStore.ClientAPI.Common; +using EventStore.Core.Services; using NUnit.Framework; namespace EventStore.Projections.Core.Tests.Services.event_filter; diff --git a/src/EventStore.Projections.Core.Tests/Services/grpc_service/SpecificationWithNodeAndProjectionSubsystem.cs b/src/EventStore.Projections.Core.Tests/Services/grpc_service/SpecificationWithNodeAndProjectionSubsystem.cs index c607a0b792..93c6e33ffe 100644 --- a/src/EventStore.Projections.Core.Tests/Services/grpc_service/SpecificationWithNodeAndProjectionSubsystem.cs +++ b/src/EventStore.Projections.Core.Tests/Services/grpc_service/SpecificationWithNodeAndProjectionSubsystem.cs @@ -1,24 +1,26 @@ using System; using System.Text; using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.SystemData; +using EventStore.Client.Streams; using EventStore.Common.Options; -using EventStore.Core.Services; +using EventStore.Core.Services.Transport.Grpc; using EventStore.Core.Tests; -using EventStore.Core.Tests.ClientAPI.Helpers; using EventStore.Core.Tests.Helpers; using EventStore.Core.Util; using EventStore.Projections.Core.Services.Processing; +using Google.Protobuf; +using Grpc.Net.Client; using NUnit.Framework; +using GrpcMetadata = EventStore.Core.Services.Transport.Grpc.Constants.Metadata; +using StreamsClient = EventStore.Client.Streams.Streams.StreamsClient; namespace EventStore.Projections.Core.Tests.Services.grpc_service; public abstract class SpecificationWithNodeAndProjectionSubsystem : SpecificationWithDirectoryPerTestFixture { protected MiniNode _node; - protected IEventStoreConnection _connection; - protected UserCredentials _credentials; + private GrpcChannel _channel; + protected StreamsClient _connection; protected TimeSpan _timeout; protected string _tag; protected virtual TimeSpan StartupTimeout => TimeSpan.FromMinutes(5); @@ -30,20 +32,17 @@ public abstract class SpecificationWithNodeAndProjectionSubsystem TestConnection.CreateMiniNodeClient(_node.TcpEndPoint), - connection => connection.ReadAllEventsForwardAsync(Position.Start, 1, false, _credentials), - StartupTimeout); + _channel = GrpcChannel.ForAddress(new UriBuilder { Scheme = Uri.UriSchemeHttps }.Uri, + new GrpcChannelOptions { HttpClient = _node.HttpClient, DisposeHttpClient = false }); + _connection = new StreamsClient(_channel); try { @@ -67,22 +66,7 @@ public override async Task TestFixtureSetUp() [OneTimeTearDown] public override async Task TestFixtureTearDown() { - if (_connection != null) - { - try - { - await TestConnectionLifecycle.CloseConnectionAndWait(_connection, _timeout); - } - catch - { - TestConnectionLifecycle.TryCloseConnection(_connection); - } - finally - { - TestConnectionLifecycle.DisposeIfNeeded(_connection); - } - } - + _channel?.Dispose(); await _node.Shutdown(); await Task.Delay(1000); @@ -101,14 +85,32 @@ protected MiniNode CreateNode() subsystems: [_projectionsSubsystem]); } - protected EventData CreateEvent(string eventType, string data) + protected async Task PostEvent(string stream, string eventType, string data) { - return new EventData(Guid.NewGuid(), eventType, true, Encoding.UTF8.GetBytes(data), null); - } - - protected Task PostEvent(string stream, string eventType, string data) - { - return _connection.AppendToStreamAsync(stream, ExpectedVersion.Any, new[] { CreateEvent(eventType, data) }); + using var call = _connection.Append(); + await call.RequestStream.WriteAsync(new AppendReq + { + Options = new() + { + Any = new(), + StreamIdentifier = new() { StreamName = ByteString.CopyFromUtf8(stream) } + } + }); + await call.RequestStream.WriteAsync(new AppendReq + { + ProposedMessage = new() + { + Id = Uuid.NewUuid().ToDto(), + Data = ByteString.CopyFromUtf8(data), + CustomMetadata = ByteString.Empty, + Metadata = { + { GrpcMetadata.Type, eventType }, + { GrpcMetadata.ContentType, GrpcMetadata.ContentTypes.ApplicationJson } + } + } + }); + await call.RequestStream.CompleteAsync(); + await call.ResponseAsync; } protected string CreateStandardQuery(string stream) diff --git a/src/EventStore.Projections.Core.Tests/Services/projections_manager/when_deleting_a_system_projection.cs b/src/EventStore.Projections.Core.Tests/Services/projections_manager/when_deleting_a_system_projection.cs index 2b46f51a4c..b4c19cba97 100644 --- a/src/EventStore.Projections.Core.Tests/Services/projections_manager/when_deleting_a_system_projection.cs +++ b/src/EventStore.Projections.Core.Tests/Services/projections_manager/when_deleting_a_system_projection.cs @@ -2,7 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using EventStore.ClientAPI.Common.Utils; +using EventStore.Common.Utils; using EventStore.Core.Messages; using EventStore.Core.Messaging; using EventStore.Core.Tests; diff --git a/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs b/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs index 83cfb94daf..f970cd0c05 100644 --- a/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs +++ b/src/EventStore.Projections.Core.Tests/Services/projections_system/when_starting_up.cs @@ -59,11 +59,7 @@ protected override IEnumerable PreWhen() { yield return (new SystemMessage.BecomeFollower(Guid.NewGuid(), MemberInfo.Initial(Guid.NewGuid(), DateTime.UtcNow, VNodeState.Unknown, true, - new IPEndPoint(IPAddress.Loopback, 1111), - new IPEndPoint(IPAddress.Loopback, 1112), - new IPEndPoint(IPAddress.Loopback, 1113), - new IPEndPoint(IPAddress.Loopback, 1114), - new IPEndPoint(IPAddress.Loopback, 1115), null, 0, 0, + new IPEndPoint(IPAddress.Loopback, 1115), null, 0, 1, false ))); diff --git a/src/EventStore.Projections.Core/Services/Management/ManagedProjection.cs b/src/EventStore.Projections.Core/Services/Management/ManagedProjection.cs index 40a8abb7c8..ab131a91eb 100644 --- a/src/EventStore.Projections.Core/Services/Management/ManagedProjection.cs +++ b/src/EventStore.Projections.Core/Services/Management/ManagedProjection.cs @@ -1015,7 +1015,7 @@ private void DeleteStreamCompleted(ClientMessage.DeleteStreamCompleted message, 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. + // Changing this response requires a coordinated public API and Admin UI contract change. // 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) { diff --git a/src/EventStore.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs b/src/EventStore.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs index 0ecd944c9c..24b2a9460f 100644 --- a/src/EventStore.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs +++ b/src/EventStore.Projections.Core/Services/Processing/Emitting/EmittedStreamsDeleter.cs @@ -86,7 +86,7 @@ private void ReadCompleted(ClientMessage.ReadStreamEventsForwardCompleted onRead 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. + // Changing this response requires a coordinated public API and Admin UI contract change. // 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) { @@ -108,7 +108,7 @@ private void ReadCompleted(ClientMessage.ReadStreamEventsForwardCompleted onRead 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. + // Changing this response requires a coordinated public API and Admin UI contract change. // 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) { diff --git a/src/EventStore.TestClient.Tests/CommandInventoryTests.cs b/src/EventStore.TestClient.Tests/CommandInventoryTests.cs new file mode 100644 index 0000000000..29246e7a86 --- /dev/null +++ b/src/EventStore.TestClient.Tests/CommandInventoryTests.cs @@ -0,0 +1,37 @@ +using System; +using System.Linq; +using System.Threading; +using NUnit.Framework; + +namespace EventStore.TestClient.Tests; + +[TestFixture] +public class CommandInventoryTests +{ + private static readonly string[] SupportedCommandKeywords = + [ + "PING", "PINGFL", "PINGFLW", + "WR", "WRJ", "WRFL", "WRFLCA", "WRFLTCP", "WRFLW", + "MWR", "MWRFLW", "TWR", "DEL", + "RDALL", "RD", "RDFL", "WRLT", + "VERIFY", "SUBSCR", "SCAVENGE", "CHKGRPC", "SST" + ]; + + [Test] + public void test_client_preserves_supported_command_surface() + { + using var cancellation = new CancellationTokenSource(); + var client = new Client(new ClientOptions(), cancellation); + var usages = client.GetCommandList() + .Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + Assert.Multiple(() => + { + foreach (var keyword in SupportedCommandKeywords) + { + Assert.That(usages.Any(x => x == keyword || x.StartsWith($"{keyword} ", StringComparison.Ordinal)), + Is.True, $"Missing TestClient command '{keyword}'"); + } + }); + } +} diff --git a/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj b/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj new file mode 100644 index 0000000000..51224f527e --- /dev/null +++ b/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj @@ -0,0 +1,19 @@ + + + true + + + + + + + + + + + + version.properties + PreserveNewest + + + diff --git a/src/EventStore.TestClient/Client.cs b/src/EventStore.TestClient/Client.cs index 28385ba8a0..55d74fb10a 100644 --- a/src/EventStore.TestClient/Client.cs +++ b/src/EventStore.TestClient/Client.cs @@ -4,8 +4,6 @@ using System.Threading; using EventStore.Common.Utils; using EventStore.TestClient.Commands; -using EventStore.TestClient.Commands.DvuBasic; -using Connection = EventStore.Transport.Tcp.TcpTypedConnection; using ILogger = Serilog.ILogger; #pragma warning disable 1591 @@ -20,9 +18,7 @@ public class Client public readonly ClientOptions Options; - public readonly TcpTestClient _tcpTestClient; public readonly GrpcTestClient _grpcTestClient; - public readonly ClientApiTcpTestClient _clientApiTestClient; private readonly CommandsProcessor _commands = new CommandsProcessor(Log); @@ -30,13 +26,9 @@ public Client(ClientOptions options, CancellationTokenSource cancellationTokenSo { Options = options; - var interactiveMode = options.Command.IsEmpty(); - InteractiveMode = options.Command.IsEmpty(); - _tcpTestClient = new TcpTestClient(options, interactiveMode, Log); _grpcTestClient = new GrpcTestClient(options, Log); - _clientApiTestClient = new ClientApiTcpTestClient(options, Log); RegisterProcessors(cancellationTokenSource); } @@ -46,46 +38,25 @@ private void RegisterProcessors(CancellationTokenSource cancellationTokenSource) _commands.Register(new UsageProcessor(_commands), usageProcessor: true); _commands.Register(new ExitProcessor(cancellationTokenSource)); - _commands.Register(new PingProcessor()); - _commands.Register(new PingFloodProcessor()); - _commands.Register(new PingFloodWaitingProcessor()); - - _commands.Register(new WriteProcessor()); - _commands.Register(new WriteJsonProcessor()); - _commands.Register(new WriteFloodProcessor()); - _commands.Register(new WriteFloodClientApiProcessor()); - _commands.Register(new WriteFloodWaitingProcessor()); - - _commands.Register(new MultiWriteProcessor()); - _commands.Register(new MultiWriteFloodWaitingProcessor()); - - _commands.Register(new TransactionWriteProcessor()); - - _commands.Register(new DeleteProcessor()); - - _commands.Register(new ReadAllProcessor()); - _commands.Register(new ReadProcessor()); - _commands.Register(new ReadFloodProcessor()); - - _commands.Register(new WriteLongTermProcessor()); - - _commands.Register(new DvuBasicProcessor()); - _commands.Register(new RunTestScenariosProcessor()); - - _commands.Register(new SubscribeToStreamProcessor()); - - _commands.Register(new ScavengeProcessor()); - - _commands.Register(new TcpSanitazationCheckProcessor()); - - _commands.Register(new SubscriptionStressTestProcessor()); - - // gRPC _commands.Register(new GrpcCommands.ReadAllProcessor()); - _commands.Register(new GrpcCommands.WriteFloodProcessor()); - - // TCP Client API - _commands.Register(new ClientApiTcpCommands.WriteFloodProcessor()); + var writeFlood = new GrpcCommands.WriteFloodProcessor(); + _commands.Register(writeFlood); + + foreach (var processor in GrpcCommands.CompatibilityProcessor.CreateSupportedProcessors()) + _commands.Register(processor); + + _commands.Register(new GrpcCommands.DelegatingProcessor( + "WRFL", + "WRFL [ [ [ [ []]]]]", + writeFlood)); + _commands.Register(new GrpcCommands.DelegatingProcessor( + "WRFLCA", + "WRFLCA [ [ []]]", + writeFlood)); + _commands.Register(new GrpcCommands.DelegatingProcessor( + "WRFLTCP", + "WRFLTCP [ [ [ [ []]]]]", + writeFlood)); } public string GetCommandList() @@ -149,7 +120,7 @@ private int Execute(string[] args, CancellationToken cancellationToken) { Log.Information("Processing command: {command}.", string.Join(" ", args)); - var context = new CommandProcessorContext(_tcpTestClient, _grpcTestClient, _clientApiTestClient, Options.Timeout, + var context = new CommandProcessorContext(_grpcTestClient, Options.Timeout, Log, Options.StatsLog, Options.OutputCsv, new ManualResetEventSlim(true), cancellationToken); int exitCode; diff --git a/src/EventStore.TestClient/ClientApiLoggerBridge.cs b/src/EventStore.TestClient/ClientApiLoggerBridge.cs deleted file mode 100644 index a7dc5f5bd1..0000000000 --- a/src/EventStore.TestClient/ClientApiLoggerBridge.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.TestClient; - -internal class ClientApiLoggerBridge : ClientAPI.ILogger -{ - public static readonly ClientApiLoggerBridge Default = - new ClientApiLoggerBridge(Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, - "client-api")); - - private readonly Serilog.ILogger _log; - - public ClientApiLoggerBridge(ILogger log) - { - Ensure.NotNull(log, "log"); - _log = log; - } - - public void Error(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Error(format); - } - else - { - _log.Error(format, args); - } - } - - public void Error(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Error(ex, format); - } - else - { - _log.Error(ex, format, args); - } - } - - public void Info(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Information(format); - } - else - { - _log.Information(format, args); - } - } - - public void Info(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Information(ex, format); - } - else - { - _log.Information(ex, format, args); - } - } - - public void Debug(string format, params object[] args) - { - if (args.Length == 0) - { - _log.Debug(format); - } - else - { - _log.Debug(format, args); - } - } - - public void Debug(Exception ex, string format, params object[] args) - { - if (args.Length == 0) - { - _log.Debug(ex, format); - } - else - { - _log.Debug(ex, format, args); - } - } -} diff --git a/src/EventStore.TestClient/ClientApiTcpCommands/WriteFloodProcessor.cs b/src/EventStore.TestClient/ClientApiTcpCommands/WriteFloodProcessor.cs deleted file mode 100644 index 4c2951aa5d..0000000000 --- a/src/EventStore.TestClient/ClientApiTcpCommands/WriteFloodProcessor.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Exceptions; -using EventStore.TestClient.Commands; -using EventStore.TestClient.Statistics; -using EventData = EventStore.ClientAPI.EventData; -using ExpectedVersion = EventStore.Core.Data.ExpectedVersion; -using StreamDeletedException = EventStore.ClientAPI.Exceptions.StreamDeletedException; -using WrongExpectedVersionException = EventStore.ClientAPI.Exceptions.WrongExpectedVersionException; - -namespace EventStore.TestClient.ClientApiTcpCommands; - -internal class WriteFloodProcessor : ICmdProcessor -{ - public string Usage - { - // 0 1 2 3 4 5 - get { return "WRFLTCP [ [ [ [ []]]]]"; } - } - - public string Keyword - { - get { return "WRFLTCP"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 5000; - int streamsCnt = 1000; - int size = 256; - int batchSize = 1; - string streamPrefix = ""; - if (args.Length > 0) - { - if (args.Length < 2 || args.Length > 6) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseLong(args[1]); - if (args.Length >= 3) - { - streamsCnt = MetricPrefixValue.ParseInt(args[2]); - } - - if (args.Length >= 4) - { - size = MetricPrefixValue.ParseInt(args[3]); - } - - if (args.Length >= 5) - { - batchSize = MetricPrefixValue.ParseInt(args[4]); - } - - if (args.Length >= 6) - { - streamPrefix = args[5]; - } - } - catch - { - return false; - } - } - - var stats = new WriteFloodStats(Keyword, context.OutputCsv, args); - var monitor = new RequestMonitor(); - WriteFlood(context, stats, clientsCnt, requestsCnt, streamsCnt, size, batchSize, streamPrefix, monitor) - .GetAwaiter().GetResult(); - return true; - } - - private async Task WriteFlood(CommandProcessorContext context, WriteFloodStats stats, int clientsCnt, long requestsCnt, int streamsCnt, - int size, int batchSize, string streamPrefix, RequestMonitor monitor) - { - context.IsAsync(); - - var doneEvent = new ManualResetEventSlim(false); - var clients = new List(); - - long last = 0; - - var streams = Enumerable.Range(0, streamsCnt).Select(x => - string.IsNullOrWhiteSpace(streamPrefix) - ? Guid.NewGuid().ToString() - : $"{streamPrefix}-{x}").ToArray(); - - context.Log.Information("Writing streams randomly between {first} and {last}", - streams.FirstOrDefault(), - streams.LastOrDefault()); - - var start = new TaskCompletionSource(); - stats.StartTime = DateTime.UtcNow; - var sw2 = new Stopwatch(); - var capacity = 2000 / clientsCnt; - var clientTasks = new List(); - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - - var client = context._clientApiTestClient.CreateConnection(); - await client.ConnectAsync(); - clientTasks.Add(RunClient(client, count)); - } - - async Task RunClient(IEventStoreConnection client, long count) - { - var rnd = new Random(); - List pending = new List(capacity); - await start.Task; - - for (int j = 0; j < count; ++j) - { - var events = new EventData[batchSize]; - for (int q = 0; q < batchSize; q++) - { - events[q] = new EventData(Guid.NewGuid(), - "TakeSomeSpaceEvent", false, - Common.Utils.Helper.UTF8NoBom.GetBytes( - "{ \"DATA\" : \"" + new string('*', size) + "\"}"), - Common.Utils.Helper.UTF8NoBom.GetBytes( - "{ \"METADATA\" : \"" + new string('$', 100) + "\"}")); - } - - var corrid = Guid.NewGuid(); - monitor.StartOperation(corrid); - - pending.Add(client.AppendToStreamAsync(streams[rnd.Next(streamsCnt)], ExpectedVersion.Any, events) - .ContinueWith(t => - { - monitor.EndOperation(corrid); - if (t.IsCompletedSuccessfully) - { - Interlocked.Add(ref stats.Succ, batchSize); - } - else - { - if (Interlocked.Increment(ref stats.Fail) % 1000 == 0) - { - Console.Write("#"); - } - - if (t.Exception != null) - { - var exception = t.Exception.Flatten(); - switch (exception.InnerException) - { - case WrongExpectedVersionException _: - Interlocked.Increment(ref stats.WrongExpVersion); - break; - case StreamDeletedException _: - Interlocked.Increment(ref stats.StreamDeleted); - break; - case OperationTimedOutException _: - Interlocked.Increment(ref stats.CommitTimeout); - break; - } - } - } - - Interlocked.Add(ref stats.Succ, batchSize); - if (stats.Succ - last > 1000) - { - last = stats.Succ; - Console.Write("."); - } - - var localAll = Interlocked.Add(ref stats.All, batchSize); - if (localAll % 100000 == 0) - { - stats.Elapsed = sw2.Elapsed; - stats.Rate = 1000.0 * 100000 / stats.Elapsed.TotalMilliseconds; - sw2.Restart(); - context.Log.Debug( - "\nDONE TOTAL {writes} WRITES IN {elapsed} ({rate:0.0}/s) [S:{success}, F:{failures} (WEV:{wrongExpectedVersion}, P:{prepareTimeout}, C:{commitTimeout}, F:{forwardTimeout}, D:{streamDeleted})].", - localAll, stats.Elapsed, stats.Rate, - stats.Succ, stats.Fail, - stats.WrongExpVersion, stats.PrepTimeout, stats.CommitTimeout, stats.ForwardTimeout, stats.StreamDeleted); - stats.WriteStatsToFile(context.StatsLogger); - } - - if (localAll >= requestsCnt) - { - context.Success(); - doneEvent.Set(); - } - })); - if (pending.Count == capacity) - { - await Task.WhenAny(pending); - - while (pending.Count > 0 && Task.WhenAny(pending).IsCompleted) - { - pending.RemoveAll(x => x.IsCompleted); - if (stats.Succ - last > 1000) - { - Console.Write("."); - last = stats.Succ; - } - } - } - } - - if (pending.Count > 0) - { - await Task.WhenAll(pending); - } - } - - var sw = Stopwatch.StartNew(); - sw2.Start(); - start.SetResult(); - await Task.WhenAll(clientTasks); - sw.Stop(); - - clients.ForEach(client => client.Close()); - - context.Log.Information( - "Completed. Successes: {success}, failures: {failures} (WRONG VERSION: {wrongExpectedVersion}, P: {prepareTimeout}, C: {commitTimeout}, F: {forwardTimeout}, D: {streamDeleted})", - stats.Succ, stats.Fail, - stats.WrongExpVersion, stats.PrepTimeout, stats.CommitTimeout, stats.ForwardTimeout, stats.StreamDeleted); - stats.WriteStatsToFile(context.StatsLogger); - - var reqPerSec = (stats.All + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", stats.All, - sw.ElapsedMilliseconds, reqPerSec); - - PerfUtils.LogData( - Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("successes", stats.Succ), PerfUtils.Col("failures", stats.Fail))); - - var failuresRate = (int)(100 * stats.Fail / (stats.Fail + stats.Succ)); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt), failuresRate); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-c{1}-r{2}-st{3}-s{4}-reqPerSec", Keyword, clientsCnt, requestsCnt, streamsCnt, - size), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-c{1}-r{2}-st{3}-s{4}-failureSuccessRate", Keyword, clientsCnt, requestsCnt, - streamsCnt, size), failuresRate); - monitor.GetMeasurementDetails(); - if (Interlocked.Read(ref stats.Succ) != requestsCnt) - { - context.Fail(reason: "There were errors or not all requests completed."); - } - else - { - context.Success(); - } - } -} diff --git a/src/EventStore.TestClient/ClientApiTcpTestClient.cs b/src/EventStore.TestClient/ClientApiTcpTestClient.cs deleted file mode 100644 index ec3781b506..0000000000 --- a/src/EventStore.TestClient/ClientApiTcpTestClient.cs +++ /dev/null @@ -1,44 +0,0 @@ -using EventStore.ClientAPI; -using ILogger = Serilog.ILogger; - -namespace EventStore.TestClient; - -/// -/// A test client that connects using the legacy dotnet TCP client -/// -public class ClientApiTcpTestClient -{ - /// - /// The options specified when starting the EventStore.TestClient - /// - public ClientOptions Options { get; set; } - private ILogger _log; - - /// - /// Constructs a new - /// - /// - /// - public ClientApiTcpTestClient(ClientOptions options, ILogger log) - { - Options = options; - _log = log; - } - - /// - /// Creates a new TCP connection. - /// - /// - public IEventStoreConnection CreateConnection() - { - var connectionString = string.IsNullOrWhiteSpace(Options.ConnectionString) - ? $"ConnectTo=tcp://{Options.Host}:{Options.TcpPort};UseSslConnection={Options.UseTls};ValidateServer={Options.TlsValidateServer}" - : Options.ConnectionString; - _log.Debug("Creating TCP client with connection string '{connectionString}.", connectionString); - - var connectionSettings = ConnectionSettings.Create() - .LimitRetriesForOperationTo(0) - .KeepReconnecting(); - return EventStoreConnection.Create(connectionString, connectionSettings); - } -} diff --git a/src/EventStore.TestClient/ClientOptions.cs b/src/EventStore.TestClient/ClientOptions.cs index 88e7b51c7b..4596b72969 100644 --- a/src/EventStore.TestClient/ClientOptions.cs +++ b/src/EventStore.TestClient/ClientOptions.cs @@ -17,14 +17,9 @@ namespace EventStore.TestClient; public sealed record ClientOptions { public string Host { get; init; } - public int TcpPort { get; init; } public int HttpPort { get; init; } public int Timeout { get; init; } - public int ReadWindow { get; init; } - public int WriteWindow { get; init; } - public int PingWindow { get; init; } public string[] Command { get; init; } - public bool Reconnect { get; set; } public bool UseTls { get; init; } public bool TlsValidateServer { get; init; } @@ -37,13 +32,8 @@ public ClientOptions() { Command = Array.Empty(); Host = IPAddress.Loopback.ToString(); - TcpPort = 1113; HttpPort = 2113; Timeout = -1; - ReadWindow = 2000; - WriteWindow = 2000; - PingWindow = 2000; - Reconnect = true; UseTls = false; TlsValidateServer = false; ConnectionString = string.Empty; diff --git a/src/EventStore.TestClient/CommandProcessorContext.cs b/src/EventStore.TestClient/CommandProcessorContext.cs index fdb720428a..a7db6df513 100644 --- a/src/EventStore.TestClient/CommandProcessorContext.cs +++ b/src/EventStore.TestClient/CommandProcessorContext.cs @@ -31,23 +31,20 @@ public class CommandProcessorContext /// public bool OutputCsv = false; - public readonly TcpTestClient _tcpTestClient; public readonly GrpcTestClient _grpcTestClient; - public readonly ClientApiTcpTestClient _clientApiTestClient; private readonly ManualResetEventSlim _doneEvent; private readonly CancellationToken _cancellationToken; private int _completed; private int _timeout; - public CommandProcessorContext(TcpTestClient tcpTestClient, GrpcTestClient grpcTestClient, - ClientApiTcpTestClient clientApiTestClient, int timeout, ILogger log, ILogger statsLogger, bool outputCsv, + public CancellationToken CancellationToken => _cancellationToken; + + public CommandProcessorContext(GrpcTestClient grpcTestClient, + int timeout, ILogger log, ILogger statsLogger, bool outputCsv, ManualResetEventSlim doneEvent, CancellationToken cancellationToken) { - - _tcpTestClient = tcpTestClient; _grpcTestClient = grpcTestClient; - _clientApiTestClient = clientApiTestClient; Log = log; StatsLogger = statsLogger; _doneEvent = doneEvent; diff --git a/src/EventStore.TestClient/Commands/DeleteProcessor.cs b/src/EventStore.TestClient/Commands/DeleteProcessor.cs deleted file mode 100644 index 7ec096e2a1..0000000000 --- a/src/EventStore.TestClient/Commands/DeleteProcessor.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Diagnostics; -using EventStore.Client.Messages; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; -using OperationResult = EventStore.Client.Messages.OperationResult; - -namespace EventStore.TestClient.Commands; - -internal class DeleteProcessor : ICmdProcessor -{ - public string Usage - { - get { return "DEL [ []]"; } - } - - public string Keyword - { - get { return "DEL"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - var eventStreamId = "test-stream"; - var expectedVersion = ExpectedVersion.Any; - - if (args.Length > 0) - { - if (args.Length > 2) - { - return false; - } - - eventStreamId = args[0]; - if (args.Length == 2) - { - expectedVersion = args[1].Trim().ToUpper() == "ANY" ? ExpectedVersion.Any : int.Parse(args[1]); - } - } - - context.IsAsync(); - var sw = new Stopwatch(); - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information( - "[{remoteEndPoint}, L{localEndPoint}]: Trying to delete event stream '{stream}'...", - conn.RemoteEndPoint, conn.LocalEndPoint, eventStreamId); - var corrid = Guid.NewGuid(); - var deleteDto = new DeleteStream(eventStreamId, expectedVersion, false, true); - var package = new TcpPackage(TcpCommand.DeleteStream, corrid, deleteDto.Serialize()).AsByteArray(); - sw.Start(); - conn.EnqueueSend(package); - }, - handlePackage: (conn, pkg) => - { - sw.Stop(); - context.Log.Information("Delete request took: {elapsed}.", sw.Elapsed); - - if (pkg.Command != TcpCommand.DeleteStreamCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result == OperationResult.Success) - { - context.Log.Information("DELETED event stream {stream}.", eventStreamId); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds)); - context.Success(); - } - else - { - context.Log.Information("DELETION FAILED for event stream {stream}: {message} ({e}).", eventStreamId, - dto.Message, dto.Result); - context.Fail(); - } - - conn.Close(); - }, - connectionClosed: (connection, error) => context.Fail(reason: "Connection was closed prematurely.")); - - context.WaitForCompletion(); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/DvuBasic/BankAccountBasicProducer.cs b/src/EventStore.TestClient/Commands/DvuBasic/BankAccountBasicProducer.cs deleted file mode 100644 index c2c56853d4..0000000000 --- a/src/EventStore.TestClient/Commands/DvuBasic/BankAccountBasicProducer.cs +++ /dev/null @@ -1,292 +0,0 @@ -using System; -using System.Globalization; -using System.Text; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.TestClient.Commands; -using ILogger = Serilog.ILogger; -#pragma warning disable 1591 - -namespace EventStore.TestClient.Commands.DvuBasic; - -internal class BankAccountBasicProducer : IBasicProducer -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - - public string Name - { - get { return "account"; } - } - - public Event Create(int version) - { - var accountObject = BankAccountEventFactory.CreateAccountObject(version); - - var serializedObject = TestClientJson.To(accountObject); - var @event = new Event(Guid.NewGuid(), accountObject.GetType().Name, true, - Helper.UTF8NoBom.GetBytes(serializedObject), new byte[0]); - - return @event; - } - - public bool Equal(int eventVersion, string eventType, byte[] actualData) - { - var generated = BankAccountEventFactory.CreateAccountObject(eventVersion); - object deserialized; - - bool isEqual; - string reason; - if (actualData == null) - { - isEqual = false; - reason = "Null received"; - } - else - { - deserialized = Deserialize(eventType, actualData); - - if (deserialized.GetType() != generated.GetType()) - { - isEqual = false; - reason = string.Format("Type does not match, actual type is {0}", deserialized.GetType().Name); - } - else - { - isEqual = generated.Equals(deserialized); - reason = "Value differs"; - } - } - - if (!isEqual) - { - LogExpected(generated, actualData, reason); - } - - return isEqual; - } - - private object Deserialize(string eventType, byte[] actualData) - { - object result; - var strData = Helper.UTF8NoBom.GetString(actualData); - if (eventType == typeof(AccountCreated).FullName) - { - result = TestClientJson.From(strData); - } - else - { - if (eventType == typeof(AccountCredited).Name) - { - result = TestClientJson.From(strData); - } - else - { - if (eventType == typeof(AccountDebited).Name) - { - result = TestClientJson.From(strData); - } - else - { - if (eventType == typeof(AccountCheckPoint).Name) - { - result = TestClientJson.From(strData); - } - else - { - throw new NotSupportedException(string.Format("Event type {0} is not recognized.", - eventType)); - } - } - } - } - - return result; - } - - private static void LogExpected(object generated, object actual, string reason) - { - Log.Information("Expected: {expected}\n" + - " Actual: {actual}\n" + - " Details: {reason}", - generated.ToString(), - (actual == null ? "" : actual.ToString()), - reason - ); - } -} - -public class AccountCreated -{ - public readonly string AccountNumber; - - public AccountCreated(string accountNumber) - { - AccountNumber = accountNumber; - } - - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - var casted = (AccountCreated)obj; - return Equals(casted); - } - - protected bool Equals(AccountCreated other) - { - return string.Equals(AccountNumber, other.AccountNumber); - } - - public override int GetHashCode() - { - return (AccountNumber != null ? AccountNumber.GetHashCode() : 0); - } - - public override string ToString() - { - return string.Format("Type: {0}; AccountNumber: {1}", GetType().Name, AccountNumber); - } -} - -public class AccountCredited -{ - public readonly decimal CreditedAmount; - public readonly string Kind; - - public AccountCredited(decimal creditedAmount, int kind) - { - CreditedAmount = creditedAmount; - Kind = kind.ToString(CultureInfo.InvariantCulture); - } - - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - var casted = (AccountCredited)obj; - return Equals(casted); - } - - protected bool Equals(AccountCredited other) - { - return CreditedAmount == other.CreditedAmount; - } - - public override int GetHashCode() - { - return CreditedAmount.GetHashCode(); - } - - public override string ToString() - { - return string.Format("Type: {0}; CreditedAmount: {1}", GetType().Name, CreditedAmount); - } -} - -public class AccountDebited -{ - public readonly decimal DebitedAmount; - public readonly string Kind; - - public AccountDebited(decimal debitedAmount, int kind) - { - DebitedAmount = debitedAmount; - Kind = kind.ToString(CultureInfo.InvariantCulture); - } - - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - var casted = (AccountDebited)obj; - return Equals(casted); - } - - protected bool Equals(AccountDebited other) - { - return DebitedAmount == other.DebitedAmount; - } - - public override int GetHashCode() - { - return DebitedAmount.GetHashCode(); - } - - public override string ToString() - { - return string.Format("Type: {0}; DebitedAmount: {1}", GetType().Name, DebitedAmount); - } -} - -public class AccountCheckPoint -{ - public readonly decimal CreditedAmount; - public readonly decimal DebitedAmount; - - public AccountCheckPoint(decimal creditedAmount, decimal debitedAmount) - { - CreditedAmount = creditedAmount; - DebitedAmount = debitedAmount; - } - - public override bool Equals(object obj) - { - if (obj == null) - { - return false; - } - - if (obj.GetType() != GetType()) - { - return false; - } - - var casted = (AccountCheckPoint)obj; - return Equals(casted); - } - - protected bool Equals(AccountCheckPoint other) - { - return CreditedAmount == other.CreditedAmount && DebitedAmount == other.DebitedAmount; - } - - public override int GetHashCode() - { - unchecked - { - return (CreditedAmount.GetHashCode() * 397) ^ DebitedAmount.GetHashCode(); - } - } - - public override string ToString() - { - return string.Format("Type: {0}; CreditedAmount: {1}, DebitedAmount: {2}", - GetType().Name, - CreditedAmount, - DebitedAmount); - } -} diff --git a/src/EventStore.TestClient/Commands/DvuBasic/BankAccountEventFactory.cs b/src/EventStore.TestClient/Commands/DvuBasic/BankAccountEventFactory.cs deleted file mode 100644 index 81f7bad717..0000000000 --- a/src/EventStore.TestClient/Commands/DvuBasic/BankAccountEventFactory.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace EventStore.TestClient.Commands.DvuBasic; - -internal static class BankAccountEventFactory -{ - public static object CreateAccountObject(int version) - { - object accountObject; - - var internalCounter = version + 1; - - { - const int checkpointVersion = 10; - - var checkPointModVersion = internalCounter % checkpointVersion; - if (checkPointModVersion == 0) - { - int otherCheckPointsCount = internalCounter / checkpointVersion; - - var elementsCount = internalCounter / 2; - - var creditedSum = ComputeSum(20, elementsCount, 20) - ComputeSum(100, otherCheckPointsCount, 100); - var debitedSum = ComputeSum(10, elementsCount, 20); - - var checkpoint = new AccountCheckPoint(creditedSum, debitedSum); - - accountObject = checkpoint; - } - else - { - var modVersion = internalCounter % 2; - if (modVersion == 0) - { - var credited = new AccountCredited(internalCounter * 10, internalCounter % 17); - accountObject = credited; - } - else - { - var debited = new AccountDebited(internalCounter * 10, internalCounter % 17); - accountObject = debited; - } - } - } - return accountObject; - } - - private static int ComputeSum(int first, int count, int step) - { - var sum = count * (2 * first + step * (count - 1)) / 2; - return sum; - } -} diff --git a/src/EventStore.TestClient/Commands/DvuBasic/DvuBasicProcessor.cs b/src/EventStore.TestClient/Commands/DvuBasic/DvuBasicProcessor.cs deleted file mode 100644 index d84b762db8..0000000000 --- a/src/EventStore.TestClient/Commands/DvuBasic/DvuBasicProcessor.cs +++ /dev/null @@ -1,491 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net.Sockets; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Messages; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; -using OperationResult = EventStore.Client.Messages.OperationResult; - -namespace EventStore.TestClient.Commands.DvuBasic; - -internal class DvuBasicProcessor : ICmdProcessor -{ - public string Keyword - { - get { return "verify"; } - } - - public string Usage - { - get - { - return string.Format("{0} " + - " " + - " " + - " " + - " " + - "", - Keyword, - string.Join(",", AvailableProducers)); - } - } - - public IEnumerable AvailableProducers - { - get { yield return "bank"; } - } - - public IBasicProducer[] Producers { get; set; } - - private int _writers; - private string[] _streams; - private int[] _heads; - - private volatile bool _stopReading; - private readonly object _factoryLock = new object(); - - public bool Execute(CommandProcessorContext context, string[] args) - { - var writers = 20; - var readers = 30; - var events = 1000000; - var streams = 1000; - var producers = new[] { "bank" }; - - if (args.Length != 0 && args.Length != 5) - { - context.Log.Error("Invalid number of arguments. Should be 0 or 5"); - return false; - } - - if (args.Length > 0) - { - int writersArg; - int readersArg; - int eventsArg; - int streamsArg; - - if (!int.TryParse(args[0], out writersArg)) - { - context.Log.Error("Invalid argument value for "); - return false; - } - - if (!int.TryParse(args[1], out readersArg)) - { - context.Log.Error("Invalid argument value for "); - return false; - } - - if (!int.TryParse(args[2], out eventsArg)) - { - context.Log.Error("Invalid argument value for "); - return false; - } - - if (!int.TryParse(args[3], out streamsArg)) - { - context.Log.Error("Invalid argument value for "); - return false; - } - - string[] producersArg = args[4].Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries) - .Select(p => p.Trim().ToLower()).Distinct().ToArray(); - if (producersArg.Length <= 0) - { - context.Log.Error("Invalid argument value for "); - return false; - } - - if (producersArg.Any(p => !AvailableProducers.Contains(p))) - { - context.Log.Error("Invalid producers argument. Pass comma-separated subset of [{producers}]", - string.Join(",", AvailableProducers)); - return false; - } - - writers = writersArg; - readers = readersArg; - events = eventsArg; - streams = streamsArg; - producers = producersArg; - } - - _writers = writers; - return InitProducers(producers) && Run(context, writers, readers, events, streams); - } - - private bool InitProducers(string[] producers) - { - if (producers.Length == 1 && producers[0] == "bank") - { - Producers = new IBasicProducer[] { new BankAccountBasicProducer() }; - return true; - } - - return false; - } - - private bool Run(CommandProcessorContext context, int writers, int readers, int events, int streams) - { - context.IsAsync(); - - _streams = new string[streams * Producers.Length]; - for (var i = 0; i < Producers.Length; i++) - { - for (var j = i * streams; j < streams * (i + 1); j++) - { - _streams[j] = StreamNamesGenerator.GenerateName(Producers[i].Name, j - i * streams); - } - } - - _heads = Enumerable.Repeat(-1, streams * Producers.Length).ToArray(); - - return Verify(context, writers, readers, events); - } - - private bool Verify(CommandProcessorContext context, int writers, int readers, int events) - { - var readStatuses = Enumerable.Range(0, readers).Select(x => new Status(context.Log)).ToArray(); - var readNotifications = Enumerable.Range(0, readers).Select(x => new ManualResetEventSlim(false)).ToArray(); - var writeStatuses = Enumerable.Range(0, writers).Select(x => new Status(context.Log)).ToArray(); - var writeNotifications = - Enumerable.Range(0, writers).Select(x => new ManualResetEventSlim(false)).ToArray(); - - for (int i = 0; i < readers; i++) - { - var i1 = i; - var thread = new Thread(() => Read(readStatuses[i1], i1, context, readNotifications[i1])); - thread.IsBackground = true; - thread.Start(); - } - - for (int i = 0; i < writers; i++) - { - var i1 = i; - var thread = new Thread(() => - Write(writeStatuses[i1], i1, context, events / writers, writeNotifications[i1])); - thread.IsBackground = true; - thread.Start(); - } - - foreach (var writeNotification in writeNotifications) - { - writeNotification.Wait(); - } - - _stopReading = true; - foreach (var readNotification in readNotifications) - { - readNotification.Wait(); - } - - context.Log.Information("dvub finished execution : "); - - var writersTable = new ConsoleTable("WRITER ID", "Status"); - - foreach (var ws in writeStatuses) - { - writersTable.AppendRow(ws.ThreadId.ToString(), ws.Success ? "Success" : "Fail"); - } - - var readersTable = new ConsoleTable("READER ID", "Status"); - foreach (var rs in readStatuses) - { - readersTable.AppendRow(rs.ThreadId.ToString(), rs.Success ? "Success" : "Fail"); - } - - context.Log.Information(writersTable.CreateIndentedTable()); - context.Log.Information(readersTable.CreateIndentedTable()); - - var success = writeStatuses.All(s => s.Success) && readStatuses.All(s => s.Success); - if (success) - { - context.Success(); - } - else - { - context.Fail(); - } - - return success; - } - - private void Write(Status status, int writerIdx, CommandProcessorContext context, int requests, - ManualResetEventSlim finish) - { - TcpTypedConnection connection; - var iteration = new AutoResetEvent(false); - - var sent = 0; - - var prepareTimeouts = 0; - var commitTimeouts = 0; - var forwardTimeouts = 0; - var wrongExpectedVersion = 0; - var streamsDeleted = 0; - - var failed = 0; - - var rnd = new Random(writerIdx); - - var streamIdx = -1; - var head = -1; - - Action, TcpPackage> packageHandler = (conn, pkg) => - { - var dto = pkg.Data.Deserialize(); - switch (dto.Result) - { - case OperationResult.Success: - lock (_heads) - { - var currentHead = _heads[streamIdx]; - Ensure.Equal(currentHead, head, "currentHead"); - _heads[streamIdx]++; - } - - break; - case OperationResult.PrepareTimeout: - prepareTimeouts++; - failed++; - break; - case OperationResult.CommitTimeout: - commitTimeouts++; - failed++; - break; - case OperationResult.ForwardTimeout: - forwardTimeouts++; - failed++; - break; - case OperationResult.WrongExpectedVersion: - wrongExpectedVersion++; - failed++; - break; - case OperationResult.StreamDeleted: - streamsDeleted++; - failed++; - break; - default: - throw new ArgumentOutOfRangeException(); - } - - sent++; - if (sent % 1000 == 0) - { - status.ReportWritesProgress(writerIdx, sent, prepareTimeouts, commitTimeouts, forwardTimeouts, - wrongExpectedVersion, streamsDeleted, failed, requests); - } - - iteration.Set(); - }; - - Action> established = _ => { }; - Action, SocketError> closed = null; - closed = (_, __) => - { - if (!context._tcpTestClient.Options.Reconnect) - { - return; - } - - Thread.Sleep(TimeSpan.FromSeconds(1)); - connection = - context._tcpTestClient.CreateTcpConnection(context, packageHandler, cn => iteration.Set(), closed, false); - }; - - connection = context._tcpTestClient.CreateTcpConnection(context, packageHandler, established, closed, false); - - for (var i = 0; i < requests; ++i) - { - streamIdx = NextStreamForWriting(rnd, writerIdx); - lock (_heads) - { - head = _heads[streamIdx]; - } - - var evnt = CreateEvent(_streams[streamIdx], head + 1); - var write = new WriteEvents( - _streams[streamIdx], - head, - new[] { - new NewEvent(evnt.EventId.ToByteArray(), evnt.EventType, - evnt.IsJson ? 1 : 0, 0, evnt.Data, evnt.Metadata) - }, - false); - - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), write.Serialize()); - connection.EnqueueSend(package.AsByteArray()); - iteration.WaitOne(); - } - - status.ReportWritesProgress(writerIdx, sent, prepareTimeouts, commitTimeouts, forwardTimeouts, - wrongExpectedVersion, streamsDeleted, failed, requests); - status.FinalizeStatus(writerIdx, failed != sent); - context._tcpTestClient.Options.Reconnect = false; - connection.Close(); - finish.Set(); - } - - private void Read(Status status, int readerIdx, CommandProcessorContext context, - ManualResetEventSlim finishedEvent) - { - TcpTypedConnection connection; - var iteration = new AutoResetEvent(false); - - var successes = 0; - var fails = 0; - - var rnd = new Random(readerIdx); - - var streamIdx = -1; - var eventidx = -1; - - Action, TcpPackage> packageReceived = (conn, pkg) => - { - var dto = pkg.Data.Deserialize(); - switch (dto.Result) - { - case ReadEventCompleted.Types.ReadEventResult.Success: - if (Equal(_streams[streamIdx], eventidx, dto.Event.Event.EventType, dto.Event.Event.Data.ToByteArray())) - { - successes++; - if (successes % 1000 == 0) - { - status.ReportReadsProgress(readerIdx, successes, fails); - } - } - else - { - fails++; - status.ReportReadError(readerIdx, _streams[streamIdx], eventidx); - } - - break; - case ReadEventCompleted.Types.ReadEventResult.NotFound: - case ReadEventCompleted.Types.ReadEventResult.NoStream: - case ReadEventCompleted.Types.ReadEventResult.StreamDeleted: - case ReadEventCompleted.Types.ReadEventResult.Error: - case ReadEventCompleted.Types.ReadEventResult.AccessDenied: - fails++; - status.ReportNotFoundOnRead(readerIdx, _streams[streamIdx], eventidx); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - iteration.Set(); - }; - Action> established = _ => { }; - Action, SocketError> closed = null; - closed = (_, __) => - { - if (!context._tcpTestClient.Options.Reconnect) - { - return; - } - - Thread.Sleep(TimeSpan.FromSeconds(1)); - connection = - context._tcpTestClient.CreateTcpConnection(context, packageReceived, cn => iteration.Set(), closed, false); - }; - - connection = context._tcpTestClient.CreateTcpConnection(context, packageReceived, established, closed, false); - - while (!_stopReading) - { - streamIdx = NextStreamForReading(rnd, readerIdx); - int head; - lock (_heads) - { - head = _heads[streamIdx]; - } - - if (head > 0) - { - eventidx = NextRandomEventVersion(rnd, head); - var stream = _streams[streamIdx]; - var corrid = Guid.NewGuid(); - var read = new ReadEvent(stream, eventidx, resolveLinkTos: false, - requireLeader: false); - var package = new TcpPackage(TcpCommand.ReadEvent, corrid, read.Serialize()); - - connection.EnqueueSend(package.AsByteArray()); - iteration.WaitOne(); - } - else - { - Thread.Sleep(100); - } - } - - status.ReportReadsProgress(readerIdx, successes, fails); - status.FinalizeStatus(readerIdx, fails == 0); - context._tcpTestClient.Options.Reconnect = false; - connection.Close(); - finishedEvent.Set(); - } - - private int NextStreamForWriting(Random rnd, int writerIdx) - { - if (_writers >= _streams.Length) - { - if (_writers > _streams.Length) - { - return writerIdx % _streams.Length; - } - - return writerIdx; - } - - return rnd.Next(_streams.Length); - } - - private int NextStreamForReading(Random rnd, int readerIdx) - { - return rnd.Next(_streams.Length); - } - - private int NextRandomEventVersion(Random rnd, int head) - { - return head % 2 == 0 ? head : rnd.Next(1, head); - } - - private IBasicProducer CorrespondingProducer(string stream) - { - return Producers.Single(f => string.Equals(f.Name, stream, StringComparison.OrdinalIgnoreCase)); - } - - private Event CreateEvent(string stream, int version) - { - var originalName = StreamNamesGenerator.GetOriginalName(stream); - var factory = CorrespondingProducer(originalName); - - Event generated; - lock (_factoryLock) - { - generated = factory.Create(version); - } - - return generated; - } - - private bool Equal(string stream, int expectedIdx, string eventType, byte[] actual) - { - var originalName = StreamNamesGenerator.GetOriginalName(stream); - var producer = CorrespondingProducer(originalName); - - bool equal; - lock (_factoryLock) - { - equal = producer.Equal(expectedIdx, eventType, actual); - } - - return equal; - } -} diff --git a/src/EventStore.TestClient/Commands/DvuBasic/IBasicProducer.cs b/src/EventStore.TestClient/Commands/DvuBasic/IBasicProducer.cs deleted file mode 100644 index 3384d87a5a..0000000000 --- a/src/EventStore.TestClient/Commands/DvuBasic/IBasicProducer.cs +++ /dev/null @@ -1,12 +0,0 @@ -using EventStore.Core.Data; -#pragma warning disable 1591 - -namespace EventStore.TestClient.Commands.DvuBasic; - -public interface IBasicProducer -{ - string Name { get; } - - Event Create(int version); - bool Equal(int eventVersion, string eventType, byte[] actualData); -} diff --git a/src/EventStore.TestClient/Commands/DvuBasic/Status.cs b/src/EventStore.TestClient/Commands/DvuBasic/Status.cs deleted file mode 100644 index d00c58c398..0000000000 --- a/src/EventStore.TestClient/Commands/DvuBasic/Status.cs +++ /dev/null @@ -1,95 +0,0 @@ -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; -#pragma warning disable 1591 - -namespace EventStore.TestClient.Commands.DvuBasic; - -public class Status -{ - private readonly Serilog.ILogger _log; - - public int ThreadId { get; private set; } - public bool Success { get; private set; } - - public Status(ILogger log) - { - Ensure.NotNull(log, "log"); - _log = log; - } - - public void ReportWritesProgress(int threadId, - int sent, - int prepareTimeouts, - int commitTimeouts, - int forwardTimeouts, - int wrongExpctdVersions, - int streamsDeleted, - int fails, - int requests) - { - var sentP = ToPercent(sent, requests); - var failsP = ToPercent(fails, sent); - - var table = new ConsoleTable("WRITER ID", "Completed %", "Completed/Total", - "Failed %", "Failed/Sent", "Prepare Timeouts", - "Commit Timeouts", "Forward Timeouts", - "Wrong Versions", "Stream Deleted"); - table.AppendRow(threadId.ToString(), string.Format("{0:0.0}%", sentP), - string.Format("{0}/{1}", sent, requests), - string.Format("{0:0.0}%", failsP), string.Format("{0}/{1}", fails, sent), prepareTimeouts.ToString(), - commitTimeouts.ToString(), forwardTimeouts.ToString(), - wrongExpctdVersions.ToString(), streamsDeleted.ToString()); - - if (failsP > 50d) - { - _log.Fatal(table.CreateIndentedTable()); - } - else - { - _log.Information(table.CreateIndentedTable()); - } - } - - public void ReportReadsProgress(int threadId, int successes, int fails) - { - var all = successes + fails; - - var table = new ConsoleTable("READER ID", "Fails", "Total Random Reads"); - table.AppendRow(threadId.ToString(), fails.ToString(), all.ToString()); - - if (fails != 0) - { - _log.Fatal(table.CreateIndentedTable()); - } - else - { - _log.Information(table.CreateIndentedTable()); - } - } - - public void ReportReadError(int threadId, string stream, int index) - { - _log.Fatal("FATAL : READER [{threadId}] encountered an error in {stream} ({index})", threadId, stream, - index); - } - - public void ReportNotFoundOnRead(int threadId, string stream, int index) - { - _log.Fatal( - "FATAL : READER [{threadId}] asked for event {index} in '{stream}' but server returned 'Not Found'", - threadId, - index, stream); - } - - public void FinalizeStatus(int threadId, bool success) - { - ThreadId = threadId; - Success = success; - } - - private double ToPercent(int value, int max) - { - var approx = (value / (double)max) * 100d; - return approx <= 100d ? approx : 100; - } -} diff --git a/src/EventStore.TestClient/Commands/DvuBasic/StreamNamesGenerator.cs b/src/EventStore.TestClient/Commands/DvuBasic/StreamNamesGenerator.cs deleted file mode 100644 index 9461091880..0000000000 --- a/src/EventStore.TestClient/Commands/DvuBasic/StreamNamesGenerator.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace EventStore.TestClient.Commands.DvuBasic; - -internal static class StreamNamesGenerator -{ - public static string GenerateName(string original, int index) - { - return string.Format("{0}-{1}", original, index); - } - - public static string GetOriginalName(string autogenerated) - { - return autogenerated.Substring(0, autogenerated.LastIndexOf('-')); - } -} diff --git a/src/EventStore.TestClient/Commands/MultiWriteFloodWaiting.cs b/src/EventStore.TestClient/Commands/MultiWriteFloodWaiting.cs deleted file mode 100644 index 45f9cc1ba4..0000000000 --- a/src/EventStore.TestClient/Commands/MultiWriteFloodWaiting.cs +++ /dev/null @@ -1,168 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; -using OperationResult = EventStore.Client.Messages.OperationResult; - -namespace EventStore.TestClient.Commands; - -internal class MultiWriteFloodWaitingProcessor : ICmdProcessor -{ - public string Usage - { - get { return "MWRFLW [ [ ]]"; } - } - - public string Keyword - { - get { return "MWRFLW"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 5000; - var writeCount = 10; - - if (args.Length > 0) - { - if (args.Length != 1 && args.Length != 3) - { - return false; - } - - try - { - writeCount = MetricPrefixValue.ParseInt(args[0]); - if (args.Length > 1) - { - clientsCnt = MetricPrefixValue.ParseInt(args[1]); - requestsCnt = MetricPrefixValue.ParseLong(args[2]); - } - } - catch - { - return false; - } - } - - WriteFlood(context, writeCount, clientsCnt, requestsCnt); - return true; - } - - private void WriteFlood(CommandProcessorContext context, int writeCnt, int clientsCnt, long requestsCnt) - { - const string data = "test-data"; - - context.IsAsync(); - - var clients = new List>(); - var threads = new List(); - var doneEvent = new ManualResetEventSlim(false); - long succ = 0; - long fail = 0; - long all = 0; - - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - var localDoneEvent = new AutoResetEvent(false); - var eventStreamId = "es" + Guid.NewGuid(); - var client = context._tcpTestClient.CreateTcpConnection( - context, - (conn, pkg) => - { - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result == OperationResult.Success) - { - if (Interlocked.Increment(ref succ) % 1000 == 0) - { - Console.Write("."); - } - } - else - { - if (Interlocked.Increment(ref fail) % 1000 == 0) - { - Console.Write("#"); - } - } - - if (Interlocked.Increment(ref all) == requestsCnt) - { - context.Success(); - doneEvent.Set(); - } - - localDoneEvent.Set(); - }, - connectionClosed: (conn, err) => context.Fail(reason: "Connection was closed prematurely.")); - clients.Add(client); - - threads.Add(new Thread(() => - { - for (int j = 0; j < count; ++j) - { - var writeDto = new WriteEvents( - eventStreamId, - ExpectedVersion.Any, - Enumerable.Range(0, writeCnt).Select(x => - new NewEvent(Guid.NewGuid().ToByteArray(), - "type", - 0, 0, - Common.Utils.Helper.UTF8NoBom.GetBytes(data), - new byte[0])).ToArray(), - false); - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), writeDto.Serialize()); - client.EnqueueSend(package.AsByteArray()); - localDoneEvent.WaitOne(); - } - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - sw.Stop(); - clients.ForEach(client => client.Close()); - - var reqPerSec = (all + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("Completed. Successes: {success}, failures: {failures}", succ, fail); - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", all, - sw.ElapsedMilliseconds, reqPerSec); - - PerfUtils.LogData(Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt), - (int)(100.0 * fail / (fail + succ))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds / requestsCnt)); - - if (succ != requestsCnt) - { - context.Fail(reason: "There were errors or not all requests completed."); - } - else - { - context.Success(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/MultiWriteProcessor.cs b/src/EventStore.TestClient/Commands/MultiWriteProcessor.cs deleted file mode 100644 index 68db128631..0000000000 --- a/src/EventStore.TestClient/Commands/MultiWriteProcessor.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class MultiWriteProcessor : ICmdProcessor -{ - public string Usage - { - get { return "MWR [ [ []]"; } - } - - public string Keyword - { - get { return "MWR"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - const string data = "test-data"; - var eventStreamId = "test-stream"; - var writeCount = 10; - var expectedVersion = ExpectedVersion.Any; - - if (args.Length > 0) - { - if (args.Length > 3) - { - return false; - } - - writeCount = MetricPrefixValue.ParseInt(args[0]); - if (args.Length >= 2) - { - eventStreamId = args[1]; - } - - if (args.Length >= 3) - { - expectedVersion = args[2].Trim().ToUpper() == "ANY" - ? ExpectedVersion.Any - : int.Parse(args[2].Trim()); - } - } - - context.IsAsync(); - var sw = new Stopwatch(); - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information("[{remoteEndPoint}, L{localEndPoint}]: Writing...", conn.RemoteEndPoint, - conn.LocalEndPoint); - var writeDto = new WriteEvents( - eventStreamId, - expectedVersion, - Enumerable.Range(0, writeCount).Select(x => new NewEvent( - Guid.NewGuid().ToByteArray(), - "type", - 0, 0, - Helper.UTF8NoBom.GetBytes(data), - new byte[0])).ToArray(), - false); - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), writeDto.Serialize()) - .AsByteArray(); - sw.Start(); - conn.EnqueueSend(package); - }, - handlePackage: (conn, pkg) => - { - sw.Stop(); - context.Log.Information("Write request took: {elapsed}.", sw.Elapsed); - - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result == EventStore.Client.Messages.OperationResult.Success) - { - context.Log.Information("Successfully written {writeCount} events.", writeCount); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds)); - context.Success(); - } - else - { - context.Log.Information("Error while writing: {e}.", dto.Result); - context.Fail(); - } - - conn.Close(); - }, - connectionClosed: (connection, error) => context.Fail(reason: "Connection was closed prematurely.")); - - context.WaitForCompletion(); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/PingFloodProcessor.cs b/src/EventStore.TestClient/Commands/PingFloodProcessor.cs deleted file mode 100644 index a480f7f0d7..0000000000 --- a/src/EventStore.TestClient/Commands/PingFloodProcessor.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class PingFloodProcessor : ICmdProcessor -{ - private static readonly byte[] Payload = new byte[0]; - - public string Usage - { - get { return "PINGFL [ ]"; } - } - - public string Keyword - { - get { return "PINGFL"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 1000000; - if (args.Length > 0) - { - if (args.Length != 2) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseLong(args[1]); - } - catch - { - return false; - } - } - - PingFlood(context, clientsCnt, requestsCnt); - return true; - } - - private void PingFlood(CommandProcessorContext context, int clientsCnt, long requestsCnt) - { - context.IsAsync(); - - var doneEvent = new ManualResetEventSlim(false); - var clients = new List>(); - var threads = new List(); - long all = 0; - - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - long received = 0; - long sent = 0; - var client = context._tcpTestClient.CreateTcpConnection( - context, - (conn, msg) => - { - Interlocked.Increment(ref received); - var pongs = Interlocked.Increment(ref all); - if (pongs % 10000 == 0) - { - Console.Write('.'); - } - - if (pongs == requestsCnt) - { - context.Success(); - doneEvent.Set(); - } - }, - connectionClosed: (conn, err) => context.Fail(reason: "Connection was closed prematurely.")); - clients.Add(client); - - threads.Add(new Thread(() => - { - for (int j = 0; j < count; ++j) - { - var package = new TcpPackage(TcpCommand.Ping, Guid.NewGuid(), Payload); - client.EnqueueSend(package.AsByteArray()); - - var localSent = Interlocked.Increment(ref sent); - while (localSent - Interlocked.Read(ref received) > - context._tcpTestClient.Options.PingWindow / clientsCnt) - { - Thread.Sleep(1); - } - } - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - sw.Stop(); - clients.ForEach(client => client.Close()); - - var reqPerSec = (all + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", all, - sw.ElapsedMilliseconds, reqPerSec); - PerfUtils.LogData(Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - - if (Interlocked.Read(ref all) == requestsCnt) - { - context.Success(); - } - else - { - context.Fail(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/PingFloodWaitingProcessor.cs b/src/EventStore.TestClient/Commands/PingFloodWaitingProcessor.cs deleted file mode 100644 index 38305b3052..0000000000 --- a/src/EventStore.TestClient/Commands/PingFloodWaitingProcessor.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class PingFloodWaitingProcessor : ICmdProcessor -{ - public string Usage - { - get { return "PINGFLW [ ]"; } - } - - public string Keyword - { - get { return "PINGFLW"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 100000; - if (args.Length > 0) - { - if (args.Length != 2) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseLong(args[1]); - } - catch - { - return false; - } - } - - PingFloodWaiting(context, clientsCnt, requestsCnt); - return true; - } - - private void PingFloodWaiting(CommandProcessorContext context, int clientsCnt, long requestsCnt) - { - context.IsAsync(); - - var clients = new List>(); - var threads = new List(); - var doneEvent = new ManualResetEventSlim(false); - var clientsDone = 0; - long all = 0; - for (int i = 0; i < clientsCnt; i++) - { - var autoResetEvent = new AutoResetEvent(false); - var client = context._tcpTestClient.CreateTcpConnection( - context, - (_, __) => - { - Interlocked.Increment(ref all); - autoResetEvent.Set(); - }, - connectionClosed: (conn, err) => context.Fail(reason: "Connection was closed prematurely.")); - clients.Add(client); - - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - threads.Add(new Thread(() => - { - for (int j = 0; j < count; ++j) - { - var package = new TcpPackage(TcpCommand.Ping, Guid.NewGuid(), null); - client.EnqueueSend(package.AsByteArray()); - autoResetEvent.WaitOne(); - } - - if (Interlocked.Increment(ref clientsDone) == clientsCnt) - { - context.Success(); - doneEvent.Set(); - } - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - sw.Stop(); - clients.ForEach(x => x.Close()); - - var reqPerSec = (all + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", all, - sw.ElapsedMilliseconds, reqPerSec); - PerfUtils.LogData(Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds / all)); - - if (Interlocked.Read(ref all) == requestsCnt) - { - context.Success(); - } - else - { - context.Fail(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/PingProcessor.cs b/src/EventStore.TestClient/Commands/PingProcessor.cs deleted file mode 100644 index 064265d6a0..0000000000 --- a/src/EventStore.TestClient/Commands/PingProcessor.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class PingProcessor : ICmdProcessor -{ - public string Usage - { - get { return Keyword; } - } - - public string Keyword - { - get { return "PING"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - context.IsAsync(); - - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - var package = new TcpPackage(TcpCommand.Ping, Guid.NewGuid(), null); - context.Log.Information("[{ip}:{tcpPort}]: PING...", context._tcpTestClient.Options.Host, - context._tcpTestClient.Options.TcpPort); - conn.EnqueueSend(package.AsByteArray()); - }, - handlePackage: (conn, pkg) => - { - if (pkg.Command != TcpCommand.Pong) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - context.Log.Information("[{ip}:{tcpPort}]: PONG!", context._tcpTestClient.Options.Host, - context._tcpTestClient.Options.TcpPort); - context.Success(); - conn.Close(); - }, - connectionClosed: (typedConnection, error) => - context.Fail(reason: "Connection was closed prematurely.")); - context.WaitForCompletion(); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/ReadAllProcessor.cs b/src/EventStore.TestClient/Commands/ReadAllProcessor.cs deleted file mode 100644 index a339771a6a..0000000000 --- a/src/EventStore.TestClient/Commands/ReadAllProcessor.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System; -using System.Diagnostics; -using System.Text; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class ReadAllProcessor : ICmdProcessor -{ - public string Usage - { - get { return "RDALL [[F|B] [ []]]"; } - } - - public string Keyword - { - get { return "RDALL"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - bool forward = true; - long commitPos = 0; - long preparePos = 0; - bool positionOverridden = false; - bool resolveLinkTos = false; - bool requireLeader = false; - - if (args.Length > 0) - { - if (args.Length != 1 && args.Length != 3 && args.Length != 4) - { - return false; - } - - if (args[0].ToUpper() == "F") - { - forward = true; - } - else if (args[0].ToUpper() == "B") - { - forward = false; - } - else - { - return false; - } - - if (args.Length >= 3) - { - positionOverridden = true; - if (!long.TryParse(args[1], out commitPos) || !long.TryParse(args[2], out preparePos)) - { - return false; - } - } - - if (args.Length >= 4) - { - requireLeader = bool.Parse(args[3]); - } - } - - if (!positionOverridden) - { - commitPos = forward ? 0 : -1; - preparePos = forward ? 0 : -1; - } - - context.IsAsync(); - - int total = 0; - var sw = new Stopwatch(); - var tcpCommand = forward ? TcpCommand.ReadAllEventsForward : TcpCommand.ReadAllEventsBackward; - var tcpCommandToReceive = forward ? TcpCommand.ReadAllEventsForwardCompleted : TcpCommand.ReadAllEventsBackwardCompleted; - - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information("[{remoteEndPoint}, L{localEndPoint}]: Reading all {readDirection}...", - conn.RemoteEndPoint, conn.LocalEndPoint, forward ? "FORWARD" : "BACKWARD"); - - var readDto = - new ReadAllEvents(commitPos, preparePos, 10, resolveLinkTos, requireLeader); - var package = new TcpPackage(tcpCommand, Guid.NewGuid(), readDto.Serialize()).AsByteArray(); - sw.Start(); - conn.EnqueueSend(package); - }, - handlePackage: (conn, pkg) => - { - if (pkg.Command != tcpCommandToReceive) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Events.IsEmpty()) - { - sw.Stop(); - context.Log.Information("=== Reading ALL {readDirection} completed in {elapsed}. Total read: {total}", - forward ? "FORWARD" : "BACKWARD", sw.Elapsed, total); - context.Success(); - conn.Close(); - return; - } - - total += dto.Events.Count; - - var readDto = new ReadAllEvents(dto.NextCommitPosition, dto.NextPreparePosition, - 10, resolveLinkTos, requireLeader); - var package = new TcpPackage(tcpCommand, Guid.NewGuid(), readDto.Serialize()).AsByteArray(); - conn.EnqueueSend(package); - }, - connectionClosed: (connection, error) => context.Fail(reason: "Connection was closed prematurely.")); - - context.WaitForCompletion(); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/ReadFloodProcessor.cs b/src/EventStore.TestClient/Commands/ReadFloodProcessor.cs deleted file mode 100644 index 14e07b5e9e..0000000000 --- a/src/EventStore.TestClient/Commands/ReadFloodProcessor.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class ReadFloodProcessor : ICmdProcessor -{ - public string Usage - { - // 0 1 2 3 4 - get { return "RDFL [ [ [ []]]]"; } - } - - public string Keyword - { - get { return "RDFL"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 5000; - int streamsCnt = 1; - var streamPrefix = "test-stream"; - bool resolveLinkTos = false; - bool requireLeader = false; - if (args.Length > 0) - { - if (args.Length != 2 && args.Length != 3 && args.Length != 4 && args.Length != 5) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseLong(args[1]); - if (args.Length >= 3) - { - streamsCnt = MetricPrefixValue.ParseInt(args[2]); - } - - if (args.Length >= 4) - { - streamPrefix = args[3]; - } - - if (args.Length >= 5) - { - requireLeader = bool.Parse(args[4]); - } - } - catch - { - return false; - } - } - - RequestMonitor monitor = new RequestMonitor(); - ReadFlood(context, streamPrefix, clientsCnt, requestsCnt, streamsCnt, resolveLinkTos, requireLeader, monitor); - return true; - } - - private void ReadFlood(CommandProcessorContext context, string streamPrefix, int clientsCnt, long requestsCnt, int streamsCnt, - bool resolveLinkTos, bool requireLeader, RequestMonitor monitor) - { - context.IsAsync(); - - string[] streams = streamsCnt == 1 - ? new[] { streamPrefix } - : Enumerable.Range(0, streamsCnt).Select(x => $"{streamPrefix}-{x}").ToArray(); - - context.Log.Information("Reading streams {first} through to {last}", - streams.FirstOrDefault(), - streams.LastOrDefault()); - - var clients = new List>(); - var threads = new List(); - var doneEvent = new ManualResetEventSlim(false); - var sw2 = new Stopwatch(); - long succ = 0; - long fail = 0; - long all = 0; - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - long received = 0; - long sent = 0; - var client = context._tcpTestClient.CreateTcpConnection( - context, - (conn, pkg) => - { - if (pkg.Command != TcpCommand.ReadEventCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - monitor.EndOperation(pkg.CorrelationId); - if (dto.Result == ReadEventCompleted.Types.ReadEventResult.Success) - { - if (Interlocked.Increment(ref succ) % 1000 == 0) - { - Console.Write("."); - } - } - else - { - if (Interlocked.Increment(ref fail) % 1000 == 0) - { - Console.Write("#"); - } - } - - Interlocked.Increment(ref received); - var localAll = Interlocked.Increment(ref all); - if (localAll % 100000 == 0) - { - var elapsed = sw2.Elapsed; - sw2.Restart(); - context.Log.Information("\nDONE TOTAL {reads} READS IN {elapsed} ({rate:0.0}/s).", localAll, - elapsed, 1000.0 * 100000 / elapsed.TotalMilliseconds); - } - - if (localAll == requestsCnt) - { - doneEvent.Set(); - } - }, - connectionClosed: (conn, err) => context.Fail(reason: "Connection was closed prematurely.")); - clients.Add(client); - - var clientNum = i; - threads.Add(new Thread(() => - { - int streamIndex = (streamsCnt / clientsCnt) * clientNum; - context.Log.Information("Reader #{clientNum} performing {count} reads on {streamsCnt} streams starting at stream index {streamIndex}", - clientNum, count, streamsCnt, streamIndex); - for (int j = 0; j < count; ++j) - { - var corrId = Guid.NewGuid(); - - var eventStreamId = streams[streamIndex++]; - if (streamIndex >= streamsCnt) - { - streamIndex = 0; - } - - var read = new ReadEvent(eventStreamId, 0, resolveLinkTos, requireLeader); - var package = new TcpPackage(TcpCommand.ReadEvent, corrId, read.Serialize()); - monitor.StartOperation(corrId); - client.EnqueueSend(package.AsByteArray()); - - var localSent = Interlocked.Increment(ref sent); - while (localSent - Interlocked.Read(ref received) > - context._tcpTestClient.Options.ReadWindow / clientsCnt) - { - Thread.Sleep(1); - } - } - context.Log.Information("Reader #{clientNum} done", clientNum); - - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - sw2.Start(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - sw.Stop(); - clients.ForEach(client => client.Close()); - - var reqPerSec = (all + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("Completed. READS succ: {success}, fail: {failures}.", Interlocked.Read(ref succ), - Interlocked.Read(ref fail)); - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", all, - sw.ElapsedMilliseconds, reqPerSec); - monitor.GetMeasurementDetails(); - PerfUtils.LogData(Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("readsCnt", all))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - - if (succ != requestsCnt) - { - context.Fail(reason: "There were errors or not all requests completed."); - } - else - { - context.Success(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/ReadProcessor.cs b/src/EventStore.TestClient/Commands/ReadProcessor.cs deleted file mode 100644 index b9d004d9a6..0000000000 --- a/src/EventStore.TestClient/Commands/ReadProcessor.cs +++ /dev/null @@ -1,108 +0,0 @@ -using System; -using System.Diagnostics; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class ReadProcessor : ICmdProcessor -{ - public string Usage - { - get { return "RD [ [ []]]"; } - } - - public string Keyword - { - get { return "RD"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - var eventStreamId = "test-stream"; - var fromNumber = 0; - const bool resolveLinkTos = false; - var requireLeader = false; - - if (args.Length > 0) - { - if (args.Length > 3) - { - return false; - } - - eventStreamId = args[0]; - if (args.Length >= 2) - { - fromNumber = MetricPrefixValue.ParseInt(args[1]); - } - - if (args.Length >= 3) - { - requireLeader = bool.Parse(args[2]); - } - } - - context.IsAsync(); - - var sw = new Stopwatch(); - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information("[{remoteEndPoint}, L{localEndPoint}]: Reading...", conn.RemoteEndPoint, - conn.LocalEndPoint); - var readDto = - new ReadEvent(eventStreamId, fromNumber, resolveLinkTos, requireLeader); - var package = - new TcpPackage(TcpCommand.ReadEvent, Guid.NewGuid(), readDto.Serialize()).AsByteArray(); - sw.Start(); - conn.EnqueueSend(package); - }, - handlePackage: (conn, pkg) => - { - sw.Stop(); - context.Log.Information("Read request took: {elapsed}.", sw.Elapsed); - - if (pkg.Command != TcpCommand.ReadEventCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - context.Log.Information("READ events from <{stream}>:\n\n" - + "\tEventStreamId: {stream}\n" - + "\tEventNumber: {eventNumber}\n" - + "\tReadResult: {readResult}\n" - + "\tEventType: {eventType}\n" - + "\tData: {data}\n" - + "\tMetadata: {metadata}\n", - eventStreamId, - eventStreamId, - dto.Event.Event.EventNumber, - dto.Result, - dto.Event.Event.EventType, - Helper.UTF8NoBom.GetString(dto.Event.Event.Data.ToByteArray()), - Helper.UTF8NoBom.GetString(dto.Event.Event.Metadata.ToByteArray())); - - - if (dto.Result == ReadEventCompleted.Types.ReadEventResult.Success) - { - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds)); - context.Success(); - } - else - { - context.Fail(); - } - - conn.Close(); - }, - connectionClosed: (connection, error) => context.Fail(reason: "Connection was closed prematurely.")); - - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/RequestMonitor.cs b/src/EventStore.TestClient/Commands/RequestMonitor.cs index 06580f521f..59328bd85d 100644 --- a/src/EventStore.TestClient/Commands/RequestMonitor.cs +++ b/src/EventStore.TestClient/Commands/RequestMonitor.cs @@ -5,10 +5,6 @@ using System.Linq; using System.Text; using System.Threading; -using EventStore.Core.Data; -using EventStore.Core.Messages; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; namespace EventStore.TestClient.Commands; diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/BankAccountEvent.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/BankAccountEvent.cs deleted file mode 100644 index d7e5af6f4d..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/BankAccountEvent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using EventStore.ClientAPI; -using EventStore.Common.Utils; -using EventStore.TestClient.Commands; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class BankAccountEvent -{ - public static EventData FromEvent(object accountObject) - { - if (accountObject == null) - { - throw new ArgumentNullException("accountObject"); - } - - var type = accountObject.GetType().Name; - var encodedData = Helper.UTF8NoBom.GetBytes(TestClientJson.To(accountObject)); - var encodedMetadata = - Helper.UTF8NoBom.GetBytes(TestClientJson.To(new Dictionary { { "IsEmpty", true } })); - - return new EventData(Guid.NewGuid(), type, true, encodedData, encodedMetadata); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/IScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/IScenario.cs deleted file mode 100644 index 1cfca4cdeb..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/IScenario.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal interface IScenario : IDisposable -{ - void Run(); - void Clean(); -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/JsonEventContainer.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/JsonEventContainer.cs deleted file mode 100644 index 09aaac7c54..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/JsonEventContainer.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.Collections.Generic; -using EventStore.ClientAPI; -using EventStore.Common.Utils; -using EventStore.TestClient.Commands; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal static class JsonEventContainer -{ - public static EventData ForEvent(object @event) - { - if (@event == null) - { - throw new ArgumentNullException("event"); - } - - var encodedData = Helper.UTF8NoBom.GetBytes(TestClientJson.To(@event)); - var encodedMetadata = - Helper.UTF8NoBom.GetBytes(TestClientJson.To(new Dictionary { { "IsEmpty", true } })); - - return new EventData(Guid.NewGuid(), @event.GetType().Name, true, encodedData, encodedMetadata); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjTranWriteScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjTranWriteScenario.cs deleted file mode 100644 index c00285cd6e..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjTranWriteScenario.cs +++ /dev/null @@ -1,269 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class LoopingProjTranWriteScenario : ProjectionsScenarioBase -{ - private readonly TimeSpan _executionPeriod; - - private readonly Random _random; - - public LoopingProjTranWriteScenario(Action directSendOverTcp, - int maxConcurrentRequests, - int connections, - int streams, - int eventsPerStream, - int streamDeleteStep, - TimeSpan executionPeriod, - string dbParentPath, - NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - _executionPeriod = executionPeriod; - _random = new Random(); - } - - private EventData CreateEventA(int version) - { - var @event = JsonEventContainer.ForEvent(new VersionnedEventA(version)); - return @event; - } - - private EventData CreateEventB(int version) - { - var @event = JsonEventContainer.ForEvent(new VersionnedEventB(version)); - return @event; - } - - internal class VersionnedEvent - { - public readonly int Version; - - public VersionnedEvent(int version) - { - Version = version; - } - } - - internal class VersionnedEventA : VersionnedEvent - { - public VersionnedEventA(int version) : base(version) - { - } - } - - internal class VersionnedEventB : VersionnedEvent - { - public VersionnedEventB(int version) : base(version) - { - } - } - - protected override void RunInternal() - { - var stopWatch = Stopwatch.StartNew(); - - var runIndex = 0; - while (stopWatch.Elapsed < _executionPeriod) - { - var msg = string.Format( - "=================== Start run #{0}, elapsed {1} of {2} minutes, {3} =================== ", - runIndex, - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information( - "=================== Start run #{runIndex}, elapsed {elapsed} of {executionPeriod} minutes, {type} =================== ", - runIndex, - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information("##teamcity[message '{message}']", msg); - - InnerRun(runIndex); - runIndex += 1; - } - } - - private void InnerRun(int runIndex) - { - var nodeId = StartNode(); - - try - { - EnableProjectionByCategory(); - - var streamA = string.Format("numberedevents{0}-stream_a", runIndex); - var streamB = string.Format("numberedevents{0}-stream_b", runIndex); - - var store = GetConnection(); - - var writtenCountA = 0; - var writtenCountB = 0; - while (writtenCountA + writtenCountB < EventsPerStream) - { - var batchSizeA = _random.Next(1, EventsPerStream / 10); - var batchSizeB = _random.Next(1, EventsPerStream / 10); - - var transactionA = store.StartTransactionAsync(streamA, ExpectedVersion.Any).Result; - - var w1 = WriteTransactionData(transactionA, writtenCountA, batchSizeA, CreateEventA); - w1.Wait(); - - var transactionB = store.StartTransactionAsync(streamB, ExpectedVersion.Any).Result; - var w2 = WriteTransactionData(transactionB, writtenCountB, batchSizeB, CreateEventB); - w2.Wait(); - - var cB = CommitTransaction(transactionB); - cB.Wait(); - - var cA = CommitTransaction(transactionA); - cA.Wait(); - - writtenCountA += batchSizeA; - writtenCountB += batchSizeB; - } - - var projectionName = string.Format("NumberedByType{0}", runIndex); - var projection = string.Format(@" - fromCategory('numberedevents{0}') - .when({{ - $init: function() {{ - return {{aVer:0, aList:'', bVer:0, bList:''}}; - }}, - VersionnedEventA: function(state, event) {{ - state.aVer += 1; - if (state.aVer != event.body.version) {{ - throw JSON.stringify({{ - message: 'Version in A is incorrect. ', - stream: event.streamId, - seqNumber: event.sequenceNumber, - eventType: event.eventType, - eventInternalVer: event.body.version, - detailsAVer: state.aVer, - detailsBVer: state.bVer}}); - }} - return state; - }}, - VersionnedEventB: function(state, event) {{ - state.bVer += 1; - if (state.bVer != event.body.version) {{ - throw JSON.stringify({{ - message: 'Version in B is incorrect. ', - stream: event.streamId, - seqNumber: event.sequenceNumber, - eventType: event.eventType, - eventInternalVer: event.body.version, - detailsAVer: state.aVer, - detailsBVer: state.bVer }}); - }} - return state; - }} - }}) -", runIndex); - - var projectionManager = GetProjectionsManager(); - projectionManager.CreateContinuousAsync(projectionName, projection, AdminCredentials).Wait(); - - WaitAndCheckIfIsFaulted(projectionName); - - Log.Debug("Done iteration {runIndex}", runIndex); - } - finally - { - KillNode(nodeId); - } - } - - private Task WriteTransactionData(EventStoreTransaction transaction, int startingVersion, - int eventCount, Func createEvent) - { - Log.Information("Starting to write {eventCount} events in transaction {transactionId}", eventCount, - transaction.TransactionId); - - var resSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - Action fail = prevTask => - { - Log.Information("WriteEventsInTransactionalWay for transaction {transactionId} failed.", - transaction.TransactionId); - resSource.SetException(prevTask.Exception); - }; - - int version = startingVersion; - - Action writeTransactionEvent = null; - writeTransactionEvent = _ => - { - if (version == startingVersion + eventCount) - { - resSource.SetResult(null); - return; - } - - version += 1; - - var writeTask = transaction.WriteAsync(new[] { createEvent(version) }); - writeTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - writeTask.ContinueWith(writeTransactionEvent, TaskContinuationOptions.OnlyOnRanToCompletion); - }; - - Task.Factory.StartNew(() => writeTransactionEvent(null)); - - return resSource.Task; - } - - private Task CommitTransaction(EventStoreTransaction transaction) - { - var resSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - Action fail = prevTask => - { - Log.Information("WriteEventsInTransactionalWay for transaction {transactionId} failed", - transaction.TransactionId); - resSource.SetException(prevTask.Exception); - }; - - var commitTask = transaction.CommitAsync(); - commitTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - commitTask.ContinueWith(t => - { - Log.Information("Committed transaction {transactionId}", transaction.TransactionId); - resSource.SetResult(null); - }, TaskContinuationOptions.OnlyOnRanToCompletion); - - return resSource.Task; - } - - private void WaitAndCheckIfIsFaulted(string projectionName) - { - var stopWatch = Stopwatch.StartNew(); - - var waitDuration = TimeSpan.FromMilliseconds(20 * 1000 + 5 * Streams * EventsPerStream); - while (stopWatch.Elapsed < waitDuration) - { - string reason; - var failed = GetProjectionIsFaulted(projectionName, out reason); - if (failed) - { - var message = string.Format("Projection {0} failed, reason:\n{1}", projectionName, reason); - throw new ApplicationException(message); - } - - var position = GetProjectionPosition(projectionName); - if (position >= (EventsPerStream - 1)) - { - Log.Debug("Expected position reached, done."); - break; - } - - Thread.Sleep(2000); - } - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjectionKillScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjectionKillScenario.cs deleted file mode 100644 index 803773c5e7..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingProjectionKillScenario.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class LoopingProjectionKillScenario : ProjectionsKillScenario -{ - private TimeSpan _executionPeriod; - - private int _iterationCode; - private readonly TimeSpan _iterationLoopDuration; - private readonly TimeSpan _firstKillInterval; - - public LoopingProjectionKillScenario(Action directSendOverTcp, int maxConcurrentRequests, - int connections, int streams, int eventsPerStream, int streamDeleteStep, TimeSpan executionPeriod, - string dbParentPath, NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - _executionPeriod = executionPeriod; - - _iterationLoopDuration = TimeSpan.FromMilliseconds(10 * (Streams * EventsPerStream + Streams) + 20 * 1000); - _firstKillInterval = TimeSpan.FromSeconds(_iterationLoopDuration.TotalSeconds / 2); - } - - protected override int GetIterationCode() - { - return _iterationCode; - } - - protected void SetNextIterationCode() - { - _iterationCode += 1; - } - - protected override void RunInternal() - { - var nodeProcessId = StartNode(); - EnableProjectionByCategory(); - - var stopWatch = Stopwatch.StartNew(); - - while (stopWatch.Elapsed < _executionPeriod) - { - var msg = string.Format( - "=================== Start run #{0}, elapsed {1} of {2} minutes, {3} =================== ", - GetIterationCode(), - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information( - "=================== Start run #{iteration}, elapsed {elapsed} of {executionPeriod} minutes, {type} =================== ", - GetIterationCode(), - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information("##teamcity[message '{message}']", msg); - - var iterationTask = RunIteration(); - - Thread.Sleep(_firstKillInterval); - - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - - if (!iterationTask.Wait(_iterationLoopDuration)) - { - throw new TimeoutException("Iteration execution timeout."); - } - - if (iterationTask.Result != true) - { - throw new ApplicationException("Iteration faulted.", iterationTask.Exception); - } - - SetNextIterationCode(); - } - } - - private Task RunIteration() - { - var countItem = CreateCountItem(); - var sumCheckForBankAccount0 = CreateSumCheckForBankAccount0(); - - var writeTask = WriteData(); - - var expectedAllEventsCount = (Streams * EventsPerStream).ToString(); - var lastExpectedEventVersion = (EventsPerStream - 1).ToString(); - - var successTask = Task.Factory.StartNew(() => - { - var success = false; - var stopWatch = new Stopwatch(); - while (stopWatch.Elapsed < _iterationLoopDuration) - { - if (writeTask.IsFaulted) - { - throw new ApplicationException("Failed to write data"); - } - - if (writeTask.IsCompleted && !stopWatch.IsRunning) - { - stopWatch.Start(); - } - - success = CheckProjectionState(countItem, "count", x => x == expectedAllEventsCount) - && CheckProjectionState(sumCheckForBankAccount0, "success", - x => x == lastExpectedEventVersion); - - if (success) - { - break; - } - - Thread.Sleep(4000); - } - - if (!CheckProjectionState(countItem, "count", x => x == expectedAllEventsCount)) - { - Log.Error( - "Projection '{projection}' has not completed with expected result {expectedCount} in time. ", - countItem, expectedAllEventsCount); - } - - if (!CheckProjectionState(sumCheckForBankAccount0, "success", x => x == lastExpectedEventVersion)) - { - Log.Error( - "Projection '{projection}' has not completed with expected result {lastExpectedEventVersion} in time.", - sumCheckForBankAccount0, lastExpectedEventVersion); - } - - return success; - }); - - writeTask.Wait(); - - return successTask; - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingScenario.cs deleted file mode 100644 index ec71c09579..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/LoopingScenario.cs +++ /dev/null @@ -1,201 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Threading.Tasks; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class LoopingScenario : ScenarioBase -{ - private readonly TimeSpan _executionPeriod; - - private readonly Random _rnd = new Random(); - private volatile bool _stopParalleWrites; - private TimeSpan _startupWaitInterval; - - protected override TimeSpan StartupWaitInterval - { - get { return _startupWaitInterval; } - } - - public LoopingScenario(Action directSendOverTcp, - int maxConcurrentRequests, - int connections, - int streams, - int eventsPerStream, - int streamDeleteStep, - TimeSpan executionPeriod, - string dbParentPath, - NodeConnectionInfo customNodeConnection) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNodeConnection) - { - _executionPeriod = executionPeriod; - SetStartupWaitInterval(TimeSpan.FromSeconds(10)); - } - - private void SetStartupWaitInterval(TimeSpan interval) - { - _startupWaitInterval = interval; - } - - protected override void RunInternal() - { - var stopWatch = Stopwatch.StartNew(); - - var runIndex = 0; - while (stopWatch.Elapsed < _executionPeriod) - { - var msg = string.Format( - "=================== Start run #{0}, elapsed {1} of {2} minutes, {3} =================== ", - runIndex, - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information( - "=================== Start run #{runIndex}, elapsed {elapsed} of {executionPeriod} minutes, {type} =================== ", - runIndex, - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information("##teamcity[message '{message}']", msg); - - SetStartupWaitInterval(TimeSpan.FromSeconds(10 + (2 * (runIndex % 200)))); - InnerRun(runIndex); - runIndex += 1; - } - } - - protected virtual void InnerRun(int runIndex) - { - var nodeProcessId = StartNode(); - - var parallelWritesTimeout = TimeSpan.FromMinutes((EventsPerStream / 1000.0) * 10); - - var parallelWriteTask = RunParallelWrites(runIndex); - - var streams = Enumerable.Range(0, Streams).Select(i => FormatStreamName(runIndex, i)).ToArray(); - - var slices = Split(streams, 3); - var singleEventSlice = slices.Take(1).SelectMany(x => x).ToArray(); - var batchSlice = slices.Skip(1).Take(1).SelectMany(x => x).ToArray(); - var transSlice = slices.Skip(2).Take(1).SelectMany(x => x).ToArray(); - - var wr1 = Write(WriteMode.SingleEventAtTime, singleEventSlice, EventsPerStream); - var wr2 = Write(WriteMode.Bucket, batchSlice, EventsPerStream); - var wr3 = Write(WriteMode.Transactional, transSlice, EventsPerStream); - - if (runIndex % 4 == 0) - { - Scavenge(); - } - - Task.WaitAll(wr1, wr2, wr3); - - var deleted = streams.Where((s, i) => i % StreamDeleteStep == 0).ToArray(); - DeleteStreams(deleted); - - _stopParalleWrites = true; - if (!parallelWriteTask.Wait(parallelWritesTimeout)) - { - throw new ApplicationException("Parallel writes stop timed out, 1."); - } - - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - - parallelWriteTask = RunParallelWrites(runIndex); - - var dl1 = CheckStreamsDeleted(deleted); - - var exceptDeleted = streams.Except(deleted).ToArray(); - - var readCnt = Math.Min(100, EventsPerStream / 3); - var rd1 = Read(exceptDeleted, @from: 0, count: readCnt); - var rd2 = Read(exceptDeleted, @from: EventsPerStream - readCnt, count: readCnt); - var rd3 = Read(exceptDeleted, @from: EventsPerStream / 2, - count: Math.Min(readCnt, EventsPerStream - EventsPerStream / 2)); - - Log.Information("== READ from picked ALL =="); - - var allExistingStreamsSlice = (from run in Enumerable.Range(0, runIndex + 1) - from streamNum in Enumerable.Range(0, Streams) - where streamNum % StreamDeleteStep != 0 - where _rnd.NextDouble() < 0.1 - select FormatStreamName(run, streamNum)).ToArray(); - - var allDeletedStreamsSlice = (from run in Enumerable.Range(0, runIndex + 1) - from streamNum in Enumerable.Range(0, Streams) - where streamNum % StreamDeleteStep == 0 - where _rnd.NextDouble() < 0.1 - select FormatStreamName(run, streamNum)).ToArray(); - - var prevCheckTasks = new List(); - if (allExistingStreamsSlice.Length > 0) - { - var rd4 = Read(allExistingStreamsSlice, 0, Math.Max(1, EventsPerStream / 5)); - var rd5 = Read(allExistingStreamsSlice, EventsPerStream / 2, Math.Max(1, EventsPerStream / 5)); - - prevCheckTasks.Add(rd4); - prevCheckTasks.Add(rd5); - } - - if (allDeletedStreamsSlice.Length > 0) - { - var dl2 = CheckStreamsDeleted(allDeletedStreamsSlice); - prevCheckTasks.Add(dl2); - } - - Task.WaitAll(new[] { dl1, rd1, rd2, rd3 }.Union(prevCheckTasks).ToArray()); - - _stopParalleWrites = true; - if (!parallelWriteTask.Wait(parallelWritesTimeout)) - { - throw new ApplicationException("Parallel writes stop timed out, 2."); - } - - KillNode(nodeProcessId); - } - - protected virtual Task RunParallelWrites(int runIndex) - { - _stopParalleWrites = false; - - return Task.Factory.StartNew(() => - { - int index = 0; - while (!_stopParalleWrites) - { - Log.Debug("Start RunParallelWrites #{index} for runIndex {runIndex}", index, runIndex); - - var parallelStreams = Enumerable.Range(0, 2) - .Select(x => string.Format("parallel-write-stream-in{0}-{1}-{2}", - runIndex, - x, - string.Format("rnd{0}-{1}", _rnd.Next(), DateTime.UtcNow.Ticks))) - .ToArray(); - - var wr = Write(WriteMode.SingleEventAtTime, parallelStreams, EventsPerStream); - wr.Wait(); - - Log.Debug("Wrote RunParallelWrites #{index} for runIndex {runIndex}", index, runIndex); - - var rd1 = Read(parallelStreams, 0, EventsPerStream / 6); - var rd2 = Read(parallelStreams, EventsPerStream / 3, EventsPerStream / 6); - var rd3 = Read(parallelStreams, EventsPerStream - EventsPerStream / 10, EventsPerStream / 10); - Task.WaitAll(rd1, rd2, rd3); - - Log.Debug("Done RunParallelWrites #{index} for runIndex {runIndex}", index, runIndex); - - index += 1; - } - }, TaskCreationOptions.LongRunning); - } - - protected static string FormatStreamName(int runIndex, int i) - { - return string.Format("stream-in{0}-{1}", runIndex, i); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/MassProjectionsScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/MassProjectionsScenario.cs deleted file mode 100644 index 1a94c9433f..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/MassProjectionsScenario.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class MassProjectionsScenario : ProjectionsKillScenario -{ - private readonly Random _random = new Random(); - - public MassProjectionsScenario(Action directSendOverTcp, int maxConcurrentRequests, - int threads, int streams, int eventsPerStream, int streamDeleteStep, string dbParentPath, - NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, threads, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - } - - protected override void RunInternal() - { - var success = true; - - var nodeProcessId = StartNode(); - EnableProjectionByCategory(); - - var countProjections = new List(); - var bankProjections = new List(); - - var writeTasks = new List(); - - while (GetIterationCode() < Streams / 3) - { - writeTasks.Add(WriteData()); - - SetNextIterationCode(); - - countProjections.Add(CreateCountItem()); - bankProjections.Add(CreateSumCheckForBankAccount0()); - - Log.Information("Created {bankProjections} and {countProjections}", bankProjections[bankProjections.Count - 1], - countProjections[countProjections.Count - 1]); - } - - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - - writeTasks.Add(WriteData()); - - var writeTask = Task.Factory.ContinueWhenAll(writeTasks.ToArray(), tsks => Log.Information("All Data written")); - - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - - int count = 10; - while (count > 0) - { - Log.Information("Stop and start projection, remaining iterations {count}, waiting for data to be written.", - count); - - StartOrStopProjection(countProjections, false); - StartOrStopProjection(bankProjections, false); - - Thread.Sleep(TimeSpan.FromSeconds(10)); - - StartOrStopProjection(countProjections, true); - StartOrStopProjection(bankProjections, true); - - if (writeTask.IsCompleted) - { - count -= 1; - } - - if (writeTask.IsFaulted) - { - throw new ApplicationException("Failed to write data", writeTask.Exception); - } - - success = CheckProjectionState(bankProjections[bankProjections.Count - 1], - "success", - x => x == (EventsPerStream - 1).ToString()); - if (success) - { - break; - } - - var sleepTimeSeconds = 10 + Streams * EventsPerStream / 1000.0; - Log.Information("Sleep 1 for {sleepTime} seconds, remaining count {count}", sleepTimeSeconds, count); - Thread.Sleep(TimeSpan.FromSeconds(sleepTimeSeconds)); - } - - writeTask.Wait(); - - count = 20; - success = false; - while (!success && count > 0) - { - Log.Information("Wait until projections are computed, remaining iterations {count}", count); - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - - success = CheckProjectionState(bankProjections[bankProjections.Count - 1], - "success", - x => x == (EventsPerStream - 1).ToString()); - - if (success) - { - break; - } - - var sleepTimeSeconds = 10 + (Streams * EventsPerStream) / 500; - Log.Information("Sleep 2 for {sleepTime} seconds, remaining count {count}", sleepTimeSeconds, count); - Thread.Sleep(TimeSpan.FromSeconds(sleepTimeSeconds)); - - count -= 1; - } - - if (!success) - { - throw new ApplicationException("Last bank projection failed"); - } - } - - private void StartOrStopProjection(IEnumerable projections, bool enable) - { - var manager = GetProjectionsManager(); - const int retriesNumber = 5; - - foreach (var projection in projections.ToArray()) - { - var retry = 0; - - int shortWait = 50 + _random.Next(100); - Log.Debug("Wait for {waitTime}ms before next enable/disable projection", shortWait); - Thread.Sleep(shortWait); - - while (retry <= retriesNumber) - { - var isRunning = GetProjectionIsRunning(projection); - - try - { - if (enable) - { - if (!isRunning) - { - manager.EnableAsync(projection, AdminCredentials).Wait(); - } - else - { - Log.Information("Projection '{projection}' is already running and will not be enabled.", - projection); - } - } - else - { - if (isRunning) - { - manager.DisableAsync(projection, AdminCredentials).Wait(); - } - else - { - Log.Information( - "Projection '{projection}' is already not running and will not be disabled again.", - projection); - } - } - - break; - } - catch (Exception ex) - { - var waitForMs = 5000 + (retry * (3000 + _random.Next(2000))); - - Log.Information(ex, - "Failed to StartOrStopProjection (enable:{enable}; isRunning:{isRunning}) projection {projection}, retry #{retry}, wait {waitTime}ms", - enable, - isRunning, - projection, - retry, - waitForMs); - - if (retry != 0) - { - Thread.Sleep(waitForMs); - } - - if (retry == retriesNumber) - { - throw new ApplicationException(string.Format( - "Failed to StartOrStopProjection (enable:{0}) projection {1}," + - " max number ({2}) of retries reached.", - enable, - projection, - retry), - ex); - } - } - - - retry++; - } - } - } - - private int _iterationCode; - - protected override int GetIterationCode() - { - return _iterationCode; - } - - private void SetNextIterationCode() - { - _iterationCode += 1; - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/NodeConnectionInfo.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/NodeConnectionInfo.cs deleted file mode 100644 index 8c703f089c..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/NodeConnectionInfo.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Net; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class NodeConnectionInfo -{ - public IPAddress IpAddress { get; private set; } - public int TcpPort { get; private set; } - public int HttpPort { get; private set; } - - public NodeConnectionInfo(IPAddress ipAddress, int tcpPort, int httPort) - { - IpAddress = ipAddress; - TcpPort = tcpPort; - HttpPort = httPort; - } - - public override string ToString() - { - return string.Format("[{0}:{1}:{2}]", IpAddress, TcpPort, HttpPort); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjForeachForcedCommonNameScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/ProjForeachForcedCommonNameScenario.cs deleted file mode 100644 index 5f66b83d7d..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjForeachForcedCommonNameScenario.cs +++ /dev/null @@ -1,286 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.TestClient.Commands.DvuBasic; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class ProjForeachForcedCommonNameScenario : ProjectionsScenarioBase -{ - private int _iterationCode = 0; - private TimeSpan _executionPeriod; - - public ProjForeachForcedCommonNameScenario(Action directSendOverTcp, - int maxConcurrentRequests, int connections, int streams, int eventsPerStream, int streamDeleteStep, - TimeSpan executionPeriod, string dbParentPath, NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - _executionPeriod = executionPeriod; - } - - private EventData CreateBankEvent(int version) - { - var accountObject = BankAccountEventFactory.CreateAccountObject(version); - var @event = BankAccountEvent.FromEvent(accountObject); - return @event; - } - - protected virtual int GetIterationCode() - { - return _iterationCode; - } - - protected virtual bool ShouldRestartNode - { - get { return true; } - } - - protected override void RunInternal() - { - var started = DateTime.UtcNow; - - while ((DateTime.UtcNow - started) < _executionPeriod) - { - var msg = string.Format( - "=================== Start run #{0}, elapsed {1} of {2} minutes, {3} =================== ", - GetIterationCode(), - (int)(DateTime.Now - started).TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information( - "=================== Start run #{iteration}, elapsed {elapsed} of {executionPeriod} minutes, {type} =================== ", - GetIterationCode(), - (int)(DateTime.Now - started).TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information("##teamcity[message '{message}']", msg); - - - InnerRun(); - _iterationCode += 1; - } - } - - private void InnerRun() - { - int nodeProcessId = -1; - - if (ShouldRestartNode) - { - nodeProcessId = StartNode(); - } - - EnableProjectionByCategory(); - - var sumCheckForBankAccounts = CreateSumCheckForBankAccounts("sumCheckForBankAccounts", "1"); - var sumCheckForBankAccounts2 = CreateSumCheckForBankAccounts("sumCheckForBankAccounts", "2"); - - var projections = new[] { sumCheckForBankAccounts, sumCheckForBankAccounts2 }; - - var writeTask = WriteData(); - - bool failed = false; - string failReason = null; - - var isWatchStarted = false; - var expectedAllEventsCount = Streams + (Streams * EventsPerStream); - - var stopWatch = new Stopwatch(); - - var waitDuration = TimeSpan.FromMilliseconds(20 * 1000 + 5 * Streams * EventsPerStream); - while (stopWatch.Elapsed < waitDuration) - { - if (writeTask.IsFaulted) - { - throw new ApplicationException("Failed to write data"); - } - - if (writeTask.IsCompleted && !stopWatch.IsRunning) - { - stopWatch.Start(); - isWatchStarted = true; - } - - if (isWatchStarted) - { - stopWatch.Stop(); - } - - failed = CheckIsFaulted(projections, out failReason); - if (failed) - { - break; - } - - if (CheckIsCompleted(projections, expectedAllEventsCount)) - { - break; - } - - Thread.Sleep((int)(waitDuration.TotalMilliseconds / 6)); - - failed = CheckIsFaulted(projections, out failReason); - if (failed) - { - break; - } - - if (ShouldRestartNode) - { - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - } - - if (isWatchStarted) - { - stopWatch.Start(); - } - } - - writeTask.Wait(); - - for (int i = 3; i < 11; ++i) - { - var newSumCheckForBankAccounts = CreateSumCheckForBankAccounts("sumCheckForBankAccounts", i.ToString()); - Thread.Sleep(200); - failed = CheckIsFaulted(new[] { newSumCheckForBankAccounts }, out failReason); - - if (failed) - { - break; - } - } - - if (ShouldRestartNode) - { - KillNode(nodeProcessId); - } - - if (failed) - { - throw new ApplicationException(string.Format("Projection failed due to reason: {0}.", failReason)); - } - } - - private bool CheckIsFaulted(IEnumerable projectionsNames, out string failReason) - { - var faulted = false; - failReason = null; - foreach (var projectionName in projectionsNames) - { - faulted = faulted || GetProjectionIsFaulted(projectionName, out failReason); - } - - return faulted; - } - - private bool CheckIsCompleted(IEnumerable projectionsNames, int expectedAllEventsCount) - { - var completed = false; - foreach (var projectionName in projectionsNames) - { - var position = GetProjectionPosition(projectionName); - if (position == expectedAllEventsCount) - { - Log.Debug("Expected position reached in {projection}, done.", projectionName); - completed = true; - } - } - - return completed; - } - - protected Task WriteData() - { - var streams = Enumerable.Range(0, Streams) - .Select(i => string.Format("bank_account_it{0}-{1}", GetIterationCode(), i)).ToArray(); - var slices = Split(streams, 3); - - var w1 = Write(WriteMode.SingleEventAtTime, slices[0], EventsPerStream, CreateBankEvent); - var w2 = Write(WriteMode.Bucket, slices[1], EventsPerStream, CreateBankEvent); - var w3 = Write(WriteMode.Transactional, slices[2], EventsPerStream, CreateBankEvent); - - var task = Task.Factory.ContinueWhenAll(new[] { w1, w2, w3 }, Task.WaitAll); - return task.ContinueWith(x => Log.Information("Data written for iteration {iteration}.", GetIterationCode())); - } - - protected string CreateSumCheckForBankAccounts(string projectionName, string suffix = "") - { - var fullProjectionName = string.Format("{0}_it{1}_{2}", projectionName, GetIterationCode(), suffix); - var countItemsProjection = string.Format(@" - fromCategory('bank_account_it{1}').foreachStream().when({{ - $init: function() {{ - return {{credited:0, credsum:'', debited:0, debsum:''}}; - }}, - AccountCredited: function (state, event) {{ - state.credited += event.body.creditedAmount; - /*state.credsum += '#' + event.sequenceNumber + ':' + event.body.creditedAmount + ';'*/ - }}, - AccountDebited: function (state, event) {{ - state.debited += event.body.debitedAmount; - /*state.debsum += '#' + event.sequenceNumber + ':' + event.body.debitedAmount + ';'*/ - }}, - AccountCheckPoint: function(state, event) {{ - if (state.credited != event.body.creditedAmount) {{ - throw JSON.stringify({{ - message: 'Credited amount is incorrect. ', - expected: event.body.creditedAmount, - actual: state.credited, - stream: event.streamId, - ver: event.sequenceNumber, - details: state.credsum }}); - }} - if (state.debited != event.body.debitedAmount) {{ - throw JSON.stringify({{ - message: 'Debited amount is incorrect. ', - expected: event.body.debitedAmount, - actual: state.debited, - stream: event.streamId, - ver: event.sequenceNumber, - details: state.debsum }}); - }} - state.success=event.sequenceNumber; - }} - }}) -", projectionName, GetIterationCode()); - - GetProjectionsManager().CreateContinuousAsync(fullProjectionName, countItemsProjection, AdminCredentials) - .Wait(); - - return fullProjectionName; - } -} - -internal class ProjForeachForcedCommonNameNoRestartScenario : ProjForeachForcedCommonNameScenario -{ - public ProjForeachForcedCommonNameNoRestartScenario(Action directSendOverTcp, - int maxConcurrentRequests, - int connections, - int streams, - int eventsPerStream, - int streamDeleteStep, - TimeSpan executionPeriod, - string dbParentPath, - NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - executionPeriod, dbParentPath, customNode) - { - } - - protected override bool ShouldRestartNode - { - get { return false; } - } - - protected override void RunInternal() - { - StartNode(); - base.RunInternal(); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionWrongTagCheck.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionWrongTagCheck.cs deleted file mode 100644 index 7d9000670e..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionWrongTagCheck.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class ProjectionWrongTagCheck : ProjectionsKillScenario -{ - private TimeSpan _executionPeriod; - - public ProjectionWrongTagCheck(Action directSendOverTcp, int maxConcurrentRequests, - int connections, int streams, int eventsPerStream, int streamDeleteStep, TimeSpan executionPeriod, - string dbParentPath, NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - _executionPeriod = executionPeriod; - } - - private int _iterationCode = 0; - - protected override int GetIterationCode() - { - return _iterationCode; - } - - protected void SetNextIterationCode() - { - _iterationCode += 1; - } - - protected override void RunInternal() - { - var nodeProcessId = StartNode(); - EnableProjectionByCategory(); - KillNode(nodeProcessId); - - var stopWatch = Stopwatch.StartNew(); - - while (stopWatch.Elapsed < _executionPeriod) - { - var msg = string.Format( - "=================== Start run #{0}, elapsed {1} of {2} minutes, {3} =================== ", - GetIterationCode(), - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - - Log.Information( - "=================== Start run #{iteration}, elapsed {elapsed} of {executionPeriod} minutes, {type} =================== ", - GetIterationCode(), - (int)stopWatch.Elapsed.TotalMinutes, - _executionPeriod.TotalMinutes, - GetType().Name); - Log.Information("##teamcity[message '{message}']", msg); - - var iterationTask = RunIteration(); - iterationTask.Wait(); - - SetNextIterationCode(); - } - } - - private Task RunIteration() - { - var nodeProcessId = StartNode(); - - - var sumCheckForBankAccount0 = CreateSumCheckForBankAccount0(); - - var writeTask = WriteData(); - - var lastExpectedEventVersion = (EventsPerStream - 1).ToString(); - - var successTask = Task.Factory.StartNew(() => - { - var success = true; - var stopWatch = new Stopwatch(); - - var wasLessLastTime = false; - while (stopWatch.Elapsed < TimeSpan.FromMilliseconds(10 * (Streams * EventsPerStream + Streams))) - { - if (!wasLessLastTime) - { - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - } - - if (writeTask.IsFaulted) - { - throw new ApplicationException("Failed to write data"); - } - - if (writeTask.IsCompleted && !stopWatch.IsRunning) - { - stopWatch.Start(); - } - - var count1 = GetProjectionStateValue(sumCheckForBankAccount0, "success", int.Parse, -1); - for (var i = 0; i < 5; ++i) - { - Thread.Sleep(TimeSpan.FromSeconds(1)); - var count2 = GetProjectionStateValue(sumCheckForBankAccount0, "success", int.Parse, -1); - - if (count1 > count2) - { - if (wasLessLastTime) - { - success = false; - break; - } - - wasLessLastTime = true; - } - - count1 = count2; - } - - if (!success) - { - break; - } - - if (CheckProjectionState(sumCheckForBankAccount0, "success", x => x == lastExpectedEventVersion)) - { - break; - } - } - - KillNode(nodeProcessId); - - if (!success) - { - throw new ApplicationException(string.Format( - "Projection {0} has not completed with expected result {1} in time.", sumCheckForBankAccount0, - lastExpectedEventVersion)); - } - - return success; - }); - - return Task.Factory.ContinueWhenAll(new[] { writeTask, successTask }, tasks => - { - Log.Information("Iteration {iteration} tasks completed", GetIterationCode()); - Task.WaitAll(tasks); - Log.Information("Iteration {iteration} successful", GetIterationCode()); - }); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsKillScenario.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsKillScenario.cs deleted file mode 100644 index 38ac40b10b..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsKillScenario.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.TestClient.Commands.DvuBasic; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class ProjectionsKillScenario : ProjectionsScenarioBase -{ - public ProjectionsKillScenario(Action directSendOverTcp, - int maxConcurrentRequests, - int connections, - int streams, - int eventsPerStream, - int streamDeleteStep, - string dbParentPath, - NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - } - - private EventData CreateBankEvent(int version) - { - var accountObject = BankAccountEventFactory.CreateAccountObject(version); - var @event = BankAccountEvent.FromEvent(accountObject); - return @event; - } - - protected virtual int GetIterationCode() - { - return 0; - } - - protected override void RunInternal() - { - var nodeProcessId = StartNode(); - EnableProjectionByCategory(); - - var countItem = CreateCountItem(); - var sumCheckForBankAccount0 = CreateSumCheckForBankAccount0(); - - var writeTask = WriteData(); - - var success = false; - var expectedAllEventsCount = (Streams * EventsPerStream).ToString(); - var lastExpectedEventVersion = (EventsPerStream - 1).ToString(); - - var isWatchStarted = false; - - var stopWatch = new Stopwatch(); - - var waitDuration = TimeSpan.FromMilliseconds(20 * 1000 + 5 * Streams * EventsPerStream); - while (stopWatch.Elapsed < waitDuration) - { - if (writeTask.IsFaulted) - { - throw new ApplicationException("Failed to write data"); - } - - if (writeTask.IsCompleted && !stopWatch.IsRunning) - { - stopWatch.Start(); - isWatchStarted = true; - } - - success = CheckProjectionState(countItem, "count", x => x == expectedAllEventsCount) - && CheckProjectionState(sumCheckForBankAccount0, "success", - x => x == lastExpectedEventVersion); - - if (success) - { - break; - } - - if (isWatchStarted) - { - stopWatch.Stop(); - } - - Thread.Sleep((int)(waitDuration.TotalMilliseconds / 10)); - - KillNode(nodeProcessId); - nodeProcessId = StartNode(); - - if (isWatchStarted) - { - stopWatch.Start(); - } - } - - writeTask.Wait(); - - KillNode(nodeProcessId); - - if (!success) - { - throw new ApplicationException( - string.Format("Projections did not complete with expected result in time")); - } - } - - protected Task WriteData() - { - var streams = Enumerable.Range(0, Streams) - .Select(i => string.Format("bank_account_it{0}-{1}", GetIterationCode(), i)).ToArray(); - var slices = Split(streams, 3); - - var w1 = Write(WriteMode.SingleEventAtTime, slices[0], EventsPerStream, CreateBankEvent); - var w2 = Write(WriteMode.Bucket, slices[1], EventsPerStream, CreateBankEvent); - var w3 = Write(WriteMode.Transactional, slices[2], EventsPerStream, CreateBankEvent); - - var task = Task.Factory.ContinueWhenAll(new[] { w1, w2, w3 }, Task.WaitAll); - return task.ContinueWith(x => Log.Information("Data written for iteration {iteration}.", GetIterationCode())); - } - - protected string CreateCountItem() - { - var projectionManager = GetProjectionsManager(); - - string countItemsProjectionName = string.Format("CountItems_it{0}", GetIterationCode()); - string countItemsProjection = string.Format(@" - fromCategory('bank_account_it{0}').when({{ - $init: function() {{ return {{count:0}}; }}, - AccountCredited: function (state, event) {{ - state.count += 1; - }}, - AccountDebited: function (state, event) {{ - state.count += 1; - }}, - AccountCheckPoint: function (state, event) {{ - state.count += 1; - }} - }}) -", GetIterationCode()); - - projectionManager.CreateContinuousAsync(countItemsProjectionName, countItemsProjection, AdminCredentials) - .Wait(); - return countItemsProjectionName; - } - - protected string CreateSumCheckForBankAccount0() - { - string countItemsProjectionName = string.Format("CheckSumsInAccounts_it{0}", GetIterationCode()); - string countItemsProjection = string.Format(@" - fromStream('bank_account_it{0}-0').when({{ - $init: function() {{ - return {{credited:0, credsum:'', debited:0, debsum:''}}; - }}, - AccountCredited: function (state, event) {{ - state.credited += event.body.creditedAmount; - /*state.credsum += '#' + event.sequenceNumber + ':' + event.body.creditedAmount + ';'*/ - }}, - AccountDebited: function (state, event) {{ - state.debited += event.body.debitedAmount; - /*state.debsum += '#' + event.sequenceNumber + ':' + event.body.debitedAmount + ';'*/ - }}, - AccountCheckPoint: function(state, event) {{ - if (state.credited != event.body.creditedAmount) {{ - throw JSON.stringify({{ - message: 'Credited amount is incorrect. ', - expected: event.body.creditedAmount, - actual: state.credited, - stream: event.streamId, - ver: event.sequenceNumber, - details: state.credsum }}); - }} - if (state.debited != event.body.debitedAmount) {{ - throw JSON.stringify({{ - message: 'Debited amount is incorrect. ', - expected: event.body.debitedAmount, - actual: state.debited, - stream: event.streamId, - ver: event.sequenceNumber, - details: state.debsum }}); - }} - state.success=event.sequenceNumber; - }} - }}) -", GetIterationCode()); - - GetProjectionsManager() - .CreateContinuousAsync(countItemsProjectionName, countItemsProjection, AdminCredentials).Wait(); - - return countItemsProjectionName; - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsScenarioBase.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsScenarioBase.cs deleted file mode 100644 index 73c73536e5..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/ProjectionsScenarioBase.cs +++ /dev/null @@ -1,212 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using EventStore.TestClient.Commands; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal abstract class ProjectionsScenarioBase : ScenarioBase -{ - protected ProjectionsScenarioBase(Action directSendOverTcp, int maxConcurrentRequests, - int connections, int streams, int eventsPerStream, int streamDeleteStep, string dbParentPath, - NodeConnectionInfo customNode) - : base(directSendOverTcp, maxConcurrentRequests, connections, streams, eventsPerStream, streamDeleteStep, - dbParentPath, customNode) - { - } - - protected bool CheckProjectionState(string projectionName, string key, Func checkValue) - { - var state = GetProjectionState(projectionName); - string value; - return state != null && state.Count > 0 && state.TryGetValue(key, out value) && checkValue(value); - } - - protected T GetProjectionStateValue(string projectionName, string key, Func convert, - T defaultValue) - { - var result = defaultValue; - - var state = GetProjectionState(projectionName); - string value; - if (state != null && state.Count > 0 && state.TryGetValue(key, out value)) - { - result = convert(value); - } - - return result; - } - - protected bool GetProjectionIsRunning(string projectionName) - { - var dic = GetProjectionStatistics(projectionName); - - string value; - var isRunning = dic != null && dic.TryGetValue("status", out value) && value == "Running"; - - return isRunning; - } - - protected long GetProjectionPosition(string projectionName) - { - var dic = GetProjectionStatistics(projectionName); - - long result = -1; - - string value; - if (dic != null && dic.TryGetValue("position", out value)) - { - if (!string.IsNullOrWhiteSpace(value)) - { - var subpositions = value.Split(':'); - var positionString = subpositions.Length == 2 ? subpositions[1] : value; - result = long.Parse(positionString); - } - else - { - result = -1; - } - - return result; - } - - return result; - } - - protected bool GetProjectionIsFaulted(string projectionName, out string reason) - { - var dic = GetProjectionStatistics(projectionName); - - string status; - var isFaulted = dic != null && dic.TryGetValue("status", out status) && status.StartsWith("Faulted"); - - if (isFaulted) - { - dic.TryGetValue("stateReason", out reason); - } - else - { - reason = null; - } - - return isFaulted; - } - - private Dictionary GetProjectionStatistics(string projectionName) - { - string rawState; - try - { - rawState = GetProjectionsManager().GetStatisticsAsync(projectionName, AdminCredentials).Result; - } - catch (Exception ex) - { - Log.Information(ex, "Failed to read projection statistics. Will continue."); - rawState = null; - } - - Log.Information("Raw {projection} stats: {rawState}", projectionName, rawState); - - if (string.IsNullOrEmpty(rawState)) - { - return null; - } - - if (rawState == "*** UNKNOWN ***") - { - return null; - } - - var start = rawState.IndexOf('['); - var end = rawState.IndexOf(']'); - - if (start == -1 || end == -1) - { - return null; - } - - var statDic = rawState.Substring(start + 1, end - start - 1); - - var state = TestClientJson.From>(statDic); - return state; - } - - private Dictionary GetProjectionState(string projectionName) - { - var rawState = GetProjectionStateSafe(projectionName); - - Log.Information("Raw {projection} state: {rawState}", projectionName, rawState); - - if (string.IsNullOrEmpty(rawState)) - { - return null; - } - - if (rawState == "*** UNKNOWN ***") - { - return null; - } - - var state = TestClientJson.From>(rawState); - return state; - } - - private string GetProjectionStateSafe(string projectionName) - { - string rawState; - try - { - rawState = GetProjectionsManager().GetStateAsync(projectionName, AdminCredentials).Result; - } - catch (Exception ex) - { - rawState = null; - Log.Information(ex, "Failed to get projection state"); - } - - return rawState; - } - - protected void EnableProjectionByCategory() - { - const string byCategoryProjection = "$by_category"; - - var retryCount = 0; - Exception exception = null; - while (retryCount < 5) - { - Thread.Sleep(250 * (retryCount * retryCount)); - try - { - var isRunning = GetProjectionIsRunning(byCategoryProjection); - - if (!isRunning) - { - Log.Debug("Enable *{projection}* projection", byCategoryProjection); - GetProjectionsManager().EnableAsync(byCategoryProjection, AdminCredentials).Wait(); - } - else - { - Log.Debug("Already enabled *{projection}* projection", byCategoryProjection); - } - - exception = null; - break; - } - catch (Exception ex) - { - exception = new ApplicationException("Failed to enable by_category.", ex); - Log.Error(ex, "Failed to enable *$by_category* projection, retry #{retryCount}.", - retryCount); - } - - retryCount += 1; - } - - if (exception != null) - { - throw exception; - } - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/ScenarioBase.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/ScenarioBase.cs deleted file mode 100644 index 0c32302ffd..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/ScenarioBase.cs +++ /dev/null @@ -1,705 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.ClientAPI.Projections; -using EventStore.ClientAPI.SystemData; -using EventStore.Core.Services; -using ConsoleLogger = EventStore.ClientAPI.Common.Log.ConsoleLogger; -using ILogger = Serilog.ILogger; -using TcpCommand = EventStore.Core.Services.Transport.Tcp.TcpCommand; -using TcpPackage = EventStore.Core.Services.Transport.Tcp.TcpPackage; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal abstract class ScenarioBase : IScenario -{ - protected static readonly ILogger Log = Serilog.Log.ForContext(); - - protected static readonly ClientAPI.ILogger ApiLogger = - new ClientApiLoggerBridge(Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, - "client-api")); - - protected readonly UserCredentials AdminCredentials = - new UserCredentials(SystemUsers.Admin, SystemUsers.DefaultAdminPassword); - - protected readonly Action DirectSendOverTcp; - protected readonly int MaxConcurrentRequests; - protected readonly int Connections; - protected readonly int Streams; - protected readonly int EventsPerStream; - protected readonly int StreamDeleteStep; - - private readonly NodeConnectionInfo _customNodeConnection; - private readonly NodeConnectionInfo _nodeConnection; - - private readonly HashSet _startedNodesProcIds; - - private readonly string _dbPath; - - private readonly Dictionary, Task>> _writeHandlers; - private readonly IEventStoreConnection[] _connections; - private int _nextConnectionNum = -1; - private readonly ProjectionsManager _projectionsManager; - - protected virtual TimeSpan StartupWaitInterval - { - get { return TimeSpan.FromSeconds(12); } - } - - protected ScenarioBase(Action directSendOverTcp, - int maxConcurrentRequests, - int connections, - int streams, - int eventsPerStream, - int streamDeleteStep, - string dbParentPath, - NodeConnectionInfo customNodeConnection) - { - DirectSendOverTcp = directSendOverTcp; - MaxConcurrentRequests = maxConcurrentRequests; - Connections = connections; - Streams = streams; - EventsPerStream = eventsPerStream; - StreamDeleteStep = streamDeleteStep; - _customNodeConnection = customNodeConnection; - - _startedNodesProcIds = new HashSet(); - - var ipAddress = IPAddress.Loopback; - - if (_customNodeConnection != null) - { - _nodeConnection = _customNodeConnection; - _dbPath = null; - } - else - { - _dbPath = CreateNewDbPath(dbParentPath); - _nodeConnection = new NodeConnectionInfo(ipAddress, - PortsHelper.GetAvailablePort(ipAddress), - PortsHelper.GetAvailablePort(ipAddress)); - } - - _connections = new IEventStoreConnection[connections]; - - Log.Information("Projection manager points to {nodeConnection}.", _nodeConnection); - _projectionsManager = new ProjectionsManager(new ConsoleLogger(), - new IPEndPoint(_nodeConnection.IpAddress, _nodeConnection.HttpPort), TimeSpan.FromMilliseconds(5000)); - - _writeHandlers = new Dictionary, Task>> { - {WriteMode.SingleEventAtTime, WriteSingleEventAtTime}, - {WriteMode.Bucket, WriteBucketOfEventsAtTime}, - {WriteMode.Transactional, WriteEventsInTransactionalWay} - }; - } - - private static string CreateNewDbPath(string dbParentPath) - { - var dbParent = dbParentPath ?? Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - - var dataFolder = Path.Combine(dbParent, "data"); - var idx = 0; - var dbPath = Path.Combine(dataFolder, string.Format("es_{0}", idx)); - - while (Directory.Exists(dbPath)) - { - idx += 1; - dbPath = Path.Combine(dataFolder, string.Format("es_{0}", idx)); - } - - return dbPath; - } - - protected IEventStoreConnection GetConnection() - { - var connectionNum = (int)(((uint)Interlocked.Increment(ref _nextConnectionNum)) % Connections); - return _connections[connectionNum]; - } - - protected ProjectionsManager GetProjectionsManager() - { - return _projectionsManager; - } - - public void Run() - { - const int maxReconnections = 200; - const int maxOperationRetries = 200; - - for (int i = 0; i < Connections; ++i) - { - _connections[i] = EventStoreConnection.Create( - ConnectionSettings.Create() - .UseCustomLogger(ApiLogger) - .LimitConcurrentOperationsTo(MaxConcurrentRequests) - .LimitRetriesForOperationTo(maxReconnections) - .LimitReconnectionsTo(maxOperationRetries) - .FailOnNoServerResponse(), - new Uri(string.Format("tcp://admin:changeit@{0}:{1}", _nodeConnection.IpAddress, - _nodeConnection.TcpPort)), - string.Format("ESConn-{0}", i)); - _connections[i].Closed += (s, e) => - Log.Debug("[SCENARIO] {connection} closed.", e.Connection.ConnectionName); - _connections[i].Connected += (s, e) => - Log.Debug("[SCENARIO] {connection} connected to [{remoteEndPoint}].", e.Connection.ConnectionName, - e.RemoteEndPoint); - _connections[i].Disconnected += (s, e) => - Log.Debug("[SCENARIO] {connection} disconnected from [{remoteEndPoint}].", - e.Connection.ConnectionName, e.RemoteEndPoint); - _connections[i].Reconnecting += (s, e) => - Log.Debug("[SCENARIO] {connection} reconnecting.", e.Connection.ConnectionName); - _connections[i].ErrorOccurred += (s, e) => Log.Debug(e.Exception, - "[SCENARIO] {connection} error occurred.", e.Connection.ConnectionName); - _connections[i].ConnectAsync().Wait(); - } - - RunInternal(); - } - - protected abstract void RunInternal(); - - public void Clean() - { - CloseConnections(); - KillStartedNodes(); - DeleteDatabase(); - } - - private void DeleteDatabase() - { - try - { - if (_dbPath != null) - { - Log.Information("Deleting {dbPath}...", _dbPath); - Directory.Delete(_dbPath, true); - Log.Information("Deleted {dbPath}", _dbPath); - } - } - catch (IOException ex) - { - Log.Error(ex, "Failed to delete dir {dbPath}, IOException was raised", _dbPath); - } - catch (UnauthorizedAccessException ex) - { - Log.Error(ex, "Failed to delete dir {dbPath}, UnauthorizedAccessException was raised", - _dbPath); - } - } - - protected T[][] Split(IEnumerable sequence, int parts) - { - return sequence.Select((x, i) => new { GroupNum = i % parts, Item = x }) - .GroupBy(x => x.GroupNum, y => y.Item) - .Select(x => x.ToArray()) - .ToArray(); - } - - - protected Task Write(WriteMode mode, string[] streams, int eventsPerStream) - { - Func createEvent = TestEvent.NewTestEvent; - return Write(mode, streams, eventsPerStream, createEvent); - } - - protected Task Write(WriteMode mode, string[] streams, int eventsPerStream, Func createEvent) - { - Log.Information( - "Writing. Mode : {mode,-15} Streams : {streamsLength,-10} Events per stream : {eventsPerStream,-10}", - mode, - streams.Length, - eventsPerStream); - - Func, Task> handler; - if (!_writeHandlers.TryGetValue(mode, out handler)) - { - throw new ArgumentOutOfRangeException("mode"); - } - - var tasks = new List(); - for (var i = 0; i < streams.Length; i++) - { - //Console.WriteLine("WRITING TO {0}", streams[i]); - tasks.Add(handler(streams[i], eventsPerStream, createEvent)); - } - - return Task.Factory.ContinueWhenAll(tasks.ToArray(), tsks => - { - Task.WaitAll(tsks); - Log.Information( - "Finished writing. Mode : {mode,-15} Streams : {streamsLength,-10} Events per stream : {eventsPerStream,-10}", - mode, - streams.Length, - eventsPerStream); - }); - } - - protected void DeleteStreams(IEnumerable streams) - { - Log.Information("Deleting streams..."); - var store = GetConnection(); - - var tasks = new List(); - foreach (var stream in streams) - { - var s = stream; - Log.Information("Deleting stream {stream}...", stream); - var task = store.DeleteStreamAsync(stream, (EventsPerStream - 1), hardDelete: true) - .ContinueWith(x => Log.Information("Stream {stream} successfully deleted", s)); - - tasks.Add(task); - } - - Task.WaitAll(tasks.ToArray()); - - Log.Information("All streams successfully deleted"); - } - - protected Task CheckStreamsDeleted(IEnumerable streams) - { - Log.Information("Verifying streams are deleted..."); - - var store = GetConnection(); - var tasks = new List(); - - foreach (var stream in streams) - { - var s = stream; - var task = store.ReadStreamEventsForwardAsync(stream, 0, 1, resolveLinkTos: false).ContinueWith(t => - { - if (t.Result.Status != SliceReadStatus.StreamDeleted) - { - throw new Exception(string.Format("Stream '{0}' is not deleted, but should be!", s)); - } - }); - - tasks.Add(task); - } - - return Task.Factory.ContinueWhenAll(tasks.ToArray(), tsks => - { - Task.WaitAll(tsks); - Log.Information("Stream deletion verification succeeded."); - }); - } - - protected Task Read(string[] streams, int @from, int count) - { - if (streams.Length == 0) - { - throw new Exception("Streams shouldn't be empty."); - } - - Log.Information("Reading [{streams}]\nfrom {from,-10} count {count,-10}", string.Join(",", streams), from, count); - - var tasks = new List(); - - for (int i = 0; i < streams.Length; i++) - { - var task = ReadStream(streams[i], from, count); - tasks.Add(task); - } - - return Task.Factory.ContinueWhenAll(tasks.ToArray(), tsks => - { - Task.WaitAll(tsks); - Log.Information("Done reading [{streams}]", string.Join(",", streams)); - }); - } - - private bool TryGetPathToMono(out string pathToMono) - { - const string monopathVariable = "EVENTSTORE_MONOPATH"; - pathToMono = Environment.GetEnvironmentVariable(monopathVariable); - return !string.IsNullOrEmpty(pathToMono); - } - - protected int StartNode() - { - int processId = -1; - if (_customNodeConnection == null) - { - processId = StartNewNode(); - } - - return processId; - } - - private int StartNewNode() - { - var clientFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - //../../EventStore.ClusterNode/net471/EventStore.ClusterNode.exe - var clusterNodeFolder = System.IO.Directory.GetParent(System.IO.Directory.GetParent(clientFolder).FullName) - + "/EventStore.ClusterNode/net471/"; - string fileName; - string argumentsHead; - - string pathToMono; - if (TryGetPathToMono(out pathToMono)) - { - Log.Information("Mono at {pathToMono} will be used.", pathToMono); - fileName = pathToMono; - argumentsHead = string.Format("--debug --gc=sgen {0}", - Path.Combine(clusterNodeFolder, "EventStore.ClusterNode.exe")); - } - else - { - fileName = Path.Combine(clusterNodeFolder, "EventStore.ClusterNode.exe"); - argumentsHead = ""; - } - - var arguments = string.Format( - "{0} --run-projections=all --ext-ip {1} --ext-tcp-port {2} --ext-http-port {3} --db {4}", - argumentsHead, - _nodeConnection.IpAddress, - _nodeConnection.TcpPort, - _nodeConnection.HttpPort, - _dbPath); - - Log.Information("Starting [{fileName} {arguments}]...", fileName, arguments); - - var startInfo = new ProcessStartInfo(fileName, arguments); - - var nodeProcess = Process.Start(startInfo); - if (nodeProcess == null || nodeProcess.HasExited) - { - throw new ApplicationException(string.Format("Process was not started [{0} {1}].", fileName, - arguments)); - } - - Thread.Sleep(3000); - Process tmp; - var running = TryGetProcessById(nodeProcess.Id, out tmp); - if (!running || tmp.HasExited) - { - throw new ApplicationException(string.Format("Process was not started [{0} {1}].", fileName, - arguments)); - } - - _startedNodesProcIds.Add(nodeProcess.Id); - - Log.Information("Started node with process id {id}", nodeProcess.Id); - - Thread.Sleep(StartupWaitInterval); - Log.Information("Started [{fileName} {arguments}]", fileName, arguments); - - return nodeProcess.Id; - } - - private bool TryGetProcessById(int processId, out Process process) - { - process = null; - - try - { - process = Process.GetProcessById(processId); - } - catch (ArgumentException) - { - return false; - } - catch (InvalidOperationException) - { - return false; - } - - return true; - } - - protected void KillNode(int processId) - { - if (processId != -1) - { - KillStartedNode(processId); - } - else - { - Log.Information("Skip killing, procId -1"); - } - } - - private void KillStartedNode(int processId) - { - Log.Information("Killing {processId}...", processId); - - Process process; - if (TryGetProcessById(processId, out process)) - { - process.Kill(); - - var waitCount = 200; - while (!process.HasExited && waitCount > 0) - { - Thread.Sleep(250); - waitCount -= 1; - } - - if (process.HasExited) - { - _startedNodesProcIds.Remove(processId); - - PortsHelper.ReturnPort(_nodeConnection.TcpPort); - PortsHelper.ReturnPort(_nodeConnection.HttpPort); - - Log.Information("Killed process {processId}, wait a bit.", processId); - Thread.Sleep(1000); // wait for system to release port used by HttpListener. - } - else - { - if (TryGetProcessById(processId, out _)) - { - Log.Error( - "Process {processId} did not report about exit in time and is still present in processes list.", - processId); - } - else - { - Log.Information("Process {processId} did not report about exit in time but is not found again.", - processId); - } - } - } - else - { - Log.Error("Process {processId} was not found to be killed.", processId); - } - } - - public void Dispose() - { - CloseConnections(); - Thread.Sleep(2 * 1000); - KillStartedNodes(); - } - - private void CloseConnections() - { - for (int i = 0; i < _connections.Length; ++i) - { - _connections[i].Close(); - } - } - - private void KillStartedNodes() - { - Log.Information("Killing remaining nodes..."); - try - { - _startedNodesProcIds.ToList().ForEach(KillNode); - } - catch (Exception ex) - { - Log.Error("Failed to kill started nodes: {e}.", ex.Message); - } - } - - protected void Scavenge() - { - Log.Information("Send scavenge command..."); - var package = new TcpPackage(TcpCommand.ScavengeDatabase, Guid.NewGuid(), null).AsByteArray(); - DirectSendOverTcp(new IPEndPoint(_nodeConnection.IpAddress, _nodeConnection.TcpPort), package); - Log.Information("Scavenge command was sent."); - } - - private Task WriteSingleEventAtTime(string stream, int events, Func createEvent) - { - var resSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - - Log.Information("Starting to write {events} events to [{stream}]", events, stream); - var store = GetConnection(); - int eventVersion = 0; - - Action fail = prevTask => - { - Log.Information("WriteSingleEventAtTime for stream {stream} failed.", stream); - resSource.SetException(prevTask.Exception); - }; - - Action writeSingleEvent = null; - writeSingleEvent = _ => - { - if (eventVersion == events) - { - Log.Information("Wrote {events} events to [{stream}]", events, stream); - resSource.SetResult(null); - return; - } - - var writeTask = store.AppendToStreamAsync(stream, - eventVersion - 1, - new[] { createEvent(eventVersion) }); - - eventVersion += 1; - - writeTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - writeTask.ContinueWith(writeSingleEvent, TaskContinuationOptions.OnlyOnRanToCompletion); - }; - - writeSingleEvent(null); - - return resSource.Task; - } - - private Task WriteBucketOfEventsAtTime(string stream, int eventCount, Func createEvent) - { - const int bucketSize = 25; - Log.Information("Starting to write {eventCount} events to [{stream}] ({bucketSize} events at once)", eventCount, - stream, bucketSize); - - var resSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var store = GetConnection(); - int writtenCount = 0; - - Action fail = prevTask => - { - Log.Information("WriteBucketOfEventsAtTime for stream {stream} failed.", stream); - resSource.SetException(prevTask.Exception); - }; - - Action writeBatch = null; - writeBatch = _ => - { - if (writtenCount == eventCount) - { - Log.Information("Wrote {eventCount} events to [{stream}] ({bucketSize} events at once)", eventCount, - stream, bucketSize); - resSource.SetResult(null); - return; - } - - var startIndex = writtenCount; - var endIndex = Math.Min(eventCount, startIndex + bucketSize); - var events = Enumerable.Range(startIndex, endIndex - startIndex).Select(createEvent).ToArray(); - - writtenCount = endIndex; - - var expectedVersion = startIndex - 1; - var writeTask = store.AppendToStreamAsync(stream, expectedVersion, events); - - writeTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - writeTask.ContinueWith(writeBatch, TaskContinuationOptions.OnlyOnRanToCompletion); - }; - - writeBatch(null); - - return resSource.Task; - } - - private Task WriteEventsInTransactionalWay(string stream, int eventCount, Func createEvent) - { - Log.Information("Starting to write {eventCount} events to [{stream}] (in single transaction)", eventCount, stream); - - var resSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var store = GetConnection(); - - Action fail = prevTask => - { - Log.Information("WriteEventsInTransactionalWay for stream {stream} failed.", stream); - resSource.SetException(prevTask.Exception); - }; - - int writtenCount = 0; - EventStoreTransaction transaction = null; - - Action writeTransactionEvent = null; - writeTransactionEvent = prevTask => - { - if (writtenCount == eventCount) - { - var commitTask = transaction.CommitAsync(); - commitTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - commitTask.ContinueWith(t => - { - Log.Information("Wrote {eventCount} events to [{stream}] (in single transaction)", eventCount, stream); - resSource.SetResult(null); - }, TaskContinuationOptions.OnlyOnRanToCompletion); - return; - } - - var writeTask = transaction.WriteAsync(createEvent(writtenCount)); - - writtenCount += 1; - - writeTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - writeTask.ContinueWith(writeTransactionEvent, TaskContinuationOptions.OnlyOnRanToCompletion); - }; - - var startTask = store.StartTransactionAsync(stream, -1); - startTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - startTask.ContinueWith(t => - { - transaction = t.Result; - writeTransactionEvent(t); - }, TaskContinuationOptions.OnlyOnRanToCompletion); - - return resSource.Task; - } - - private Task ReadStream(string stream, int from, int count) - { - Log.Information("Reading [{stream}] from {from,-10} count {count,-10}", stream, from, count); - var resSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); - var store = GetConnection(); - - Action fail = prevTask => - { - Log.Information("ReadStream for stream {stream} failed.", stream); - resSource.SetException(prevTask.Exception); - }; - - var readTask = store.ReadStreamEventsForwardAsync(stream, @from, count, resolveLinkTos: false); - readTask.ContinueWith(fail, TaskContinuationOptions.OnlyOnFaulted); - readTask.ContinueWith(t => - { - try - { - var slice = t.Result; - if (slice?.Events == null || slice.Events.Length != count) - { - throw new Exception(string.Format( - "Tried to read {0} events from event number {1} from stream '{2}' but failed. Reason: {3}.", - count, - from, - stream, - slice == null ? "slice == null" - : slice.Events == null ? "slive.Events == null" - : slice.Events.Length != count ? string.Format("Expected count: {0}, actual count: {1}.", - count, slice.Events.Length) - : "WAT?!?")); - } - - for (int i = 0; i < count; ++i) - { - var evnt = slice.Events[i].Event; - if (evnt.EventNumber != i + from) - { - throw new Exception(string.Format( - "Received event with wrong event number. Expected: {0}, actual: {1}.\nEvent: {2}.", - from + i, - evnt.EventNumber, - evnt)); - } - - TestEvent.VerifyIfMatched(evnt); - } - - Log.Information("Done reading [{stream}] from {from,-10} count {count,-10}", stream, from, count); - resSource.SetResult(null); - } - catch (Exception exc) - { - Log.Information("ReadStream for stream {stream} failed.", stream); - resSource.SetException(exc); - } - }, TaskContinuationOptions.OnlyOnRanToCompletion); - - return resSource.Task; - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/TestEvent.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/TestEvent.cs deleted file mode 100644 index daba21602e..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/TestEvent.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System; -using System.Text; -using EventStore.ClientAPI; -using EventStore.Common.Utils; - -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal class TestEvent -{ - public static EventData NewTestEvent(int index) - { - var subIndex = (index % 50); - var type = "TestEvent-" + subIndex.ToString(); - var body = new string('#', 1 + subIndex * subIndex); - var encodedData = Helper.UTF8NoBom.GetBytes(string.Format("{0}-{1}-{2}", index, body.Length, body)); - - return new EventData(Guid.NewGuid(), type, false, encodedData, new byte[0]); - } - - public static void VerifyIfMatched(RecordedEvent evnt) - { - if (evnt.EventType.StartsWith("TestEvent")) - { - var data = Common.Utils.Helper.UTF8NoBom.GetString(evnt.Data); - var atoms = data.Split('-'); - if (atoms.Length != 3) - { - throw new ApplicationException(string.Format("Invalid TestEvent object: currupted data format: {0}", - RecordDetailsString(evnt))); - } - - var expectedLength = int.Parse(atoms[1]); - if (expectedLength != atoms[2].Length) - { - throw new ApplicationException(string.Format( - "Invalid TestEvent object: not expected data length: {0}", - RecordDetailsString(evnt))); - } - - if (new string('#', expectedLength) != atoms[2]) - { - throw new ApplicationException(string.Format("Invalid TestEvent object: currupted data: {0}", - RecordDetailsString(evnt))); - } - } - } - - private static string RecordDetailsString(RecordedEvent evnt) - { - var data = Common.Utils.Helper.UTF8NoBom.GetString(evnt.Data); - return string.Format("[stream:{0}; eventNumber:{1}; type:{2}; data:{3}]", - evnt.EventStreamId, - evnt.EventNumber, - evnt.EventType, - data.Length > 12 ? (data.Substring(0, 12) + "...") : data); - } -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenarios/WriteMode.cs b/src/EventStore.TestClient/Commands/RunTestScenarios/WriteMode.cs deleted file mode 100644 index 7f06a592e3..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenarios/WriteMode.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace EventStore.TestClient.Commands.RunTestScenarios; - -internal enum WriteMode -{ - SingleEventAtTime, - Bucket, - Transactional -} diff --git a/src/EventStore.TestClient/Commands/RunTestScenariosProcessor.cs b/src/EventStore.TestClient/Commands/RunTestScenariosProcessor.cs deleted file mode 100644 index 98fa09860b..0000000000 --- a/src/EventStore.TestClient/Commands/RunTestScenariosProcessor.cs +++ /dev/null @@ -1,280 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.TestClient.Commands.RunTestScenarios; -using EventStore.Transport.Tcp; -using ILogger = Serilog.ILogger; - -namespace EventStore.TestClient.Commands; - -internal class RunTestScenariosProcessor : ICmdProcessor -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - private const string AllScenariosFlag = "ALL"; - - public string Keyword - { - get { return "RT"; } - } - - public string Usage - { - get - { - const string usage = " " + - "\n " + - "\n " + - "\n " + - "\n " + - "\n" + - "\n" + - "\n"; - - return usage; - } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - if (args.Length != 0 && false == (args.Length == 7 || args.Length == 8)) - { - return false; - } - - var maxConcurrentRequests = 20; - var connections = 10; - var streams = 100; - var eventsPerStream = 400; - var streamDeleteStep = 7; - var scenarioName = ""; - var executionPeriodMinutes = 2; - string dbParentPath = null; - NodeConnectionInfo customNode = null; - - { - if (args.Length == 9) - { - throw new ArgumentException( - "Not compatible arguments, only one of or can be specified."); - } - - if (args.Length == 8) - { - IPAddress ip; - int tcpPort; - int httpPort; - - var atoms = args[7].Split(':'); - if (atoms.Length == 3 - && IPAddress.TryParse(atoms[0], out ip) - && int.TryParse(atoms[1], out tcpPort) - && int.TryParse(atoms[2], out httpPort)) - { - customNode = new NodeConnectionInfo(ip, tcpPort, httpPort); - - args = CutLastArgument(args); - } - } - - if (args.Length == 7 || args.Length == 8) - { - try - { - maxConcurrentRequests = int.Parse(args[0]); - connections = int.Parse(args[1]); - streams = int.Parse(args[2]); - eventsPerStream = int.Parse(args[3]); - streamDeleteStep = int.Parse(args[4]); - scenarioName = args[5]; - executionPeriodMinutes = int.Parse(args[6]); - - if (args.Length == 8) - { - dbParentPath = args[7]; - } - else - { - var envDbPath = Environment.GetEnvironmentVariable("EVENTSTORE_DATABASEPATH"); - if (!string.IsNullOrEmpty(envDbPath)) - { - dbParentPath = envDbPath; - } - } - } - catch (Exception e) - { - Log.Error("Invalid arguments ({e})", e.Message); - return false; - } - } - } - - context.IsAsync(); - - Log.Information("\n---" + - "\nRunning scenario {scenario} using {connections} connections with {maxConcurrentRequests} max concurrent requests," + - "\nfor {streams} streams {eventsPerStream} events each deleting every {streamDeleteStep}th stream. " + - "\nExecution period {executionPeriod} minutes. " + - "\nDatabase path {dbParentPath};" + - "\nCustom Node {customNode};" + - "\n---", - scenarioName, - connections, - maxConcurrentRequests, - streams, - eventsPerStream, - streamDeleteStep, - executionPeriodMinutes, - dbParentPath, - customNode); - - var directTcpSender = CreateDirectTcpSender(context); - var allScenarios = new IScenario[] { - new LoopingScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - TimeSpan.FromMinutes(executionPeriodMinutes), - dbParentPath, - customNode), - new ProjectionsKillScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - dbParentPath, - customNode), - new ProjForeachForcedCommonNameScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - TimeSpan.FromMinutes(executionPeriodMinutes), - dbParentPath, - customNode), - new ProjForeachForcedCommonNameNoRestartScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - TimeSpan.FromMinutes(executionPeriodMinutes), - dbParentPath, - customNode), - new LoopingProjTranWriteScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - TimeSpan.FromMinutes(executionPeriodMinutes), - dbParentPath, - customNode), - new LoopingProjectionKillScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - TimeSpan.FromMinutes(executionPeriodMinutes), - dbParentPath, - customNode), - new MassProjectionsScenario(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - dbParentPath, - customNode), - new ProjectionWrongTagCheck(directTcpSender, - maxConcurrentRequests, - connections, - streams, - eventsPerStream, - streamDeleteStep, - TimeSpan.FromMinutes(executionPeriodMinutes), - dbParentPath, - customNode), - }; - - Log.Information("Found scenarios {scenariosCount} total :\n{scenarios}.", allScenarios.Length, - allScenarios.Aggregate(new StringBuilder(), - (sb, s) => sb.AppendFormat("{0}, ", s.GetType().Name))); - var scenarios = allScenarios.Where(x => scenarioName == AllScenariosFlag - || x.GetType().Name.Equals(scenarioName, - StringComparison.InvariantCultureIgnoreCase)) - .ToArray(); - - Log.Information("Running test scenarios ({scenarios} total)...", scenarios.Length); - - foreach (var scenario in scenarios) - { - using (scenario) - { - try - { - Log.Information("Run scenario {type}", scenario.GetType().Name); - scenario.Run(); - scenario.Clean(); - Log.Information("Scenario run successfully"); - } - catch (Exception e) - { - context.Fail(e); - } - } - } - - Log.Information("Finished running test scenarios"); - - if (context.ExitCode == 0) - { - context.Success(); - } - - return true; - } - - private static string[] CutLastArgument(string[] args) - { - var cutArgs = new string[7]; - Array.Copy(args, cutArgs, 7); - return cutArgs; - } - - private Action CreateDirectTcpSender(CommandProcessorContext context) - { - const int timeoutMilliseconds = 4000; - - Action sender = (tcpEndPoint, bytes) => - { - var sent = new AutoResetEvent(false); - - Action, TcpPackage> handlePackage = (_, __) => { }; - Action> established = connection => - { - connection.EnqueueSend(bytes); - connection.Close(); - sent.Set(); - }; - Action, SocketError> closed = (_, __) => sent.Set(); - - context._tcpTestClient.CreateTcpConnection(context, handlePackage, established, closed, false, tcpEndPoint); - if (!sent.WaitOne(timeoutMilliseconds)) - { - throw new ApplicationException("Connection to server was not closed in time."); - } - }; - - return sender; - } -} diff --git a/src/EventStore.TestClient/Commands/ScavengeProcessor.cs b/src/EventStore.TestClient/Commands/ScavengeProcessor.cs deleted file mode 100644 index 28ca0af213..0000000000 --- a/src/EventStore.TestClient/Commands/ScavengeProcessor.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Net.Sockets; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class ScavengeProcessor : ICmdProcessor -{ - public string Usage - { - get { return Keyword; } - } - - public string Keyword - { - get { return "SCAVENGE"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - var package = new TcpPackage(TcpCommand.ScavengeDatabase, Guid.NewGuid(), null); - context.Log.Information("Sending SCAVENGE request..."); - - var connection = context._tcpTestClient.CreateTcpConnection( - context, - (conn, pkg) => { }, - null, - (typedConnection, error) => - { - if (error == SocketError.Success) - { - context.Success(); - } - else - { - context.Fail(); - } - }); - connection.EnqueueSend(package.AsByteArray()); - connection.Close("OK"); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/SubscribeToStreamProcessor.cs b/src/EventStore.TestClient/Commands/SubscribeToStreamProcessor.cs deleted file mode 100644 index aab311401b..0000000000 --- a/src/EventStore.TestClient/Commands/SubscribeToStreamProcessor.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net.Sockets; -using EventStore.Client.Messages; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class SubscribeToStreamProcessor : ICmdProcessor -{ - public string Usage - { - get { return "SUBSCR [ ... ]"; } - } - - public string Keyword - { - get { return "SUBSCR"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - context.IsAsync(); - - var streamByCorrId = new Dictionary(); - - var connection = context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => { }, - handlePackage: (conn, pkg) => - { - switch (pkg.Command) - { - case TcpCommand.SubscriptionConfirmation: - { - var dto = pkg.Data.Deserialize(); - context.Log.Information( - "Subscription to <{stream}> WAS CONFIRMED! Subscribed at {lastIndexedPosition} ({lastEventNumber})", - streamByCorrId[pkg.CorrelationId], dto.LastCommitPosition, dto.LastEventNumber); - break; - } - case TcpCommand.StreamEventAppeared: - { - var dto = pkg.Data.Deserialize(); - context.Log.Information("NEW EVENT:\n\n" - + "\tEventStreamId: {stream}\n" - + "\tEventNumber: {eventNumber}\n" - + "\tEventType: {eventType}\n" - + "\tData: {data}\n" - + "\tMetadata: {metadata}\n", - dto.Event.Event.EventStreamId, - dto.Event.Event.EventNumber, - dto.Event.Event.EventType, - Common.Utils.Helper.UTF8NoBom.GetString(dto.Event.Event.Data.ToByteArray()), - Common.Utils.Helper.UTF8NoBom.GetString(dto.Event.Event.Metadata.ToByteArray())); - break; - } - case TcpCommand.SubscriptionDropped: - { - pkg.Data.Deserialize(); - context.Log.Error("Subscription to <{stream}> WAS DROPPED!", - streamByCorrId[pkg.CorrelationId]); - break; - } - default: - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - break; - } - }, - connectionClosed: (c, error) => - { - if (error == SocketError.Success) - { - context.Success(); - } - else - { - context.Fail(); - } - }); - - if (args.Length == 0) - { - context.Log.Information("SUBSCRIBING TO ALL STREAMS..."); - var cmd = new SubscribeToStream(string.Empty, resolveLinkTos: false); - Guid correlationId = Guid.NewGuid(); - streamByCorrId[correlationId] = "$all"; - connection.EnqueueSend(new TcpPackage(TcpCommand.SubscribeToStream, correlationId, cmd.Serialize()) - .AsByteArray()); - } - else - { - foreach (var stream in args) - { - context.Log.Information("SUBSCRIBING TO STREAM <{stream}>...", stream); - var cmd = new SubscribeToStream(stream, resolveLinkTos: false); - var correlationId = Guid.NewGuid(); - streamByCorrId[correlationId] = stream; - connection.EnqueueSend(new TcpPackage(TcpCommand.SubscribeToStream, correlationId, cmd.Serialize()) - .AsByteArray()); - } - } - - context.WaitForCompletion(); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/SubscriptionStressTestProcessor.cs b/src/EventStore.TestClient/Commands/SubscriptionStressTestProcessor.cs deleted file mode 100644 index 4a347a6a05..0000000000 --- a/src/EventStore.TestClient/Commands/SubscriptionStressTestProcessor.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using EventStore.ClientAPI; -using EventStore.Common.Utils; - -namespace EventStore.TestClient.Commands; - -internal class SubscriptionStressTestProcessor : ICmdProcessor -{ - public string Usage - { - get { return "SST []"; } - } - - public string Keyword - { - get { return "SST"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int subscriptionCount = 5000; - - if (args.Length > 0) - { - if (args.Length > 1) - { - return false; - } - - subscriptionCount = MetricPrefixValue.ParseInt(args[0]); - } - - context.IsAsync(); - - var conn = EventStoreConnection.Create(ConnectionSettings.Create() - .UseCustomLogger(new ClientApiLoggerBridge(context.Log)) - .FailOnNoServerResponse() - /*.EnableVerboseLogging()*/, - new Uri($"tcp://{context._tcpTestClient.TcpEndpoint.GetHost()}:{context._tcpTestClient.TcpEndpoint.GetPort()}")); - conn.ConnectAsync().Wait(); - - long appearedCnt = 0; - var sw = Stopwatch.StartNew(); - for (int i = 0; i < subscriptionCount; ++i) - { - conn.SubscribeToStreamAsync( - string.Format("stream-{0}", i), - false, - (s, e) => - { - var c = Interlocked.Increment(ref appearedCnt); - if (c % 1000 == 0) - { - Console.Write('\''); - } - - if (c % 100000 == 0) - { - context.Log.Debug("Received total {events} events ({rate} per sec)...", c, - 100000.0 / sw.Elapsed.TotalSeconds); - sw.Restart(); - } - - return Task.CompletedTask; - }).Wait(); - } - - context.Log.Information("Subscribed to {subscriptionCount} streams...", subscriptionCount); - return true; - } -} diff --git a/src/EventStore.TestClient/Commands/TcpSanitazationCheckProcessor.cs b/src/EventStore.TestClient/Commands/TcpSanitazationCheckProcessor.cs deleted file mode 100644 index d65887161d..0000000000 --- a/src/EventStore.TestClient/Commands/TcpSanitazationCheckProcessor.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; -using ILogger = Serilog.ILogger; - -namespace EventStore.TestClient.Commands; - -internal class TcpSanitazationCheckProcessor : ICmdProcessor -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - - public string Keyword - { - get { return "CHKTCP"; } - } - - public string Usage - { - get { return Keyword; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - context.IsAsync(); - - var commandsToCheck = new[] { - (byte)TcpCommand.WriteEvents, - (byte)TcpCommand.TransactionStart, - (byte)TcpCommand.TransactionWrite, - (byte)TcpCommand.TransactionCommit, - (byte)TcpCommand.DeleteStream, - (byte)TcpCommand.ReadEvent, - (byte)TcpCommand.ReadStreamEventsForward, - (byte)TcpCommand.ReadStreamEventsBackward, - (byte)TcpCommand.ReadAllEventsForward, - (byte)TcpCommand.ReadAllEventsBackward, - (byte)TcpCommand.SubscribeToStream, - (byte)TcpCommand.UnsubscribeFromStream, - }; - - var packages = commandsToCheck.Select(c => - new TcpPackage((TcpCommand)c, Guid.NewGuid(), new byte[] { 0, 1, 0, 1 }).AsByteArray()) - .Union(new[] { - BitConverter.GetBytes(int.MaxValue).Union(new byte[] {1, 2, 3, 4}).ToArray(), - BitConverter.GetBytes(int.MinValue).Union(new byte[] {1, 2, 3, 4}).ToArray(), - BitConverter.GetBytes(0).Union(Enumerable.Range(0, 256).Select(x => (byte)x)).ToArray() - }); - - int step = 0; - foreach (var pkg in packages) - { - var established = new AutoResetEvent(false); - var dropped = new AutoResetEvent(false); - - if (step < commandsToCheck.Length) - { - Console.WriteLine("{0} Starting step {1} ({2}) {0}", new string('#', 20), step, - (TcpCommand)commandsToCheck[step]); - } - else - { - Console.WriteLine("{0} Starting step {1} (RANDOM BYTES) {0}", new string('#', 20), step); - } - - var connection = context._tcpTestClient.CreateTcpConnection( - context, - (conn, package) => - { - if (package.Command != TcpCommand.BadRequest) - { - context.Fail(null, string.Format("Bad request expected, got {0}!", package.Command)); - } - }, - conn => established.Set(), - (conn, err) => dropped.Set()); - - established.WaitOne(); - connection.EnqueueSend(pkg); - dropped.WaitOne(); - - if (step < commandsToCheck.Length) - { - Console.WriteLine("{0} Step {1} ({2}) Completed {0}", new string('#', 20), step, - (TcpCommand)commandsToCheck[step]); - } - else - { - Console.WriteLine("{0} Step {1} (RANDOM BYTES) Completed {0}", new string('#', 20), step); - } - - step++; - } - - Log.Information( - "Sent {packages} packages. {commandsToCheck} invalid dtos, {barFormattedPackages} bar formatted packages. Got {badRequests} BadRequests. Success", - packages.Count(), - commandsToCheck.Length, - packages.Count() - commandsToCheck.Length, - packages.Count()); - - Log.Information("Now sending raw bytes..."); - try - { - SendRaw(context._tcpTestClient.TcpEndpoint, BitConverter.GetBytes(int.MaxValue)); - SendRaw(context._tcpTestClient.TcpEndpoint, BitConverter.GetBytes(int.MinValue)); - - SendRaw(context._tcpTestClient.TcpEndpoint, BitConverter.GetBytes(double.MinValue)); - SendRaw(context._tcpTestClient.TcpEndpoint, BitConverter.GetBytes(double.MinValue)); - - SendRaw(context._tcpTestClient.TcpEndpoint, BitConverter.GetBytes(new Random().NextDouble())); - } - catch (Exception e) - { - context.Fail(e, "Raw bytes sent failed"); - return false; - } - - context.Success(); - return true; - } - - private void SendRaw(EndPoint endPoint, byte[] package) - { - using var client = new TcpClient(); - client.Connect(endPoint.ResolveDnsToIPAddress()); - using var stream = client.GetStream(); - stream.Write(package, 0, package.Length); - } -} diff --git a/src/EventStore.TestClient/Commands/TestClientJson.cs b/src/EventStore.TestClient/Commands/TestClientJson.cs deleted file mode 100644 index 068c9ee3a5..0000000000 --- a/src/EventStore.TestClient/Commands/TestClientJson.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Serialization; - -namespace EventStore.TestClient.Commands; - -internal static class TestClientJson -{ - private static readonly JsonSerializerSettings FromSettings = new() - { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - DateParseHandling = DateParseHandling.None, - NullValueHandling = NullValueHandling.Ignore, - DefaultValueHandling = DefaultValueHandling.Include, - MissingMemberHandling = MissingMemberHandling.Ignore, - TypeNameHandling = TypeNameHandling.None, - MetadataPropertyHandling = MetadataPropertyHandling.Ignore, - Converters = [new StringEnumConverter()] - }; - - private static readonly JsonSerializerSettings ToSettings = new() - { - ContractResolver = new CamelCasePropertyNamesContractResolver(), - DateFormatHandling = DateFormatHandling.IsoDateFormat, - NullValueHandling = NullValueHandling.Include, - DefaultValueHandling = DefaultValueHandling.Include, - MissingMemberHandling = MissingMemberHandling.Ignore, - TypeNameHandling = TypeNameHandling.None, - Converters = [new StringEnumConverter()] - }; - - public static T From(string text) => - JsonConvert.DeserializeObject(text, FromSettings); - - public static string To(T value) => - value is null ? "" : JsonConvert.SerializeObject(value, Formatting.Indented, ToSettings); -} diff --git a/src/EventStore.TestClient/Commands/TransactionWriteProcessor.cs b/src/EventStore.TestClient/Commands/TransactionWriteProcessor.cs deleted file mode 100644 index 8da7210c81..0000000000 --- a/src/EventStore.TestClient/Commands/TransactionWriteProcessor.cs +++ /dev/null @@ -1,199 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net.Sockets; -using System.Text; -using EventStore.Client.Messages; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; -using OperationResult = EventStore.Client.Messages.OperationResult; - -namespace EventStore.TestClient.Commands; - -internal class TransactionWriteProcessor : ICmdProcessor -{ - public string Usage - { - get { return "TWR [ [ []]]"; } - } - - public string Keyword - { - get { return "TWR"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - var eventStreamId = "test-stream"; - var expectedVersion = ExpectedVersion.Any; - int eventsCnt = 10; - - if (args.Length > 0) - { - if (args.Length > 3) - { - return false; - } - - eventStreamId = args[0]; - if (args.Length > 1) - { - expectedVersion = args[1].ToUpper() == "ANY" ? ExpectedVersion.Any : int.Parse(args[1]); - } - - if (args.Length > 2) - { - eventsCnt = MetricPrefixValue.ParseInt(args[1]); - } - } - - context.IsAsync(); - - var sw = new Stopwatch(); - var stage = Stage.AcquiringTransactionId; - long transactionId = -1; - var writtenEvents = 0; - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information("[{remoteEndPoint}, L{localEndPoint}]: Starting transaction...", - conn.RemoteEndPoint, conn.LocalEndPoint); - sw.Start(); - - var tranStart = new TransactionStart(eventStreamId, expectedVersion, false); - var package = new TcpPackage(TcpCommand.TransactionStart, Guid.NewGuid(), tranStart.Serialize()); - conn.EnqueueSend(package.AsByteArray()); - }, - handlePackage: (conn, pkg) => - { - switch (stage) - { - case Stage.AcquiringTransactionId: - { - if (pkg.Command != TcpCommand.TransactionStartCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result != OperationResult.Success) - { - var msg = string.Format("Error while starting transaction: {0} ({1}).", dto.Message, - dto.Result); - context.Log.Information("Error while starting transaction: {message} ({e}).", dto.Message, - dto.Result); - context.Fail(reason: msg); - } - else - { - context.Log.Information("Successfully started transaction. TransactionId: {transactionId}.", - dto.TransactionId); - context.Log.Information("Now sending transactional events. TransactionId: {transactionId}", - dto.TransactionId); - - transactionId = dto.TransactionId; - stage = Stage.Writing; - for (int i = 0; i < eventsCnt; ++i) - { - var writeDto = new TransactionWrite( - transactionId, - new[] { - new NewEvent(Guid.NewGuid().ToByteArray(), - "TakeSomeSpaceEvent", - 0, 0, - Common.Utils.Helper.UTF8NoBom.GetBytes(Guid.NewGuid().ToString()), - Common.Utils.Helper.UTF8NoBom.GetBytes(Guid.NewGuid().ToString())) - }, - false); - var package = new TcpPackage(TcpCommand.TransactionWrite, Guid.NewGuid(), - writeDto.Serialize()); - conn.EnqueueSend(package.AsByteArray()); - } - } - - break; - } - case Stage.Writing: - { - if (pkg.Command != TcpCommand.TransactionWriteCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result != OperationResult.Success) - { - context.Log.Information("Error while writing transactional event: {message} ({e}).", - dto.Message, dto.Result); - var msg = String.Format("Error while writing transactional event: {0} ({1}).", - dto.Message, dto.Result); - context.Fail(reason: msg); - } - else - { - writtenEvents += 1; - if (writtenEvents == eventsCnt) - { - context.Log.Information("Written all events. Committing..."); - - stage = Stage.Committing; - var commitDto = new TransactionCommit(transactionId, false); - var package = new TcpPackage(TcpCommand.TransactionCommit, Guid.NewGuid(), - commitDto.Serialize()); - conn.EnqueueSend(package.AsByteArray()); - } - } - - break; - } - case Stage.Committing: - { - if (pkg.Command != TcpCommand.TransactionCommitCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - sw.Stop(); - - var dto = pkg.Data.Deserialize(); - if (dto.Result != OperationResult.Success) - { - var msg = string.Format("Error while committing transaction: {0} ({1}).", dto.Message, - dto.Result); - context.Log.Information("Error while committing transaction: {message} ({e}).", dto.Message, - dto.Result); - context.Log.Information("Transaction took: {elapsed}.", sw.Elapsed); - context.Fail(reason: msg); - } - else - { - context.Log.Information("Successfully committed transaction [{transactionId}]!", - dto.TransactionId); - context.Log.Information("Transaction took: {elapsed}.", sw.Elapsed); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds)); - context.Success(); - } - - conn.Close(); - break; - } - default: - throw new ArgumentOutOfRangeException(); - } - }, - connectionClosed: (connection, error) => context.Fail(reason: "Connection was closed prematurely.")); - context.WaitForCompletion(); - return true; - } - - private enum Stage - { - AcquiringTransactionId, - Writing, - Committing - } -} diff --git a/src/EventStore.TestClient/Commands/WriteFloodClientApiProcessor.cs b/src/EventStore.TestClient/Commands/WriteFloodClientApiProcessor.cs deleted file mode 100644 index c86c7ee4ab..0000000000 --- a/src/EventStore.TestClient/Commands/WriteFloodClientApiProcessor.cs +++ /dev/null @@ -1,182 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using EventStore.ClientAPI; -using EventStore.Common.Utils; -using ExpectedVersion = EventStore.Core.Data.ExpectedVersion; - -namespace EventStore.TestClient.Commands; - -internal class WriteFloodClientApiProcessor : ICmdProcessor -{ - public string Usage - { - get { return "WRFLCA [ [ []]]"; } - } - - public string Keyword - { - get { return "WRFLCA"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 5000; - int streamsCnt = 1000; - int size = 256; - if (args.Length > 0) - { - if (args.Length < 2 || args.Length > 4) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseLong(args[1]); - if (args.Length >= 3) - { - streamsCnt = MetricPrefixValue.ParseInt(args[2]); - } - - if (args.Length >= 4) - { - size = MetricPrefixValue.ParseInt(args[3]); - } - } - catch - { - return false; - } - } - - WriteFlood(context, clientsCnt, requestsCnt, streamsCnt, size); - return true; - } - - private void WriteFlood(CommandProcessorContext context, int clientsCnt, long requestsCnt, int streamsCnt, - int size) - { - context.IsAsync(); - - var doneEvent = new ManualResetEventSlim(false); - var clients = new List(); - var threads = new List(); - - long succ = 0; - - var streams = Enumerable.Range(0, streamsCnt).Select(x => Guid.NewGuid().ToString()).ToArray(); - var sw2 = new Stopwatch(); - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - var rnd = new Random(); - - var settings = ConnectionSettings.Create() - .UseConsoleLogger() - .PerformOnAnyNode() - .LimitReconnectionsTo(10) - .LimitRetriesForOperationTo(10) - .LimitOperationsQueueTo(10000) - .LimitConcurrentOperationsTo(context._tcpTestClient.Options.WriteWindow / clientsCnt) - .FailOnNoServerResponse(); - - var client = EventStoreConnection.Create(settings, - new Uri($"tcp://{context._tcpTestClient.TcpEndpoint.GetHost()}:{context._tcpTestClient.TcpEndpoint.GetPort()}")); - clients.Add(client); - - threads.Add(new Thread(_ => - { - client.ErrorOccurred += (s, e) => context.Fail(e.Exception, "Error on connection"); - client.ConnectAsync().Wait(); - - for (int j = 0; j < count; ++j) - { - var task = client.AppendToStreamAsync(streams[rnd.Next(streamsCnt)], - ExpectedVersion.Any, - new EventData(Guid.NewGuid(), - "TakeSomeSpaceEvent", - false, - Common.Utils.Helper.UTF8NoBom.GetBytes("DATA" + new string('*', size)), - Common.Utils.Helper.UTF8NoBom.GetBytes("METADATA" + new string('$', 100)))); - task.ContinueWith(x => - { - if (x.IsFaulted) - { - context.Fail(x.Exception.InnerException, "Error on writing operation."); - return; - } - - var localAll = Interlocked.Increment(ref succ); - if (localAll % 1000 == 0) - { - Console.Write('.'); - } - - if (localAll % 100000 == 0) - { - var elapsed = sw2.Elapsed; - sw2.Restart(); - context.Log.Debug("\nDONE TOTAL {writes} WRITES IN {elapsed} ({rate:0.0}/s).", - localAll, - elapsed, - 1000.0 * 100000 / elapsed.TotalMilliseconds); - } - - if (localAll == requestsCnt) - { - context.Success(); - doneEvent.Set(); - } - }); - } - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - sw2.Start(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - clients.ForEach(x => x.Close()); - sw.Stop(); - - context.Log.Information("Completed. Successes: {success}.", succ); - - var reqPerSec = (succ + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", succ, - sw.ElapsedMilliseconds, reqPerSec); - - var fail = requestsCnt - succ; - PerfUtils.LogData( - Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail))); - var failuresRate = (int)(100 * fail / (fail + succ)); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt), failuresRate); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-c{1}-r{2}-st{3}-s{4}-reqPerSec", Keyword, clientsCnt, requestsCnt, streamsCnt, size), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-c{1}-r{2}-st{3}-s{4}-failureSuccessRate", Keyword, clientsCnt, requestsCnt, - streamsCnt, size), failuresRate); - - if (Interlocked.Read(ref succ) != requestsCnt) - { - context.Fail(reason: "There were errors or not all requests completed."); - } - else - { - context.Success(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/WriteFloodProcessor.cs b/src/EventStore.TestClient/Commands/WriteFloodProcessor.cs deleted file mode 100644 index fdb5ea482d..0000000000 --- a/src/EventStore.TestClient/Commands/WriteFloodProcessor.cs +++ /dev/null @@ -1,264 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.TestClient.Statistics; -using EventStore.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class WriteFloodProcessor : ICmdProcessor -{ - public string Usage - { - // 0 1 2 3 4 5 - get { return "WRFL [ [ [ [ []]]]]"; } - } - - public string Keyword - { - get { return "WRFL"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - long requestsCnt = 5000; - int streamsCnt = 1000; - int size = 256; - int batchSize = 1; - string streamNamePrefix = string.Empty; - if (args.Length > 0) - { - if (args.Length < 2 || args.Length > 6) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseLong(args[1]); - if (args.Length >= 3) - { - streamsCnt = MetricPrefixValue.ParseInt(args[2]); - } - - if (args.Length >= 4) - { - size = MetricPrefixValue.ParseInt(args[3]); - } - - if (args.Length >= 5) - { - batchSize = MetricPrefixValue.ParseInt(args[4]); - } - - if (args.Length >= 6) - { - streamNamePrefix = args[5]; - } - } - catch - { - return false; - } - } - - var stats = new WriteFloodStats(Keyword, context.OutputCsv, args); - var monitor = new RequestMonitor(); - WriteFlood(context, stats, clientsCnt, requestsCnt, streamsCnt, size, batchSize, streamNamePrefix, monitor); - return true; - } - - private void WriteFlood(CommandProcessorContext context, WriteFloodStats stats, int clientsCnt, long requestsCnt, int streamsCnt, - int size, int batchSize, string streamNamePrefix, RequestMonitor monitor) - { - context.IsAsync(); - - var doneEvent = new ManualResetEventSlim(false); - var clients = new List>(); - var threads = new List(); - - long last = 0; - - string[] streams = Enumerable.Range(0, streamsCnt).Select(x => - string.IsNullOrWhiteSpace(streamNamePrefix) - ? Guid.NewGuid().ToString() - : $"{streamNamePrefix}-{x}" - ).ToArray(); - - context.Log.Information("Writing streams randomly between {first} and {last}", - streams.FirstOrDefault(), - streams.LastOrDefault()); - - Console.WriteLine($"Last stream: {streams.LastOrDefault()}"); - stats.StartTime = DateTime.UtcNow; - var sw2 = new Stopwatch(); - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - long sent = 0; - long received = 0; - var rnd = new Random(); - var client = context._tcpTestClient.CreateTcpConnection( - context, - (conn, pkg) => - { - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - monitor.EndOperation(pkg.CorrelationId); - switch (dto.Result) - { - case OperationResult.Success: - Interlocked.Add(ref stats.Succ, batchSize); - if (stats.Succ - last > 1000) - { - last = stats.Succ; - Console.Write("."); - } - - break; - case OperationResult.PrepareTimeout: - Interlocked.Increment(ref stats.PrepTimeout); - break; - case OperationResult.CommitTimeout: - Interlocked.Increment(ref stats.CommitTimeout); - break; - case OperationResult.ForwardTimeout: - Interlocked.Increment(ref stats.ForwardTimeout); - break; - case OperationResult.WrongExpectedVersion: - Interlocked.Increment(ref stats.WrongExpVersion); - break; - case OperationResult.StreamDeleted: - Interlocked.Increment(ref stats.StreamDeleted); - break; - default: - throw new ArgumentOutOfRangeException(); - } - - if (dto.Result != OperationResult.Success) - { - if (Interlocked.Increment(ref stats.Fail) % 1000 == 0) - { - Console.Write('#'); - } - } - - Interlocked.Increment(ref received); - var localAll = Interlocked.Add(ref stats.All, batchSize); - if (localAll % 100000 == 0) - { - stats.Elapsed = sw2.Elapsed; - stats.Rate = 1000.0 * 100000 / stats.Elapsed.TotalMilliseconds; - sw2.Restart(); - context.Log.Debug( - "\nDONE TOTAL {writes} WRITES IN {elapsed} ({rate:0.0}/s) [S:{success}, F:{failures} (WEV:{wrongExpectedVersion}, " + - "P:{prepareTimeout}, C:{commitTimeout}, F:{forwardTimeout}, D:{streamDeleted})].", - localAll, stats.Elapsed, stats.Rate, stats.Succ, stats.Fail, - stats.WrongExpVersion, stats.PrepTimeout, stats.CommitTimeout, stats.ForwardTimeout, stats.StreamDeleted); - stats.WriteStatsToFile(context.StatsLogger); - } - - if (localAll >= requestsCnt) - { - context.Success(); - doneEvent.Set(); - } - }, - connectionClosed: (conn, err) => context.Fail(reason: "Connection was closed prematurely.")); - clients.Add(client); - - threads.Add(new Thread(() => - { - for (int j = 0; j < count; ++j) - { - var events = new NewEvent[batchSize]; - for (int q = 0; q < batchSize; q++) - { - events[q] = new NewEvent(Guid.NewGuid().ToByteArray(), - "TakeSomeSpaceEvent", - 1, 0, - Common.Utils.Helper.UTF8NoBom.GetBytes( - "{ \"DATA\" : \"" + new string('*', size) + "\"}"), - Common.Utils.Helper.UTF8NoBom.GetBytes( - "{ \"METADATA\" : \"" + new string('$', 100) + "\"}")); - } - - var corrid = Guid.NewGuid(); - var write = new WriteEvents( - streams[rnd.Next(streamsCnt)], - ExpectedVersion.Any, - events, - false); - var package = new TcpPackage(TcpCommand.WriteEvents, corrid, write.Serialize()); - monitor.StartOperation(corrid); - client.EnqueueSend(package.AsByteArray()); - - var localSent = Interlocked.Increment(ref sent); - while (localSent - Interlocked.Read(ref received) > - context._tcpTestClient.Options.WriteWindow / clientsCnt) - { - Thread.Sleep(1); - } - } - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - sw2.Start(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - sw.Stop(); - clients.ForEach(client => client.Close()); - - stats.WriteStatsToFile(context.StatsLogger); - context.Log.Information( - "Completed. Successes: {success}, failures: {failures} (WRONG VERSION: {wrongExpectedVersion}, P: {prepareTimeout}, " + - "C: {commitTimeout}, F: {forwardTimeout}, D: {streamDeleted})", - stats.Succ, stats.Fail, stats.WrongExpVersion, stats.PrepTimeout, stats.CommitTimeout, stats.ForwardTimeout, stats.StreamDeleted); - - var reqPerSec = (stats.All + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", stats.All, - sw.ElapsedMilliseconds, reqPerSec); - - PerfUtils.LogData( - Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("successes", stats.Succ), PerfUtils.Col("failures", stats.Fail))); - - var failuresRate = (int)(100 * stats.Fail / (stats.Fail + stats.Succ)); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt), failuresRate); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-c{1}-r{2}-st{3}-s{4}-reqPerSec", Keyword, clientsCnt, requestsCnt, streamsCnt, size), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-c{1}-r{2}-st{3}-s{4}-failureSuccessRate", Keyword, clientsCnt, requestsCnt, - streamsCnt, size), failuresRate); - monitor.GetMeasurementDetails(); - if (Interlocked.Read(ref stats.Succ) != requestsCnt) - { - context.Fail(reason: "There were errors or not all requests completed."); - } - else - { - context.Success(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/WriteFloodWaitingProcessor.cs b/src/EventStore.TestClient/Commands/WriteFloodWaitingProcessor.cs deleted file mode 100644 index 88e9859429..0000000000 --- a/src/EventStore.TestClient/Commands/WriteFloodWaitingProcessor.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class WriteFloodWaitingProcessor : ICmdProcessor -{ - public string Usage - { - get { return "WRFLW [ [payload-size]]"; } - } - - public string Keyword - { - get { return "WRFLW"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - int requestsCnt = 5000; - int payloadSize = 256 + 100; - if (args.Length > 0) - { - if (args.Length > 3 || args.Length < 2) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - requestsCnt = MetricPrefixValue.ParseInt(args[1]); - if (args.Length == 3) - { - payloadSize = MetricPrefixValue.ParseInt(args[2]); - } - } - catch - { - return false; - } - } - - WriteFlood(context, clientsCnt, requestsCnt, payloadSize); - return true; - } - - private void WriteFlood(CommandProcessorContext context, int clientsCnt, int requestsCnt, int payloadSize) - { - context.IsAsync(); - - var dataSize = Math.Max(0, payloadSize - 100); - var metadataSize = Math.Min(100, payloadSize); - - var clients = new List>(); - var threads = new List(); - var doneEvent = new ManualResetEventSlim(false); - var succ = 0; - var fail = 0; - var all = 0; - for (int i = 0; i < clientsCnt; i++) - { - var count = requestsCnt / clientsCnt + ((i == clientsCnt - 1) ? requestsCnt % clientsCnt : 0); - var autoEvent = new AutoResetEvent(false); - var eventStreamId = "es" + Guid.NewGuid(); - var received = 0; - var client = context._tcpTestClient.CreateTcpConnection( - context, - (conn, pkg) => - { - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - Interlocked.Increment(ref received); - var dto = pkg.Data.Deserialize(); - if (dto.Result == OperationResult.Success) - { - if (Interlocked.Increment(ref succ) % 1000 == 0) - { - Console.Write("."); - } - } - else - { - if (Interlocked.Increment(ref fail) % 1000 == 0) - { - Console.Write("#"); - } - } - - if (Interlocked.Increment(ref all) == requestsCnt) - { - doneEvent.Set(); - } - - autoEvent.Set(); - }, - connectionClosed: (conn, err) => context.Fail(reason: "Connection was closed prematurely.")); - clients.Add(client); - - threads.Add(new Thread(() => - { - for (int j = 0; j < count; ++j) - { - var write = new WriteEvents( - eventStreamId, - ExpectedVersion.Any, - new[] { - new NewEvent(Guid.NewGuid().ToByteArray(), - "TakeSomeSpaceEvent", - 0, 0, - Common.Utils.Helper.UTF8NoBom.GetBytes("DATA" + new string('*', dataSize)), - Common.Utils.Helper.UTF8NoBom.GetBytes("METADATA" + new string('$', metadataSize))) - }, - false); - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), write.Serialize()); - client.EnqueueSend(package.AsByteArray()); - autoEvent.WaitOne(); - } - }) - { IsBackground = true }); - } - - var sw = Stopwatch.StartNew(); - threads.ForEach(thread => thread.Start()); - doneEvent.Wait(); - sw.Stop(); - clients.ForEach(client => client.Close()); - - var reqPerSec = (all + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("Completed. Successes: {success}, failures: {failures}", succ, fail); - context.Log.Information( - "{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec, latency: {latency:0.00} ms).", - all, sw.ElapsedMilliseconds, reqPerSec, (sw.Elapsed.TotalMilliseconds + 0.0) / requestsCnt); - PerfUtils.LogData(Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt), - (int)(100.0 * fail / (fail + succ))); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds / requestsCnt)); - - if (succ != requestsCnt) - { - context.Fail(reason: "There were errors or not all requests completed."); - } - else - { - context.Success(); - } - } -} diff --git a/src/EventStore.TestClient/Commands/WriteJsonProcessor.cs b/src/EventStore.TestClient/Commands/WriteJsonProcessor.cs deleted file mode 100644 index 750cf8a8c0..0000000000 --- a/src/EventStore.TestClient/Commands/WriteJsonProcessor.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Diagnostics; -using System.Net.Sockets; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; - - -namespace EventStore.TestClient.Commands; - -internal class WriteJsonProcessor : ICmdProcessor -{ - public string Usage - { - get { return "WRJ [ []]"; } - } - - public string Keyword - { - get { return "WRJ"; } - } - - private readonly Random _random = new Random(); - - public bool Execute(CommandProcessorContext context, string[] args) - { - var eventStreamId = "test-stream"; - var expectedVersion = ExpectedVersion.Any; - var data = GenerateTestData(); - string metadata = null; - - if (args.Length > 0) - { - if (args.Length < 3 || args.Length > 4) - { - return false; - } - - eventStreamId = args[0]; - expectedVersion = args[1].ToUpper() == "ANY" ? ExpectedVersion.Any : int.Parse(args[1]); - data = args[2]; - if (args.Length == 4) - { - metadata = args[3]; - } - } - - context.IsAsync(); - var writeDto = new WriteEvents( - eventStreamId, - expectedVersion, - new[] { - new NewEvent(Guid.NewGuid().ToByteArray(), - "JsonDataEvent", - 1, 0, - Helper.UTF8NoBom.GetBytes(data), - Helper.UTF8NoBom.GetBytes(metadata ?? string.Empty)) - }, - false); - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), writeDto.Serialize()); - - var sw = new Stopwatch(); - bool dataReceived = false; - - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information("[{remoteEndPoint}, L{localEndPoint}]: Writing...", conn.RemoteEndPoint, - conn.LocalEndPoint); - sw.Start(); - conn.EnqueueSend(package.AsByteArray()); - }, - handlePackage: (conn, pkg) => - { - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - dataReceived = true; - sw.Stop(); - - var dto = pkg.Data.Deserialize(); - if (dto.Result == OperationResult.Success) - { - context.Log.Information("Successfully written. EventId: {correlationId}.", package.CorrelationId); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds)); - } - else - { - context.Log.Information("Error while writing: {message} ({e}).", dto.Message, dto.Result); - } - - context.Log.Information("Write request took: {elapsed}.", sw.Elapsed); - conn.Close(); - context.Success(); - }, - connectionClosed: (connection, error) => - { - if (dataReceived && error == SocketError.Success) - { - context.Success(); - } - else - { - context.Fail(); - } - }); - - context.WaitForCompletion(); - return true; - } - - private string GenerateTestData() - { - return TestClientJson.To(new TestData(Guid.NewGuid().ToString(), _random.Next(1, 101))); - } -} - -internal class TestData -{ - public string Name { get; set; } - public int Version { get; set; } - - public TestData() - { - } - - public TestData(string name, int version) - { - Name = name; - Version = version; - } -} diff --git a/src/EventStore.TestClient/Commands/WriteLongTermProcessor.cs b/src/EventStore.TestClient/Commands/WriteLongTermProcessor.cs deleted file mode 100644 index 559f865ab5..0000000000 --- a/src/EventStore.TestClient/Commands/WriteLongTermProcessor.cs +++ /dev/null @@ -1,253 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class WriteLongTermProcessor : ICmdProcessor -{ - public string Usage - { - get - { - return - "WRLT []"; - } - } - - public string Keyword - { - get { return "WRLT"; } - } - - private readonly object _randomLockRoot = new object(); - private readonly Random _random = new Random(); - - public bool Execute(CommandProcessorContext context, string[] args) - { - int clientsCnt = 1; - int minPerSecond = 1; - int maxPerSecond = 2; - int runTimeMinutes = 1; - string eventStreamId = null; - - if (args.Length > 0) - { - if (args.Length != 4 && args.Length != 5) - { - return false; - } - - try - { - clientsCnt = MetricPrefixValue.ParseInt(args[0]); - minPerSecond = MetricPrefixValue.ParseInt(args[1]); - maxPerSecond = MetricPrefixValue.ParseInt(args[2]); - runTimeMinutes = MetricPrefixValue.ParseInt(args[3]); - if (args.Length == 5) - { - eventStreamId = args[4]; - } - } - catch - { - return false; - } - } - - Flood(context, eventStreamId, clientsCnt, minPerSecond, maxPerSecond, runTimeMinutes); - return true; - } - - private void Flood(CommandProcessorContext context, - string eventStreamId, - int clientsCnt, - int minPerSecond, - int maxPerSecond, - int runTimeMinutes) - { - context.IsAsync(); - - var clients = new List>(); - var threads = new List(); - var doneEvent = new ManualResetEvent(false); - var done = false; - - var succ = 0; - var fail = 0; - - var requestsCnt = 0; - - int sent = 0; - int received = 0; - - var watchLockRoot = new object(); - var sw = Stopwatch.StartNew(); - for (int i = 0; i < clientsCnt; i++) - { - var esId = eventStreamId ?? "Stream-" + Thread.CurrentThread.ManagedThreadId % 3; - - var client = context._tcpTestClient.CreateTcpConnection( - context, - (conn, pkg) => - { - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result == OperationResult.Success) - { - var succDone = Interlocked.Increment(ref succ); - if (succDone % maxPerSecond == 0) - { - Console.Write("."); - } - - Interlocked.Increment(ref requestsCnt); - } - else - { - Interlocked.Increment(ref fail); - } - - Interlocked.Increment(ref received); - }, - connectionClosed: (conn, err) => - { - if (!done) - { - context.Fail(reason: "Socket was closed, but not all requests were completed."); - } - else - { - context.Success(); - } - }); - clients.Add(client); - - threads.Add(new Thread(() => - { - var sentCount = 0; - var sleepTime = 0; - - var dataSizeCoefficient = 1; - var currentMinute = -1; - - while (true) - { - TimeSpan elapsed; - lock (watchLockRoot) - { - elapsed = sw.Elapsed; - } - - if (elapsed.TotalMinutes > runTimeMinutes) - { - done = true; - doneEvent.Set(); - break; - } - - if (sentCount == 0) - { - int elapsedMinutesInt = (int)elapsed.TotalMinutes; - lock (_randomLockRoot) - { - sentCount = minPerSecond == maxPerSecond - ? maxPerSecond - : _random.Next(minPerSecond, maxPerSecond); - dataSizeCoefficient = _random.Next(8, 256); - } - - if (currentMinute != elapsedMinutesInt) - { - currentMinute = elapsedMinutesInt; - context.Log.Information( - "\nElapsed {elapsed} of {runTime} minutes, sent {sent}; next block coef. {dataSizeCoefficient}", - elapsedMinutesInt, - runTimeMinutes, - sent, - dataSizeCoefficient); - } - - sleepTime = 1000 / sentCount; - } - - var dataSize = dataSizeCoefficient * 8; - var write = new WriteEvents( - esId, - ExpectedVersion.Any, - new[] { - new NewEvent( - Guid.NewGuid().ToByteArray(), - "TakeSomeSpaceEvent", - 0, 0, - Helper.UTF8NoBom.GetBytes( - "DATA" + dataSize.ToString(" 00000 ") + new string('*', dataSize)), - Helper.UTF8NoBom.GetBytes("METADATA" + new string('$', 100))) - }, - false); - var package = new TcpPackage(TcpCommand.WriteEvents, Guid.NewGuid(), write.Serialize()); - client.EnqueueSend(package.AsByteArray()); - - Interlocked.Increment(ref sent); - - Thread.Sleep(sleepTime); - sentCount -= 1; - - while (sent - received > context._tcpTestClient.Options.WriteWindow / clientsCnt) - { - Thread.Sleep(1); - } - } - })); - } - - foreach (var thread in threads) - { - thread.IsBackground = true; - thread.Start(); - } - - doneEvent.WaitOne(); - sw.Stop(); - - foreach (var client in clients) - { - client.Close(); - } - - context.Log.Information("Completed. Successes: {success}, failures: {failures}", succ, fail); - var reqPerSec = (requestsCnt + 0.0) / sw.ElapsedMilliseconds * 1000; - context.Log.Information("{requests} requests completed in {elapsed}ms ({rate:0.00} reqs per sec).", - requestsCnt, - sw.ElapsedMilliseconds, - reqPerSec); - - PerfUtils.LogData( - Keyword, - PerfUtils.Row(PerfUtils.Col("clientsCnt", clientsCnt), - PerfUtils.Col("requestsCnt", requestsCnt), - PerfUtils.Col("ElapsedMilliseconds", sw.ElapsedMilliseconds)), - PerfUtils.Row(PerfUtils.Col("successes", succ), PerfUtils.Col("failures", fail)) - ); - - PerfUtils.LogTeamCityGraphData(string.Format("{0}-{1}-{2}-reqPerSec", Keyword, clientsCnt, requestsCnt), - (int)reqPerSec); - - PerfUtils.LogTeamCityGraphData( - string.Format("{0}-{1}-{2}-failureSuccessRate", Keyword, clientsCnt, requestsCnt), - 100 * fail / (fail + succ)); - - context.Success(); - } -} diff --git a/src/EventStore.TestClient/Commands/WriteProcessor.cs b/src/EventStore.TestClient/Commands/WriteProcessor.cs deleted file mode 100644 index 213b3a5b2b..0000000000 --- a/src/EventStore.TestClient/Commands/WriteProcessor.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Diagnostics; -using EventStore.Client.Messages; -using EventStore.Common.Utils; -using EventStore.Core.Data; -using EventStore.Core.Services.Transport.Tcp; - -namespace EventStore.TestClient.Commands; - -internal class WriteProcessor : ICmdProcessor -{ - public string Usage - { - get { return "WR [ [ [ [ ]]]"; } - } - - public string Keyword - { - get { return "WR"; } - } - - public bool Execute(CommandProcessorContext context, string[] args) - { - var eventStreamId = "test-stream"; - var expectedVersion = ExpectedVersion.Any; - var data = "test-data"; - string metadata = null; - bool isJson = false; - string login = null; - string pass = null; - - if (args.Length > 0) - { - if (args.Length < 3 || args.Length > 7 || args.Length == 6) - { - return false; - } - - eventStreamId = args[0]; - expectedVersion = args[1].ToUpper() == "ANY" ? ExpectedVersion.Any : int.Parse(args[1]); - data = args[2]; - if (args.Length >= 4) - { - metadata = args[3]; - } - - if (args.Length >= 5) - { - isJson = bool.Parse(args[4]); - } - - if (args.Length >= 7) - { - login = args[5]; - pass = args[6]; - } - } - - context.IsAsync(); - var sw = new Stopwatch(); - context._tcpTestClient.CreateTcpConnection( - context, - connectionEstablished: conn => - { - context.Log.Information("[{remoteEndPoint}, L{localEndPoint}]: Writing...", conn.RemoteEndPoint, - conn.LocalEndPoint); - var writeDto = new WriteEvents( - eventStreamId, - expectedVersion, - new[] { - new NewEvent(Guid.NewGuid().ToByteArray(), - "TakeSomeSpaceEvent", - isJson ? 1 : 0, 0, - Helper.UTF8NoBom.GetBytes(data), - Helper.UTF8NoBom.GetBytes(metadata ?? string.Empty)) - }, - false); - var package = new TcpPackage(TcpCommand.WriteEvents, - login == null ? TcpFlags.None : TcpFlags.Authenticated, - Guid.NewGuid(), - login, - pass, - writeDto.Serialize()).AsByteArray(); - sw.Start(); - conn.EnqueueSend(package); - }, - handlePackage: (conn, pkg) => - { - sw.Stop(); - context.Log.Information("Write request took: {elapsed}.", sw.Elapsed); - - if (pkg.Command != TcpCommand.WriteEventsCompleted) - { - context.Fail(reason: string.Format("Unexpected TCP package: {0}.", pkg.Command)); - return; - } - - var dto = pkg.Data.Deserialize(); - if (dto.Result == EventStore.Client.Messages.OperationResult.Success) - { - context.Log.Information("Successfully written."); - PerfUtils.LogTeamCityGraphData(string.Format("{0}-latency-ms", Keyword), - (int)Math.Round(sw.Elapsed.TotalMilliseconds)); - context.Success(); - } - else - { - context.Log.Information("Error while writing: {message} ({e}).", dto.Message, dto.Result); - context.Fail(); - } - - conn.Close(); - }, - connectionClosed: (connection, error) => context.Fail(reason: "Connection was closed prematurely.")); - - context.WaitForCompletion(); - return true; - } -} diff --git a/src/EventStore.TestClient/ConsoleTable.cs b/src/EventStore.TestClient/ConsoleTable.cs deleted file mode 100644 index 3bcc986e46..0000000000 --- a/src/EventStore.TestClient/ConsoleTable.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using EventStore.Common.Utils; -#pragma warning disable 1591 - -namespace EventStore.TestClient; - -public class ConsoleTable -{ - private readonly string[] _header; - private readonly int[] _columnWidths; - private readonly List _rows; - - public ConsoleTable(params string[] header) - { - Ensure.NotNull(header, "header"); - - _header = header; - _columnWidths = _header.Select(s => s.Length + 4).ToArray(); - _rows = new List(); - - AppendRow(_header); - } - - public string CreateIndentedTable() - { - return string.Format("{0}{1}{0}", Environment.NewLine, CreateTable()); - } - - public string CreateTable() - { - var lineSeparator = RowSeparator(_columnWidths.Sum() + _columnWidths.Length + 1); - - var table = new StringBuilder(); - foreach (var row in _rows) - { - table.AppendLine(lineSeparator); - table.AppendLine(row); - } - - table.Append(lineSeparator); - - return table.ToString(); - } - - private string RowSeparator(int width) - { - return new string('-', width); - } - - public void AppendRow(params string[] cells) - { - if (cells == null || cells.Length != _header.Length) - { - return; - } - - var row = new StringBuilder(); - for (int i = 0; i < cells.Length; i++) - { - var format = "|{0," + _columnWidths[i] + "}"; - row.AppendFormat(format, cells[i]); - } - - row.Append("|"); - - _rows.Add(row.ToString()); - } -} diff --git a/src/EventStore.TestClient/EventStore.TestClient.csproj b/src/EventStore.TestClient/EventStore.TestClient.csproj index 57f7843616..b9ab45a52e 100644 --- a/src/EventStore.TestClient/EventStore.TestClient.csproj +++ b/src/EventStore.TestClient/EventStore.TestClient.csproj @@ -5,8 +5,8 @@ false - + @@ -18,15 +18,17 @@ - - logconfig.json PreserveNewest + + version.properties + PreserveNewest + diff --git a/src/EventStore.TestClient/GrpcCommands/CompatibilityProcessors.cs b/src/EventStore.TestClient/GrpcCommands/CompatibilityProcessors.cs new file mode 100644 index 0000000000..8c36567e82 --- /dev/null +++ b/src/EventStore.TestClient/GrpcCommands/CompatibilityProcessors.cs @@ -0,0 +1,628 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using EventStore.Client; +using EventStore.TestClient.Commands; + +namespace EventStore.TestClient.GrpcCommands; + +internal sealed class CompatibilityProcessor : ICmdProcessor +{ + private static readonly UTF8Encoding Utf8NoBom = new(false); + private readonly Func _execute; + private readonly Func _validate; + + public CompatibilityProcessor( + string keyword, + string usage, + Func execute, + Func validate = null) + { + Keyword = keyword; + Usage = usage; + _execute = execute; + _validate = validate ?? (_ => true); + } + + public string Keyword { get; } + public string Usage { get; } + + public bool Execute(CommandProcessorContext context, string[] args) + { + if (!_validate(args)) + { + return false; + } + + context.IsAsync(); + _ = Complete(context, args); + return true; + } + + private async Task Complete(CommandProcessorContext context, string[] args) + { + try + { + await _execute(context, args); + context.Success(); + } + catch (OperationCanceledException) when (context.CancellationToken.IsCancellationRequested) + { + context.Success(); + } + catch (Exception ex) + { + context.Fail(ex); + } + } + + public static IEnumerable CreateSupportedProcessors() + { + yield return new CompatibilityProcessor("PING", "PING", Ping, args => args.Length == 0); + yield return new CompatibilityProcessor("PINGFL", "PINGFL [ ]", PingFlood, + args => args.Length == 0 || args.Length == 2); + yield return new CompatibilityProcessor("PINGFLW", "PINGFLW [ ]", PingFloodWaiting, + args => args.Length == 0 || args.Length == 2); + yield return new CompatibilityProcessor("WR", "WR [ [ [ [ ]]]", Write, + args => args.Length == 0 || args.Length is >= 3 and <= 7 && args.Length != 6); + yield return new CompatibilityProcessor("WRJ", "WRJ [ []]", WriteJson, + args => args.Length == 0 || args.Length is >= 3 and <= 4); + yield return new CompatibilityProcessor("MWR", "MWR [ [ []]]", MultiWrite, + args => args.Length <= 3); + yield return new CompatibilityProcessor("WRFLW", "WRFLW [ [payload-size]]", WriteFloodWaiting, + args => args.Length == 0 || args.Length is 2 or 3); + yield return new CompatibilityProcessor("MWRFLW", "MWRFLW [ [ ]]", MultiWriteFloodWaiting, + args => args.Length is 0 or 1 or 3); + yield return new CompatibilityProcessor("TWR", "TWR [ [ []]]", TransactionWrite, + args => args.Length <= 3); + yield return new CompatibilityProcessor("DEL", "DEL [ []]", Delete, + args => args.Length <= 2); + yield return new CompatibilityProcessor("RD", "RD [ [ []]]", Read, + args => args.Length <= 3); + yield return new CompatibilityProcessor("RDFL", "RDFL [ [ [ []]]]", ReadFlood, + args => args.Length == 0 || args.Length is >= 2 and <= 5); + yield return new CompatibilityProcessor("RDALL", "RDALL [[F|B] [ []]]", ReadAll, + args => args.Length is 0 or 1 or 3 or 4); + yield return new CompatibilityProcessor("WRLT", "WRLT []", WriteLongTerm, + args => args.Length == 0 || args.Length is 4 or 5); + yield return new CompatibilityProcessor("SUBSCR", "SUBSCR [ ... ]", Subscribe, + args => true); + yield return new CompatibilityProcessor("SST", "SST []", SubscriptionStress, + args => args.Length <= 1); + yield return new CompatibilityProcessor("SCAVENGE", "SCAVENGE", Scavenge, + args => args.Length == 0); + yield return new CompatibilityProcessor("VERIFY", "VERIFY ", Verify, + args => args.Length == 0 || args.Length == 5); + yield return new CompatibilityProcessor("CHKGRPC", "CHKGRPC", CheckGrpcSanitization, + args => args.Length == 0); + } + + private static EventData Event(string data = "test-data", string metadata = "", bool isJson = false) => + new(Uuid.FromGuid(Guid.NewGuid()), "TakeSomeSpaceEvent", Utf8NoBom.GetBytes(data), Utf8NoBom.GetBytes(metadata), + isJson ? "application/json" : "application/octet-stream"); + + private static async Task Append( + EventStoreClient client, + string stream, + ExpectedRevision expected, + IEnumerable events, + UserCredentials credentials, + CancellationToken cancellationToken) + { + if (expected.Revision is { } revision) + { + await client.AppendToStreamAsync(stream, revision, events, userCredentials: credentials, cancellationToken: cancellationToken); + } + else + { + await client.AppendToStreamAsync(stream, expected.State, events, userCredentials: credentials, cancellationToken: cancellationToken); + } + } + + private static async Task Ping(CommandProcessorContext context, string[] args) + { + var client = context._grpcTestClient.CreateGrpcClient(); + await Ping(client, context.CancellationToken); + } + + private static async Task Ping(EventStoreClient client, CancellationToken cancellationToken) + { + var read = client.ReadStreamAsync(Direction.Forwards, $"$test-client-ping-{Guid.NewGuid():N}", StreamPosition.Start, + maxCount: 1, cancellationToken: cancellationToken); + await read.ReadState; + } + + private static async Task PingFlood(CommandProcessorContext context, string[] args) + { + var clients = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[0]); + var messages = args.Length == 0 ? 5000 : MetricPrefixValue.ParseInt(args[1]); + await Task.WhenAll(Enumerable.Range(0, clients).Select(_ => + { + var client = context._grpcTestClient.CreateGrpcClient(); + return Task.WhenAll(Enumerable.Range(0, messages) + .Select(_ => Ping(client, context.CancellationToken))); + })); + } + + private static async Task PingFloodWaiting(CommandProcessorContext context, string[] args) + { + var clients = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[0]); + var messages = args.Length == 0 ? 5000 : MetricPrefixValue.ParseInt(args[1]); + await Task.WhenAll(Enumerable.Range(0, clients).Select(async _ => + { + var client = context._grpcTestClient.CreateGrpcClient(); + for (var i = 0; i < messages; i++) + { + await Ping(client, context.CancellationToken); + } + })); + } + + private static async Task Write(CommandProcessorContext context, string[] args) + { + var stream = args.Length == 0 ? "test-stream" : args[0]; + var expected = ExpectedRevision.Parse(args.Length == 0 ? "ANY" : args[1]); + var data = args.Length == 0 ? "test-data" : args[2]; + var metadata = args.Length >= 4 ? args[3] : ""; + var isJson = args.Length >= 5 && bool.Parse(args[4]); + var credentials = args.Length == 7 ? new UserCredentials(args[5], args[6]) : null; + await Append(context._grpcTestClient.CreateGrpcClient(), stream, expected, [Event(data, metadata, isJson)], + credentials, context.CancellationToken); + } + + private static Task WriteJson(CommandProcessorContext context, string[] args) + { + if (args.Length == 0) + { + return Write(context, ["test-stream", "ANY", "{\"value\":\"test\"}", "", "true"]); + } + + var forwarded = args.Concat(["true"]).ToArray(); + if (args.Length == 3) + { + forwarded = [args[0], args[1], args[2], "", "true"]; + } + + return Write(context, forwarded); + } + + private static async Task MultiWrite(CommandProcessorContext context, string[] args) + { + var count = args.Length == 0 ? 10 : MetricPrefixValue.ParseInt(args[0]); + var stream = args.Length >= 2 ? args[1] : "test-stream"; + var expected = ExpectedRevision.Parse(args.Length >= 3 ? args[2] : "ANY"); + await Append(context._grpcTestClient.CreateGrpcClient(), stream, expected, + Enumerable.Range(0, count).Select(i => Event($"event-{i}")), null, context.CancellationToken); + } + + private static Task TransactionWrite(CommandProcessorContext context, string[] args) + { + var stream = args.Length >= 1 ? args[0] : "test-stream"; + var expected = args.Length >= 2 ? args[1] : "ANY"; + var count = args.Length >= 3 ? args[2] : "10"; + return MultiWrite(context, [count, stream, expected]); + } + + private static async Task WriteFloodWaiting(CommandProcessorContext context, string[] args) + { + var clients = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[0]); + var requests = args.Length == 0 ? 5000 : MetricPrefixValue.ParseInt(args[1]); + var payloadSize = args.Length == 3 ? MetricPrefixValue.ParseInt(args[2]) : 1024; + var data = new string('*', Math.Max(0, payloadSize)); + await Task.WhenAll(Enumerable.Range(0, clients).Select(async clientIndex => + { + var client = context._grpcTestClient.CreateGrpcClient(); + var stream = $"write-flood-waiting-{clientIndex}-{Guid.NewGuid():N}"; + for (var i = clientIndex; i < requests; i += clients) + { + await client.AppendToStreamAsync(stream, StreamState.Any, [Event(data)], + cancellationToken: context.CancellationToken); + } + })); + } + + private static async Task MultiWriteFloodWaiting(CommandProcessorContext context, string[] args) + { + var eventCount = args.Length == 0 ? 10 : MetricPrefixValue.ParseInt(args[0]); + var clients = args.Length == 3 ? MetricPrefixValue.ParseInt(args[1]) : 1; + var requests = args.Length == 3 ? MetricPrefixValue.ParseInt(args[2]) : 5000; + await Task.WhenAll(Enumerable.Range(0, clients).Select(async clientIndex => + { + var client = context._grpcTestClient.CreateGrpcClient(); + var stream = $"multi-write-flood-waiting-{clientIndex}-{Guid.NewGuid():N}"; + for (var i = clientIndex; i < requests; i += clients) + { + await client.AppendToStreamAsync(stream, StreamState.Any, + Enumerable.Range(0, eventCount).Select(eventIndex => Event($"event-{eventIndex}")), + cancellationToken: context.CancellationToken); + } + })); + } + + private static async Task Delete(CommandProcessorContext context, string[] args) + { + var stream = args.Length >= 1 ? args[0] : "test-stream"; + var expected = ExpectedRevision.Parse(args.Length >= 2 ? args[1] : "ANY"); + var client = context._grpcTestClient.CreateGrpcClient(); + if (expected.Revision is { } revision) + { + await client.DeleteAsync(stream, revision, cancellationToken: context.CancellationToken); + } + else + { + await client.DeleteAsync(stream, expected.State, cancellationToken: context.CancellationToken); + } + } + + private static async Task Read(CommandProcessorContext context, string[] args) + { + var stream = args.Length >= 1 ? args[0] : "test-stream"; + var start = args.Length >= 2 ? StreamPosition.FromInt64(MetricPrefixValue.ParseLong(args[1])) : StreamPosition.Start; + var read = context._grpcTestClient.CreateGrpcClient().ReadStreamAsync(Direction.Forwards, stream, start, + maxCount: 1, cancellationToken: context.CancellationToken); + await foreach (var message in read.Messages.WithCancellation(context.CancellationToken)) + { + context.Log.Information("Read {messageType} from {stream}", message.GetType().Name, stream); + } + } + + private static async Task ReadFlood(CommandProcessorContext context, string[] args) + { + var clients = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[0]); + var requests = args.Length == 0 ? 5000 : MetricPrefixValue.ParseInt(args[1]); + var streams = args.Length >= 3 ? MetricPrefixValue.ParseInt(args[2]) : 1000; + var prefix = args.Length >= 4 ? args[3] : "test-stream"; + await Task.WhenAll(Enumerable.Range(0, clients).Select(async clientIndex => + { + var client = context._grpcTestClient.CreateGrpcClient(); + for (var i = clientIndex; i < requests; i += clients) + { + var read = client.ReadStreamAsync(Direction.Forwards, $"{prefix}-{i % streams}", StreamPosition.Start, + maxCount: 1, cancellationToken: context.CancellationToken); + await read.ReadState; + } + })); + } + + private static async Task ReadAll(CommandProcessorContext context, string[] args) + { + var forwards = args.Length == 0 || args[0].Equals("F", StringComparison.OrdinalIgnoreCase); + var position = forwards ? Position.Start : Position.End; + if (args.Length >= 3) + { + position = new Position(ulong.Parse(args[1]), ulong.Parse(args[2])); + } + + var read = context._grpcTestClient.CreateGrpcClient().ReadAllAsync( + forwards ? Direction.Forwards : Direction.Backwards, + position, + cancellationToken: context.CancellationToken); + await foreach (var _ in read.Messages.WithCancellation(context.CancellationToken)) + { + } + } + + private static async Task WriteLongTerm(CommandProcessorContext context, string[] args) + { + var clients = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[0]); + var minRate = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[1]); + var maxRate = args.Length == 0 ? 2 : MetricPrefixValue.ParseInt(args[2]); + var minutes = args.Length == 0 ? 1 : MetricPrefixValue.ParseInt(args[3]); + var stream = args.Length == 5 ? args[4] : null; + using var duration = CancellationTokenSource.CreateLinkedTokenSource(context.CancellationToken); + duration.CancelAfter(TimeSpan.FromMinutes(minutes)); + var random = new Random(); + await Task.WhenAll(Enumerable.Range(0, clients).Select(async clientIndex => + { + var client = context._grpcTestClient.CreateGrpcClient(); + while (!duration.IsCancellationRequested) + { + var rate = minRate == maxRate ? minRate : random.Next(minRate, maxRate + 1); + for (var i = 0; i < rate; i++) + { + await client.AppendToStreamAsync(stream ?? $"Stream-{clientIndex % 3}", StreamState.Any, [Event()], cancellationToken: duration.Token); + } + + await Task.Delay(TimeSpan.FromSeconds(1), duration.Token); + } + }).Select(IgnoreExpectedCancellation)); + } + + private static async Task IgnoreExpectedCancellation(Task task) + { + try + { + await task; + } + catch (OperationCanceledException) + { + } + } + + private static async Task Subscribe(CommandProcessorContext context, string[] args) + { + var client = context._grpcTestClient.CreateGrpcClient(); + var subscriptions = new List(); + + if (args.Length == 0) + { + context.Log.Information("Subscribing to all streams over gRPC"); + subscriptions.Add(await client.SubscribeToAllAsync( + FromAll.Start, + (_, resolvedEvent, _) => LogSubscriptionEvent(context, resolvedEvent), + cancellationToken: context.CancellationToken)); + } + else + { + foreach (var stream in args) + { + context.Log.Information("Subscribing to stream {stream} over gRPC", stream); + subscriptions.Add(await client.SubscribeToStreamAsync( + stream, + FromStream.Start, + (_, resolvedEvent, _) => LogSubscriptionEvent(context, resolvedEvent), + cancellationToken: context.CancellationToken)); + } + } + + context.Log.Information("Subscribed to {subscriptionCount} streams over gRPC", subscriptions.Count); + await Task.Delay(System.Threading.Timeout.InfiniteTimeSpan, context.CancellationToken); + } + + private static async Task SubscriptionStress(CommandProcessorContext context, string[] args) + { + var count = args.Length == 0 ? 5000 : MetricPrefixValue.ParseInt(args[0]); + var client = context._grpcTestClient.CreateGrpcClient(); + var subscriptions = new List(count); + long appeared = 0; + var interval = System.Diagnostics.Stopwatch.StartNew(); + for (var i = 0; i < count; i++) + { + subscriptions.Add(await client.SubscribeToStreamAsync($"stream-{i}", FromStream.Start, + (_, _, _) => + { + var observed = Interlocked.Increment(ref appeared); + if (observed % 100000 == 0) + { + context.Log.Information( + "Received {eventCount} subscription events at {rate:0.0} events per second", + observed, + 100000 / interval.Elapsed.TotalSeconds); + interval.Restart(); + } + + return Task.CompletedTask; + }, cancellationToken: context.CancellationToken)); + } + + context.Log.Information("Subscribed to {subscriptionCount} streams over gRPC", subscriptions.Count); + await Task.Delay(System.Threading.Timeout.InfiniteTimeSpan, context.CancellationToken); + } + + private static Task LogSubscriptionEvent(CommandProcessorContext context, ResolvedEvent resolvedEvent) + { + var @event = resolvedEvent.Event; + context.Log.Information( + "New event on {stream} at {eventNumber}: {eventType}\nData: {data}\nMetadata: {metadata}", + @event.EventStreamId, + @event.EventNumber, + @event.EventType, + Utf8NoBom.GetString(@event.Data.Span), + Utf8NoBom.GetString(@event.Metadata.Span)); + return Task.CompletedTask; + } + + private static async Task Scavenge(CommandProcessorContext context, string[] args) + { + var result = await context._grpcTestClient.CreateOperationsClient() + .StartScavengeAsync(cancellationToken: context.CancellationToken); + context.Log.Information("Scavenge request returned {result}", result); + } + + private static async Task Verify(CommandProcessorContext context, string[] args) + { + var writers = args.Length == 0 ? 20 : MetricPrefixValue.ParseInt(args[0]); + var readers = args.Length == 0 ? 30 : MetricPrefixValue.ParseInt(args[1]); + var events = args.Length == 0 ? 1_000_000 : MetricPrefixValue.ParseInt(args[2]); + var streams = args.Length == 0 ? 1_000 : MetricPrefixValue.ParseInt(args[3]); + var producers = args.Length == 0 ? ["bank"] : args[4] + .Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + if (writers <= 0 || readers <= 0 || events < 0 || streams <= 0) + { + throw new ArgumentOutOfRangeException(nameof(args), "Writers, readers, and streams must be positive, and events cannot be negative."); + } + + if (producers.Length != 1 || !producers[0].Equals("bank", StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException("The gRPC verification workload supports the bank producer.", nameof(args)); + } + + var heads = Enumerable.Repeat(-1, streams).ToArray(); + var streamGates = Enumerable.Range(0, streams).Select(_ => new SemaphoreSlim(1, 1)).ToArray(); + var stopReading = false; + try + { + var writerTasks = Enumerable.Range(0, writers).Select(async writerIndex => + { + var client = context._grpcTestClient.CreateGrpcClient(); + var random = new Random(writerIndex); + var requests = events / writers + (writerIndex == writers - 1 ? events % writers : 0); + for (var request = 0; request < requests; request++) + { + var streamIndex = writers >= streams ? writerIndex % streams : random.Next(streams); + await streamGates[streamIndex].WaitAsync(context.CancellationToken); + try + { + var nextVersion = heads[streamIndex] + 1; + var expected = nextVersion == 0 + ? new ExpectedRevision(StreamState.NoStream, null) + : new ExpectedRevision(default, StreamRevision.FromInt64(nextVersion - 1)); + await Append( + client, + $"account-{streamIndex}", + expected, + [VerificationEvent(nextVersion)], + null, + context.CancellationToken); + Volatile.Write(ref heads[streamIndex], nextVersion); + } + finally + { + streamGates[streamIndex].Release(); + } + } + }).ToArray(); + + var readerTasks = Enumerable.Range(0, readers).Select(async readerIndex => + { + var client = context._grpcTestClient.CreateGrpcClient(); + var random = new Random(readerIndex); + while (!Volatile.Read(ref stopReading)) + { + var streamIndex = random.Next(streams); + var head = Volatile.Read(ref heads[streamIndex]); + if (head < 0) + { + await Task.Yield(); + continue; + } + + await VerifyEventAt( + client, + $"account-{streamIndex}", + random.Next(head + 1), + context.CancellationToken); + } + }).ToArray(); + + try + { + await Task.WhenAll(writerTasks); + } + finally + { + Volatile.Write(ref stopReading, true); + await Task.WhenAll(readerTasks); + } + + var verificationClient = context._grpcTestClient.CreateGrpcClient(); + for (var streamIndex = 0; streamIndex < streams; streamIndex++) + { + for (var eventNumber = 0; eventNumber <= heads[streamIndex]; eventNumber++) + { + await VerifyEventAt( + verificationClient, + $"account-{streamIndex}", + eventNumber, + context.CancellationToken); + } + } + } + finally + { + foreach (var gate in streamGates) + { + gate.Dispose(); + } + } + } + + private static EventData VerificationEvent(int version) => + new( + Uuid.FromGuid(Guid.NewGuid()), + "BankAccountVerificationEvent", + Utf8NoBom.GetBytes($"account-event-{version}"), + contentType: "application/octet-stream"); + + private static async Task VerifyEventAt( + EventStoreClient client, + string stream, + int eventNumber, + CancellationToken cancellationToken) + { + var read = client.ReadStreamAsync( + Direction.Forwards, + stream, + StreamPosition.FromInt64(eventNumber), + maxCount: 1, + cancellationToken: cancellationToken); + await foreach (var message in read.Messages.WithCancellation(cancellationToken)) + { + if (message is not StreamMessage.Event observed) + { + continue; + } + + var expected = VerificationEvent(eventNumber); + if (observed.ResolvedEvent.Event.EventType != expected.Type || + !observed.ResolvedEvent.Event.Data.Span.SequenceEqual(expected.Data.Span)) + { + throw new InvalidOperationException($"Event {eventNumber} in {stream} did not match its expected payload."); + } + + return; + } + + throw new InvalidOperationException($"Event {eventNumber} was not found in {stream}."); + } + + private static async Task CheckGrpcSanitization(CommandProcessorContext context, string[] args) + { + using var handler = new HttpClientHandler(); + handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; + using var client = new HttpClient(handler); + using var request = new HttpRequestMessage(HttpMethod.Post, context._grpcTestClient.HttpEndpoint) + { + Version = HttpVersion.Version20, + VersionPolicy = HttpVersionPolicy.RequestVersionExact, + Content = new ByteArrayContent([0, 0, 0, 0, 16, 1]) + }; + request.RequestUri = new Uri(request.RequestUri, "/event_store.client.streams.Streams/Read"); + request.Content.Headers.ContentType = new("application/grpc"); + using var response = await client.SendAsync(request, context.CancellationToken); + await response.Content.ReadAsByteArrayAsync(context.CancellationToken); + var grpcStatus = response.TrailingHeaders.TryGetValues("grpc-status", out var trailerValues) + ? trailerValues.SingleOrDefault() + : response.Headers.TryGetValues("grpc-status", out var headerValues) + ? headerValues.SingleOrDefault() + : null; + if (response.IsSuccessStatusCode && (grpcStatus is null or "0")) + { + throw new InvalidOperationException("The server accepted a malformed gRPC frame."); + } + } + + private readonly record struct ExpectedRevision(StreamState State, StreamRevision? Revision) + { + public static ExpectedRevision Parse(string value) => value.ToUpperInvariant() switch + { + "ANY" => new(StreamState.Any, null), + "NO_STREAM" or "NOSTREAM" => new(StreamState.NoStream, null), + _ => new(default, StreamRevision.FromInt64(long.Parse(value))) + }; + } +} + +internal sealed class DelegatingProcessor : ICmdProcessor +{ + private readonly ICmdProcessor _inner; + private readonly Func _translate; + + public DelegatingProcessor(string keyword, string usage, ICmdProcessor inner, Func translate = null) + { + Keyword = keyword; + Usage = usage; + _inner = inner; + _translate = translate ?? (args => args); + } + + public string Keyword { get; } + public string Usage { get; } + + public bool Execute(CommandProcessorContext context, string[] args) => _inner.Execute(context, _translate(args)); +} diff --git a/src/EventStore.TestClient/GrpcTestClient.cs b/src/EventStore.TestClient/GrpcTestClient.cs index b2bb9fa793..9959c6404c 100644 --- a/src/EventStore.TestClient/GrpcTestClient.cs +++ b/src/EventStore.TestClient/GrpcTestClient.cs @@ -1,6 +1,5 @@ using System; using EventStore.Client; -using Connection = EventStore.Transport.Tcp.TcpTypedConnection; using ILogger = Serilog.ILogger; namespace EventStore.TestClient; @@ -34,6 +33,12 @@ public EventStoreClient CreateGrpcClient() return new EventStoreClient(Settings); } + internal EventStoreOperationsClient CreateOperationsClient() + { + _log.Debug("Creating gRPC operations client with connection string '{connectionString}'.", ConnectionString); + return new EventStoreOperationsClient(Settings); + } + /// /// True in case username and/or password are not specified. /// @@ -41,7 +46,10 @@ public EventStoreClient CreateGrpcClient() string.IsNullOrWhiteSpace(Settings.DefaultCredentials?.Username) || string.IsNullOrWhiteSpace(Settings.DefaultCredentials?.Password); - private EventStoreClientSettings Settings => EventStoreClientSettings.Create(ConnectionString); + internal EventStoreClientSettings Settings => EventStoreClientSettings.Create(ConnectionString); + + internal Uri HttpEndpoint => new( + $"{(_options.UseTls ? "https" : "http")}://{_options.Host}:{_options.HttpPort}"); private string ConnectionString => string.IsNullOrWhiteSpace(_options.ConnectionString) ? $"esdb://{_options.Host}:{_options.HttpPort}?tls={_options.UseTls}&tlsVerifyCert={_options.TlsValidateServer}" diff --git a/src/EventStore.TestClient/PortsHelper.cs b/src/EventStore.TestClient/PortsHelper.cs deleted file mode 100644 index ad9ff9c688..0000000000 --- a/src/EventStore.TestClient/PortsHelper.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Sockets; -using System.Threading.Tasks; -using ILogger = Serilog.ILogger; - -namespace EventStore.TestClient; - -internal static class PortsHelper -{ - private static readonly ILogger Log = - Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "PortsHelper"); - - public const int PortStart = 45000; - public const int PortCount = 200; - - private static readonly ConcurrentQueue AvailablePorts = - new ConcurrentQueue(Enumerable.Range(PortStart, PortCount)); - - public static void InitPorts(IPAddress ip) - { - var sw = Stopwatch.StartNew(); - - int p; - while (AvailablePorts.TryDequeue(out p)) - { - } - - Log.Verbose("PortsHelper: starting to examine ports at [{ip}].", ip); - - int succ = 0; - for (int port = PortStart; port < PortStart + PortCount; ++port) - { - try - { - var listener = new TcpListener(ip, port); - listener.Start(); - listener.Stop(); - } - catch (Exception exc) - { - Log.Verbose(exc, "PortsHelper: port {port} unavailable for TcpListener. Error: {e}.", port, - exc.Message); - continue; - } - - try - { - var httpListener = new HttpListener(); - httpListener.Prefixes.Add(string.Format("http://+:{0}/", port)); - httpListener.Start(); - - Exception httpListenerError = null; - var listenTask = Task.Factory.StartNew(() => - { - try - { - var context = httpListener.GetContext(); - context.Response.Close(new byte[] { 1, 2, 3 }, true); - } - catch (Exception exc) - { - httpListenerError = exc; - } - }); - var client = new HttpClient(); - var request = client.GetAsync(string.Format("http://{0}:{1}/", ip, port)).Result; - var buffer = new byte[256]; - var read = request.Content.ReadAsStream().Read(buffer, 0, buffer.Length); - if (read != 3 || buffer[0] != 1 || buffer[1] != 2 || buffer[2] != 3) - { - throw new Exception(string.Format("Unexpected response received from HTTP on port {0}.", port)); - } - - if (!listenTask.Wait(5000)) - { - throw new Exception("PortsHelper: time out waiting for HttpListener to return."); - } - - if (httpListenerError != null) - { - throw httpListenerError; - } - - httpListener.Stop(); - } - catch (Exception exc) - { - Log.Verbose(exc, "PortsHelper: port {port} unavailable for HttpListener. Error: {e}.", port, - exc.Message); - continue; - } - - AvailablePorts.Enqueue(port); - succ += 1; - } - - Log.Verbose("PortsHelper: {ports} ports are available at [{ip}].", succ, ip); - if (succ <= PortCount / 2) - { - throw new Exception("More than half requested ports are unavailable."); - } - - Log.Verbose("PortsHelper: test took {elapsed}.", sw.Elapsed); - } - - public static int GetAvailablePort(IPAddress ip) - { - for (int i = 0; i < 50; ++i) - { - int port; - if (!AvailablePorts.TryDequeue(out port)) - { - throw new Exception("Couldn't get free TCP port for MiniNode."); - } - - /* - try - { - var listener = new TcpListener(ip, port); - listener.Start(); - listener.Stop(); - } - catch (Exception) - { - AvailablePorts.Enqueue(port); - continue; - } - */ - - try - { - var httpListener = new HttpListener(); - httpListener.Prefixes.Add(string.Format("http://127.0.0.1:{0}/", port)); - httpListener.Start(); - httpListener.Stop(); - } - catch (Exception) - { - AvailablePorts.Enqueue(port); - continue; - // throw new Exception( - // string.Format("HttpListener couldn't listen on port {0}, but TcpListener was OK.\nError: {1}", port, exc), exc); - } - - return port; - } - - throw new Exception("Reached trials limit while trying to get free port for MiniNode"); - } - - public static void ReturnPort(int port) - { - AvailablePorts.Enqueue(port); - } - - /* - private static int[] GetRandomPorts(int from, int portCount) - { - var res = new int[portCount]; - var rnd = new Random(Math.Abs(Guid.NewGuid().GetHashCode())); - for (int i = 0; i < portCount; ++i) - { - res[i] = from + i; - } - for (int i = 0; i < portCount; ++i) - { - int index = rnd.Next(portCount - i); - int tmp = res[i]; - res[i] = res[i + index]; - res[i + index] = tmp; - } - return res; - } - */ -} diff --git a/src/EventStore.TestClient/Program.cs b/src/EventStore.TestClient/Program.cs index 942b00c305..eee7b0210a 100644 --- a/src/EventStore.TestClient/Program.cs +++ b/src/EventStore.TestClient/Program.cs @@ -29,22 +29,17 @@ internal static class Program /// Print effective configuration to console and then exit. /// /// The host to bind to. - /// The port to run the TCP server on. /// The port to run the HTTP server on. /// - /// - /// - /// - /// /// /// - /// The connection string to use when connecting to the server. Not used by the raw TCP client + /// The connection string to use when connecting to the server. /// The format for the stats log. /// public static async Task Main(bool version = false, FileInfo? log = null, bool whatIf = false, - string[]? command = null, string host = "localhost", int tcpPort = 1113, int httpPort = 2113, - int timeout = Timeout.Infinite, int readWindow = 2000, int writeWindow = 2000, int pingWindow = 2000, - bool reconnect = true, bool useTls = false, bool tlsValidateServer = false, string connectionString = "", + string[]? command = null, string host = "localhost", int httpPort = 2113, + int timeout = Timeout.Infinite, + bool useTls = false, bool tlsValidateServer = false, string connectionString = "", StatsFormat statsFormat = StatsFormat.Csv) { Log.Logger = EventStoreLoggerConfiguration.ConsoleLog; @@ -63,11 +58,6 @@ public static async Task Main(bool version = false, FileInfo? log = null, b Timeout = timeout, HttpPort = httpPort, Host = host, - TcpPort = tcpPort, - Reconnect = reconnect, - PingWindow = pingWindow, - ReadWindow = readWindow, - WriteWindow = writeWindow, UseTls = useTls, TlsValidateServer = tlsValidateServer, ConnectionString = connectionString, diff --git a/src/EventStore.TestClient/TcpTestClient.cs b/src/EventStore.TestClient/TcpTestClient.cs deleted file mode 100644 index 2b4470a897..0000000000 --- a/src/EventStore.TestClient/TcpTestClient.cs +++ /dev/null @@ -1,205 +0,0 @@ -using System; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Threading; -using EventStore.BufferManagement; -using EventStore.Common.Utils; -using EventStore.Core.Services.Transport.Tcp; -using EventStore.Transport.Tcp; -using EventStore.Transport.Tcp.Formatting; -using EventStore.Transport.Tcp.Framing; -using Connection = EventStore.Transport.Tcp.TcpTypedConnection; -using ILogger = Serilog.ILogger; - -namespace EventStore.TestClient; - -/// -/// A test client that connects over Raw TCP connections -/// -public class TcpTestClient -{ - private readonly BufferManager _bufferManager = - new BufferManager(TcpConfiguration.BufferChunksCount, TcpConfiguration.SocketBufferSize); - - private readonly TcpClientConnector _connector = new TcpClientConnector(); - - private readonly bool _validateServer; - private readonly bool _useSsl; - private readonly ILogger _log; - private readonly bool _interactiveMode; - /// - /// The TCP EndPoint of the Event Store node - /// - public readonly EndPoint TcpEndpoint; - /// - /// The Client Options used to start EventStore.TestClient - /// - public readonly ClientOptions Options; - - /// - /// Constructs a new - /// - /// - /// - /// - public TcpTestClient(ClientOptions options, bool interactiveMode, ILogger log) - { - _interactiveMode = interactiveMode; - _log = log; - _useSsl = options.UseTls; - TcpEndpoint = new DnsEndPoint(options.Host, options.TcpPort); - _validateServer = options.TlsValidateServer; - Options = options; - } - - /// - /// Creates a new raw TCP connection - /// - /// - /// - /// - /// - /// - /// - /// - /// - public Connection CreateTcpConnection(CommandProcessorContext context, - Action handlePackage, - Action connectionEstablished = null, - Action connectionClosed = null, - bool failContextOnError = true, - IPEndPoint tcpEndPoint = null) - { - var connectionCreatedEvent = new ManualResetEventSlim(false); - Connection typedConnection = null; - - Action onConnectionEstablished = conn => - { - // we execute callback on ThreadPool because on FreeBSD it can be called synchronously - // causing deadlock - ThreadPool.QueueUserWorkItem(_ => - { - if (!_interactiveMode) - { - _log.Information( - "TcpTypedConnection: connected to [{remoteEndPoint}, L{localEndPoint}, {connectionId:B}].", - conn.RemoteEndPoint, conn.LocalEndPoint, conn.ConnectionId); - } - - if (connectionEstablished != null) - { - if (!connectionCreatedEvent.Wait(10000)) - { - throw new Exception("TcpTypedConnection: creation took too long!"); - } - - connectionEstablished(typedConnection); - } - }); - }; - Action onConnectionFailed = (conn, error) => - { - _log.Error( - "TcpTypedConnection: connection to [{remoteEndPoint}, L{localEndPoint}, {connectionId:B}] failed. Error: {e}.", - conn.RemoteEndPoint, conn.LocalEndPoint, conn.ConnectionId, error); - - if (connectionClosed != null) - { - connectionClosed(null, error); - } - - if (failContextOnError) - { - context.Fail(reason: string.Format("Socket connection failed with error {0}.", error)); - } - }; - - var endpoint = tcpEndPoint ?? TcpEndpoint; - ITcpConnection connection; - if (_useSsl) - { - connection = _connector.ConnectSslTo( - Guid.NewGuid(), - endpoint.GetHost(), - endpoint.GetOtherNames(), - endpoint.ResolveDnsToIPAddress(), - TcpConnectionManager.ConnectionTimeout, - (_, _, err, _) => (err == SslPolicyErrors.None || !_validateServer, err.ToString()), - () => null, - onConnectionEstablished, - onConnectionFailed, - verbose: false); - } - else - { - connection = _connector.ConnectTo( - Guid.NewGuid(), - endpoint.ResolveDnsToIPAddress(), - TcpConnectionManager.ConnectionTimeout, - onConnectionEstablished, - onConnectionFailed, - verbose: false); - } - - typedConnection = new Connection(connection, new RawMessageFormatter(_bufferManager), - new LengthPrefixMessageFramer()); - typedConnection.ConnectionClosed += - (conn, error) => - { - if (!_interactiveMode || error != SocketError.Success) - { - _log.Information( - "TcpTypedConnection: connection [{remoteEndPoint}, L{localEndPoint}] was closed {status}", - conn.RemoteEndPoint, conn.LocalEndPoint, - error == SocketError.Success ? "cleanly." : "with error: " + error + "."); - } - - if (connectionClosed != null) - { - connectionClosed(conn, error); - } - else - { - _log.Information("connectionClosed callback was null"); - } - }; - connectionCreatedEvent.Set(); - - typedConnection.ReceiveAsync( - (conn, pkg) => - { - var package = new TcpPackage(); - bool validPackage = false; - try - { - package = TcpPackage.FromArraySegment(new ArraySegment(pkg)); - validPackage = true; - - if (package.Command == TcpCommand.HeartbeatRequestCommand) - { - var resp = new TcpPackage(TcpCommand.HeartbeatResponseCommand, Guid.NewGuid(), null); - conn.EnqueueSend(resp.AsByteArray()); - return; - } - - handlePackage(conn, package); - } - catch (Exception ex) - { - _log.Information(ex, - "TcpTypedConnection: [{remoteEndPoint}, L{localEndPoint}] ERROR for {package}. Connection will be closed.", - conn.RemoteEndPoint, conn.LocalEndPoint, - validPackage ? package.Command as object : ""); - conn.Close(ex.Message); - - if (failContextOnError) - { - context.Fail(ex); - } - } - }); - - return typedConnection; - } -} diff --git a/src/EventStore.Transport.Tcp/EventStore.Transport.Tcp.csproj b/src/EventStore.Transport.Tcp/EventStore.Transport.Tcp.csproj deleted file mode 100644 index 069ee6f242..0000000000 --- a/src/EventStore.Transport.Tcp/EventStore.Transport.Tcp.csproj +++ /dev/null @@ -1,10 +0,0 @@ - - - true - - - - - - - diff --git a/src/EventStore.Transport.Tcp/Formatting/FormatterBase.cs b/src/EventStore.Transport.Tcp/Formatting/FormatterBase.cs deleted file mode 100644 index ffb2ed3346..0000000000 --- a/src/EventStore.Transport.Tcp/Formatting/FormatterBase.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.IO; -using EventStore.BufferManagement; - -namespace EventStore.Transport.Tcp.Formatting; - -public abstract class FormatterBase : IMessageFormatter -{ - /// - /// Gets a representing the IMessage provided. - /// - /// The message. - /// A with a representation of the message - public abstract BufferPool ToBufferPool(T message); - - /// - /// converts the message to a - /// - /// The message. - /// - public virtual ArraySegment ToArraySegment(T message) - { - return new ArraySegment(ToArray(message)); - } - - /// - /// Converts the message to a byte array - /// - /// The message. - /// - public virtual byte[] ToArray(T message) - { - using (BufferPool pool = ToBufferPool(message)) - { - return pool.ToByteArray(); - } - } - - /// - /// Gets a message from a - /// - /// The BufferPool to get data from. - /// - public virtual T From(BufferPool bufferPool) - { - if (bufferPool == null) - { - throw new ArgumentNullException("bufferPool"); - } - - var stream = new BufferPoolStream(bufferPool); - return From(stream); - } - - /// - /// Gets a message from a - /// - /// The segment containing the raw data. - /// - public virtual T From(ArraySegment segment) - { - using (var stream = new MemoryStream(segment.Array, segment.Offset, segment.Count, false)) - { - return From(stream); - } - } - - /// - /// Gets a message from a byte array - /// - /// The byte array. - /// - public virtual T From(byte[] array) - { - if (array == null) - { - throw new ArgumentNullException("array"); - } - - using (var stream = new MemoryStream(array, 0, array.Length, false)) - { - return From(stream); - } - } - - /// - /// Creates a message object from the specified stream - /// - /// The stream. - /// - public abstract T From(Stream stream); -} diff --git a/src/EventStore.Transport.Tcp/Formatting/IMessageFormatter.cs b/src/EventStore.Transport.Tcp/Formatting/IMessageFormatter.cs deleted file mode 100644 index ba46714574..0000000000 --- a/src/EventStore.Transport.Tcp/Formatting/IMessageFormatter.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using EventStore.BufferManagement; - -namespace EventStore.Transport.Tcp.Formatting; - -public interface IMessageFormatter -{ - /// - /// Converts the object to a representing a binary format of it. - /// - /// The message to convert. - /// A containing the data representing the object. - BufferPool ToBufferPool(T message); - - /// - /// Converts the object to a representing a binary format of it. - /// - /// The message to convert. - /// A containing the data representing the object. - ArraySegment ToArraySegment(T message); - - /// - /// Converts the object to a byte array representing a binary format of it. - /// - /// The message to convert. - /// A containing the data representing the object. - byte[] ToArray(T message); - - /// - /// Takes a and converts its contents to a message object - /// - /// The buffer pool. - /// A message representing the data given - T From(BufferPool bufferPool); - - /// - /// Takes an ArraySegment and converts its contents to a message object - /// - /// The buffer pool. - /// A message representing the data given - T From(ArraySegment segment); - - /// - /// Takes an Array and converts its contents to a message object - /// - /// The buffer pool. - /// A message representing the data given - T From(byte[] array); -} diff --git a/src/EventStore.Transport.Tcp/Formatting/RawMessageFormatter.cs b/src/EventStore.Transport.Tcp/Formatting/RawMessageFormatter.cs deleted file mode 100644 index f8fd2fcdb7..0000000000 --- a/src/EventStore.Transport.Tcp/Formatting/RawMessageFormatter.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System; -using EventStore.BufferManagement; - -namespace EventStore.Transport.Tcp.Formatting; - -/// -/// Formatter which does not format anything, actually. Just outputs raw byte[]. -/// -public class RawMessageFormatter : IMessageFormatter -{ - private readonly BufferManager _bufferManager; - private readonly int _initialBuffers; - - /// - /// Initializes a new instance of the class. - /// - public RawMessageFormatter() : this(BufferManager.Default, 2) - { - } - - - /// - /// Initializes a new instance of the class. - /// - /// The buffer manager. - public RawMessageFormatter(BufferManager bufferManager) : this(bufferManager, 2) - { - } - - - /// - /// Initializes a new instance of the class. - /// - /// The buffer manager. - /// The number of initial buffers. - public RawMessageFormatter(BufferManager bufferManager, int initialBuffers) - { - _bufferManager = bufferManager; - _initialBuffers = initialBuffers; - } - - public BufferPool ToBufferPool(byte[] message) - { - if (message == null) - { - throw new ArgumentNullException("message"); - } - - var bufferPool = new BufferPool(_initialBuffers, _bufferManager); - var stream = new BufferPoolStream(bufferPool); - stream.Write(message, 0, message.Length); - return bufferPool; - } - - public ArraySegment ToArraySegment(byte[] message) - { - if (message == null) - { - throw new ArgumentNullException("message"); - } - - return new ArraySegment(message, 0, message.Length); - } - - public byte[] ToArray(byte[] message) - { - if (message == null) - { - throw new ArgumentNullException("message"); - } - - return message; - } - - public byte[] From(BufferPool bufferPool) - { - return bufferPool.ToByteArray(); - } - - public byte[] From(ArraySegment segment) - { - var msg = new byte[segment.Count]; - Buffer.BlockCopy(segment.Array, segment.Offset, msg, 0, segment.Count); - return msg; - } - - public byte[] From(byte[] array) - { - return array; - } -} diff --git a/src/EventStore.Transport.Tcp/Framing/IMessageFramer.cs b/src/EventStore.Transport.Tcp/Framing/IMessageFramer.cs deleted file mode 100644 index b254ccc5dc..0000000000 --- a/src/EventStore.Transport.Tcp/Framing/IMessageFramer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace EventStore.Transport.Tcp.Framing; - -public interface IMessageFramer -{ - bool HasData { get; } - IEnumerable> FrameData(ArraySegment data); - void Reset(); -} - -/// -/// Encodes outgoing messages in frames and decodes incoming frames. -/// For decoding it uses an internal state, raising a registered -/// callback, once full message arrives -/// -public interface IMessageFramer : IMessageFramer -{ - void UnFrameData(IEnumerable> data); - void UnFrameData(ArraySegment data); - void RegisterMessageArrivedCallback(Action handler); -} - -public interface IAsyncMessageFramer : IMessageFramer -{ - ValueTask UnFrameData(IEnumerable> data, CancellationToken token); - ValueTask UnFrameData(ArraySegment data, CancellationToken token); - void RegisterMessageArrivedCallback(Func handler); -} diff --git a/src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramer.cs b/src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramer.cs deleted file mode 100644 index c86bb34ad7..0000000000 --- a/src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramer.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System; -using System.Collections.Generic; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp.Framing; - -/// -/// Uses length-prefixed framing to encode outgoing messages and decode -/// incoming messages, using internal state and raising a callback once -/// full message arrives. -/// -public class LengthPrefixMessageFramer : IMessageFramer> -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - - public const int HeaderLength = sizeof(Int32); - - private byte[] _messageBuffer; - private int _bufferIndex = 0; - private Action> _receivedHandler; - private readonly int _maxPackageSize; - - private int _headerBytes = 0; - private int _packageLength = 0; - - /// - /// Initializes a new instance of the class. - /// - public LengthPrefixMessageFramer(int maxPackageSize = 64 * 1024 * 1024) - { - Ensure.Positive(maxPackageSize, "maxPackageSize"); - _maxPackageSize = maxPackageSize; - } - - public bool HasData => _headerBytes > 0; - - public void Reset() - { - _messageBuffer = null; - _headerBytes = 0; - _packageLength = 0; - _bufferIndex = 0; - } - - public void UnFrameData(IEnumerable> data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - foreach (ArraySegment buffer in data) - { - Parse(buffer); - } - } - - public void UnFrameData(ArraySegment data) - { - Parse(data); - } - - /// - /// Parses a stream chunking based on length-prefixed framing. - /// Calls are re-entrant and hold state internally. Once full message arrives, - /// callback is raised (it is registered via - /// - /// A byte array of data to append - private void Parse(ArraySegment bytes) - { - byte[] data = bytes.Array; - for (int i = bytes.Offset, n = bytes.Offset + bytes.Count; i < n; i++) - { - if (_headerBytes < HeaderLength) - { - _packageLength |= (data[i] << (_headerBytes * 8)); // little-endian order - ++_headerBytes; - if (_headerBytes == HeaderLength) - { - if (_packageLength <= 0 || _packageLength > _maxPackageSize) - { - Log.Error("FRAMING ERROR! Data:\n {data}", Common.Utils.Helper.FormatBinaryDump(bytes)); - throw new PackageFramingException(string.Format( - "Package size is out of bounds: {0} (max: {1}).", - _packageLength, _maxPackageSize)); - } - - _messageBuffer = new byte[_packageLength]; - } - } - else - { - int copyCnt = Math.Min(bytes.Count + bytes.Offset - i, _packageLength - _bufferIndex); - Buffer.BlockCopy(bytes.Array, i, _messageBuffer, _bufferIndex, copyCnt); - _bufferIndex += copyCnt; - i += copyCnt - 1; - - if (_bufferIndex == _packageLength) - { - if (_receivedHandler != null) - { - _receivedHandler(new ArraySegment(_messageBuffer, 0, _bufferIndex)); - } - - _messageBuffer = null; - _headerBytes = 0; - _packageLength = 0; - _bufferIndex = 0; - } - } - } - } - - public IEnumerable> FrameData(ArraySegment data) - { - var length = data.Count; - - yield return new ArraySegment( - new[] { (byte)length, (byte)(length >> 8), (byte)(length >> 16), (byte)(length >> 24) }); - yield return data; - } - - public void RegisterMessageArrivedCallback(Action> handler) - { - Ensure.NotNull(handler, nameof(handler)); - _receivedHandler = handler; - } -} diff --git a/src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramerWithBufferPool.cs b/src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramerWithBufferPool.cs deleted file mode 100644 index eddc913eb2..0000000000 --- a/src/EventStore.Transport.Tcp/Framing/LengthPrefixMessageFramerWithBufferPool.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using EventStore.BufferManagement; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp.Framing; - -public class LengthPrefixMessageFramerWithBufferPool -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - - private const int PrefixLength = sizeof(int); - - private readonly int _maxPackageSize; - private readonly BufferManager _bufferManager; - private BufferPool _messageBuffer; - private Action _receivedHandler; - - private int _headerBytes; - private int _packageLength; - - /// - /// Initializes a new instance of the class. - /// - public LengthPrefixMessageFramerWithBufferPool(BufferManager bufferManager, - int maxPackageSize = 16 * 1024 * 1024) - { - Ensure.NotNull(bufferManager, "bufferManager"); - Ensure.Positive(maxPackageSize, "maxPackageSize"); - _bufferManager = bufferManager; - _maxPackageSize = maxPackageSize; - } - - public void Reset() - { - _messageBuffer = null; - _headerBytes = 0; - _packageLength = 0; - } - - public void UnFrameData(IEnumerable> data) - { - if (data == null) - { - throw new ArgumentNullException("data"); - } - - foreach (ArraySegment buffer in data) - { - Parse(buffer); - } - } - - public void UnFrameData(ArraySegment data) - { - Parse(data); - } - - /// - /// Parses a stream chunking based on length-prefixed framing. Calls are re-entrant and hold state internally. - /// - /// A byte array of data to append - private void Parse(ArraySegment bytes) - { - byte[] data = bytes.Array; - for (int i = bytes.Offset; i < bytes.Offset + bytes.Count;) - { - if (_headerBytes < PrefixLength) - { - _packageLength |= (data[i] << (_headerBytes * 8)); // little-endian order - ++_headerBytes; - i += 1; - if (_headerBytes == PrefixLength) - { - if (_packageLength <= 0 || _packageLength > _maxPackageSize) - { - Log.Error("FRAMING ERROR! Data:\n {data}", Common.Utils.Helper.FormatBinaryDump(bytes)); - throw new PackageFramingException(string.Format( - "Package size is out of bounds: {0} (max: {1}).", - _packageLength, _maxPackageSize)); - } - - _messageBuffer = new BufferPool(_bufferManager); - } - } - else - { - int copyCnt = Math.Min(bytes.Count + bytes.Offset - i, _packageLength - _messageBuffer.Length); - _messageBuffer.Append(bytes.Array, i, copyCnt); - i += copyCnt; - - if (_messageBuffer.Length == _packageLength) - { - if (_receivedHandler != null) - { - _receivedHandler(_messageBuffer); - } - - _messageBuffer = null; - _headerBytes = 0; - _packageLength = 0; - } - } - } - } - - public IEnumerable> FrameData(ArraySegment data) - { - var length = data.Count; - - yield return new ArraySegment( - new[] { (byte)length, (byte)(length >> 8), (byte)(length >> 16), (byte)(length >> 24) }); - yield return data; - } - - public void RegisterMessageArrivedCallback(Action handler) - { - if (handler == null) - { - throw new ArgumentNullException("handler"); - } - - _receivedHandler = handler; - } -} diff --git a/src/EventStore.Transport.Tcp/Framing/PackageFramingException.cs b/src/EventStore.Transport.Tcp/Framing/PackageFramingException.cs deleted file mode 100644 index 14e62f5cf6..0000000000 --- a/src/EventStore.Transport.Tcp/Framing/PackageFramingException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Runtime.Serialization; - -namespace EventStore.Transport.Tcp.Framing; - -public class PackageFramingException : Exception -{ - public PackageFramingException(string message) : base(message) - { - } -} diff --git a/src/EventStore.Transport.Tcp/Helper.cs b/src/EventStore.Transport.Tcp/Helper.cs deleted file mode 100644 index 6baed53061..0000000000 --- a/src/EventStore.Transport.Tcp/Helper.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using EventStore.Common.Utils; - -namespace EventStore.Transport.Tcp; - -internal static class Helper -{ - public static void EatException(Action action) - { - try - { - action(); - } - catch (Exception) - { - } - } - - public static T EatException(Func func, T defaultValue = default(T)) - { - Ensure.NotNull(func, "func"); - try - { - return func(); - } - catch (Exception) - { - return defaultValue; - } - } -} diff --git a/src/EventStore.Transport.Tcp/IMonitoredTcpConnection.cs b/src/EventStore.Transport.Tcp/IMonitoredTcpConnection.cs deleted file mode 100644 index 5c1a754fd1..0000000000 --- a/src/EventStore.Transport.Tcp/IMonitoredTcpConnection.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace EventStore.Transport.Tcp; - -public interface IMonitoredTcpConnection -{ - bool IsReadyForSend { get; } - bool IsReadyForReceive { get; } - bool IsInitialized { get; } - bool IsFaulted { get; } - bool IsClosed { get; } - - bool InSend { get; } - bool InReceive { get; } - - DateTime? LastSendStarted { get; } - DateTime? LastReceiveStarted { get; } - - int PendingSendBytes { get; } - int InSendBytes { get; } - int PendingReceivedBytes { get; } - - long TotalBytesSent { get; } - long TotalBytesReceived { get; } -} diff --git a/src/EventStore.Transport.Tcp/ITcpConnection.cs b/src/EventStore.Transport.Tcp/ITcpConnection.cs deleted file mode 100644 index f0947aa9d8..0000000000 --- a/src/EventStore.Transport.Tcp/ITcpConnection.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; - -namespace EventStore.Transport.Tcp; - -public interface ITcpConnection -{ - event Action ConnectionClosed; - - Guid ConnectionId { get; } - string ClientConnectionName { get; } - IPEndPoint RemoteEndPoint { get; } - IPEndPoint LocalEndPoint { get; } - int SendQueueSize { get; } - int PendingSendBytes { get; } - long TotalBytesSent { get; } - long TotalBytesReceived { get; } - bool IsClosed { get; } - - void ReceiveAsync(Action>> callback); - void EnqueueSend(IEnumerable> data); - void Close(string reason); - void SetClientConnectionName(string clientConnectionName); -} diff --git a/src/EventStore.Transport.Tcp/SocketArgsPool.cs b/src/EventStore.Transport.Tcp/SocketArgsPool.cs deleted file mode 100644 index c4e4bb1b36..0000000000 --- a/src/EventStore.Transport.Tcp/SocketArgsPool.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net.Sockets; - -namespace EventStore.Transport.Tcp; - -internal class SocketArgsPool -{ - public readonly string Name; - - private readonly Func _socketArgsCreator; - - private readonly ConcurrentStack _socketArgsPool = - new ConcurrentStack(); - - public SocketArgsPool(string name, int initialCount, Func socketArgsCreator) - { - if (socketArgsCreator == null) - { - throw new ArgumentNullException("socketArgsCreator"); - } - - if (initialCount < 0) - { - throw new ArgumentOutOfRangeException("initialCount"); - } - - Name = name; - _socketArgsCreator = socketArgsCreator; - - for (int i = 0; i < initialCount; ++i) - { - _socketArgsPool.Push(socketArgsCreator()); - } - } - - public SocketAsyncEventArgs Get() - { - SocketAsyncEventArgs result; - if (_socketArgsPool.TryPop(out result)) - { - return result; - } - - return _socketArgsCreator(); - } - - public void Return(SocketAsyncEventArgs socketArgs) - { - _socketArgsPool.Push(socketArgs); - } -} diff --git a/src/EventStore.Transport.Tcp/TcpClientConnector.cs b/src/EventStore.Transport.Tcp/TcpClientConnector.cs deleted file mode 100644 index ee9eb567d5..0000000000 --- a/src/EventStore.Transport.Tcp/TcpClientConnector.cs +++ /dev/null @@ -1,241 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp; - -public class TcpClientConnector -{ - private const int CheckPeriodMs = 200; - - private readonly SocketArgsPool _connectSocketArgsPool; - private readonly ConcurrentDictionary _pendingConnections; - private readonly Timer _timer; - - private static readonly ILogger Log = Serilog.Log.ForContext(); - - public TcpClientConnector() - { - _connectSocketArgsPool = new SocketArgsPool("TcpClientConnector._connectSocketArgsPool", - TcpConfiguration.ConnectPoolSize, - CreateConnectSocketArgs); - _pendingConnections = new ConcurrentDictionary(); - _timer = new Timer(TimerCallback); - // prevent possible null reference exceptions in case of slow initialization - _timer.Change(CheckPeriodMs, Timeout.Infinite); - } - - private SocketAsyncEventArgs CreateConnectSocketArgs() - { - var socketArgs = new SocketAsyncEventArgs(); - socketArgs.Completed += ConnectCompleted; - socketArgs.UserToken = new CallbacksStateToken(); - return socketArgs; - } - - public ITcpConnection ConnectTo(Guid connectionId, - IPEndPoint remoteEndPoint, - TimeSpan connectionTimeout, - Action onConnectionEstablished = null, - Action onConnectionFailed = null, - bool verbose = true) - { - Ensure.NotNull(remoteEndPoint, "remoteEndPoint"); - return TcpConnection.CreateConnectingTcpConnection(connectionId, remoteEndPoint, this, connectionTimeout, - onConnectionEstablished, onConnectionFailed, verbose); - } - - public ITcpConnection ConnectSslTo(Guid connectionId, - string targetHost, - string[] otherNames, - IPEndPoint remoteEndPoint, - TimeSpan connectionTimeout, - CertificateDelegates.ServerCertificateValidator sslServerCertValidator, - Func clientCertificatesSelector, - Action onConnectionEstablished = null, - Action onConnectionFailed = null, - bool verbose = true) - { - Ensure.NotNull(remoteEndPoint, "remoteEndPoint"); - return TcpConnectionSsl.CreateConnectingConnection(connectionId, targetHost, otherNames, remoteEndPoint, sslServerCertValidator, - clientCertificatesSelector, this, connectionTimeout, onConnectionEstablished, onConnectionFailed, verbose); - } - - internal void InitConnect(IPEndPoint serverEndPoint, - Action onSocketAssigned, - Action onConnectionEstablished, - Action onConnectionFailed, - ITcpConnection connection, - TimeSpan connectionTimeout) - { - if (serverEndPoint == null) - { - throw new ArgumentNullException("serverEndPoint"); - } - - if (onSocketAssigned == null) - { - throw new ArgumentNullException("onSocketAssigned"); - } - - if (onConnectionEstablished == null) - { - throw new ArgumentNullException("onConnectionEstablished"); - } - - if (onConnectionFailed == null) - { - throw new ArgumentNullException("onConnectionFailed"); - } - - var socketArgs = _connectSocketArgsPool.Get(); - var connectingSocket = new Socket(serverEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - onSocketAssigned(connectingSocket); - socketArgs.RemoteEndPoint = serverEndPoint; - socketArgs.AcceptSocket = connectingSocket; - var callbacks = (CallbacksStateToken)socketArgs.UserToken; - callbacks.OnConnectionEstablished = onConnectionEstablished; - callbacks.OnConnectionFailed = onConnectionFailed; - callbacks.PendingConnection = new PendingConnection(connection, DateTime.UtcNow.Add(connectionTimeout)); - - AddToConnecting(callbacks.PendingConnection); - - try - { - var firedAsync = connectingSocket.ConnectAsync(socketArgs); - if (!firedAsync) - { - ProcessConnect(socketArgs); - } - } - catch (ObjectDisposedException) - { - HandleBadConnect(socketArgs); - } - } - - private void ConnectCompleted(object sender, SocketAsyncEventArgs e) - { - ProcessConnect(e); - } - - private void ProcessConnect(SocketAsyncEventArgs e) - { - if (e.SocketError != SocketError.Success) - { - HandleBadConnect(e); - } - else - { - OnSocketConnected(e); - } - } - - private void HandleBadConnect(SocketAsyncEventArgs socketArgs) - { - var serverEndPoint = socketArgs.RemoteEndPoint; - var socketError = socketArgs.SocketError; - var callbacks = (CallbacksStateToken)socketArgs.UserToken; - var onConnectionFailed = callbacks.OnConnectionFailed; - var pendingConnection = callbacks.PendingConnection; - - Helper.EatException(() => socketArgs.AcceptSocket.Close()); - socketArgs.AcceptSocket = null; - callbacks.Reset(); - _connectSocketArgsPool.Return(socketArgs); - - if (RemoveFromConnecting(pendingConnection)) - { - onConnectionFailed((IPEndPoint)serverEndPoint, socketError); - } - } - - private void OnSocketConnected(SocketAsyncEventArgs socketArgs) - { - var remoteEndPoint = (IPEndPoint)socketArgs.RemoteEndPoint; - var socket = socketArgs.AcceptSocket; - var callbacks = (CallbacksStateToken)socketArgs.UserToken; - var onConnectionEstablished = callbacks.OnConnectionEstablished; - var pendingConnection = callbacks.PendingConnection; - - socketArgs.AcceptSocket = null; - callbacks.Reset(); - _connectSocketArgsPool.Return(socketArgs); - - if (RemoveFromConnecting(pendingConnection)) - { - onConnectionEstablished(remoteEndPoint, socket); - } - } - - private void TimerCallback(object state) - { - foreach (var pendingConnection in _pendingConnections.Values) - { - if (DateTime.UtcNow >= pendingConnection.WhenToKill && RemoveFromConnecting(pendingConnection)) - { - Helper.EatException(() => pendingConnection.Connection.Close("Connection establishment timeout.")); - } - } - - try - { - _timer.Change(CheckPeriodMs, Timeout.Infinite); - } - catch (ObjectDisposedException) - { - // ignore - } - } - - private void AddToConnecting(PendingConnection pendingConnection) - { - _pendingConnections.TryAdd(pendingConnection.Connection.ConnectionId, pendingConnection); - } - - private bool RemoveFromConnecting(PendingConnection pendingConnection) - { - PendingConnection conn; - if (pendingConnection.Connection == null) - { - Log.Warning("Network Card disconnected"); - return false; - } - - return _pendingConnections.TryRemove(pendingConnection.Connection.ConnectionId, out conn) - && Interlocked.CompareExchange(ref conn.Done, 1, 0) == 0; - } - - private class CallbacksStateToken - { - public Action OnConnectionEstablished; - public Action OnConnectionFailed; - public PendingConnection PendingConnection; - - public void Reset() - { - OnConnectionEstablished = null; - OnConnectionFailed = null; - PendingConnection = null; - } - } - - private class PendingConnection - { - public readonly ITcpConnection Connection; - public readonly DateTime WhenToKill; - public int Done; - - public PendingConnection(ITcpConnection connection, DateTime whenToKill) - { - Connection = connection; - WhenToKill = whenToKill; - } - } -} diff --git a/src/EventStore.Transport.Tcp/TcpConfiguration.cs b/src/EventStore.Transport.Tcp/TcpConfiguration.cs deleted file mode 100644 index 85355e5287..0000000000 --- a/src/EventStore.Transport.Tcp/TcpConfiguration.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace EventStore.Transport.Tcp; - -public static class TcpConfiguration -{ - public const int SocketCloseTimeoutSecs = 1; - - public const int AcceptBacklogCount = 128; - public const int ConcurrentAccepts = 1; - public const int AcceptPoolSize = ConcurrentAccepts * 2; - - public const int ConnectPoolSize = 32; - public const int SendReceivePoolSize = 512; - - public const int BufferChunksCount = 512; - public const int SocketBufferSize = 8 * 1024; -} diff --git a/src/EventStore.Transport.Tcp/TcpConnection.cs b/src/EventStore.Transport.Tcp/TcpConnection.cs deleted file mode 100644 index 4790529436..0000000000 --- a/src/EventStore.Transport.Tcp/TcpConnection.cs +++ /dev/null @@ -1,563 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using EventStore.BufferManagement; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp; - -public class TcpConnection : TcpConnectionBase, ITcpConnection -{ - internal const int MaxSendPacketSize = 65535 /*Max IP packet size*/ - 20 /*IP packet header size*/ - 32 /*TCP min header size*/; - - internal static readonly BufferManager BufferManager = - new BufferManager(TcpConfiguration.BufferChunksCount, TcpConfiguration.SocketBufferSize); - - private static readonly ILogger Log = Serilog.Log.ForContext(); - - private static readonly SocketArgsPool SocketArgsPool = new SocketArgsPool("TcpConnection.SocketArgsPool", - TcpConfiguration.SendReceivePoolSize, - () => new SocketAsyncEventArgs()); - - public static ITcpConnection CreateConnectingTcpConnection(Guid connectionId, - IPEndPoint remoteEndPoint, - TcpClientConnector connector, - TimeSpan connectionTimeout, - Action onConnectionEstablished, - Action onConnectionFailed, - bool verbose) - { - var connection = new TcpConnection(connectionId, remoteEndPoint, verbose); - // ReSharper disable ImplicitlyCapturedClosure - connector.InitConnect(remoteEndPoint, - (socket) => - { - connection.InitSocket(socket); - }, - (_, socket) => - { - connection.InitSendReceive(); - if (onConnectionEstablished != null) - { - onConnectionEstablished(connection); - } - }, - (_, socketError) => - { - if (onConnectionFailed != null) - { - onConnectionFailed(connection, socketError); - } - }, connection, connectionTimeout); - // ReSharper restore ImplicitlyCapturedClosure - return connection; - } - - public static ITcpConnection CreateAcceptedTcpConnection(Guid connectionId, IPEndPoint remoteEndPoint, - Socket socket, bool verbose) - { - var connection = new TcpConnection(connectionId, remoteEndPoint, verbose); - connection.InitSocket(socket); - connection.InitSendReceive(); - return connection; - } - - public event Action ConnectionClosed; - - public Guid ConnectionId - { - get { return _connectionId; } - } - - public int SendQueueSize - { - get { return _sendQueue.Count; } - } - - public string ClientConnectionName - { - get { return _clientConnectionName; } - } - - private readonly Guid _connectionId; - private readonly bool _verbose; - private string _clientConnectionName; - - private Socket _socket; - private SocketAsyncEventArgs _receiveSocketArgs; - private SocketAsyncEventArgs _sendSocketArgs; - - private readonly ConcurrentQueueWrapper> _sendQueue = - new ConcurrentQueueWrapper>(); - - private readonly Queue _receiveQueue = new Queue(); - private readonly MemoryStream _memoryStream = new MemoryStream(); - private long _memoryStreamOffset = 0L; - - private readonly object _receivingLock = new object(); - private readonly object _sendLock = new object(); - private readonly object _closeLock = new object(); - private bool _isSending; - private volatile bool _isClosed; - private volatile bool _isClosing; - - private Action>> _receiveCallback; - - private TcpConnection(Guid connectionId, IPEndPoint remoteEndPoint, bool verbose) : base(remoteEndPoint) - { - Ensure.NotEmptyGuid(connectionId, "connectionId"); - - _connectionId = connectionId; - _verbose = verbose; - } - - private void InitSocket(Socket socket) - { - _socket = socket; - } - - private void InitSendReceive() - { - InitConnectionBase(_socket); - lock (_sendLock) - { - try - { - _socket.NoDelay = true; - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.Shutdown, "Socket disposed."); - return; - } - catch (SocketException) - { - CloseInternal(SocketError.Shutdown, "Socket is disposed."); - return; - } - - var receiveSocketArgs = SocketArgsPool.Get(); - _receiveSocketArgs = receiveSocketArgs; - _receiveSocketArgs.AcceptSocket = _socket; - _receiveSocketArgs.Completed += OnReceiveAsyncCompleted; - - var sendSocketArgs = SocketArgsPool.Get(); - _sendSocketArgs = sendSocketArgs; - _sendSocketArgs.AcceptSocket = _socket; - _sendSocketArgs.Completed += OnSendAsyncCompleted; - } - - StartReceive(); - TrySend(); - } - - public void EnqueueSend(IEnumerable> data) - { - lock (_sendLock) - { - int bytes = 0; - foreach (var segment in data) - { - _sendQueue.Enqueue(segment); - bytes += segment.Count; - } - - NotifySendScheduled(bytes); - } - - TrySend(); - } - - private void TrySend() - { - bool continueSendSynchronously; - try - { - do - { - lock (_sendLock) - { - if (_isSending || (_sendQueue.IsEmpty && _memoryStreamOffset >= _memoryStream.Length) || _sendSocketArgs == null) - { - return; - } - - if (TcpConnectionMonitor.Default.IsSendBlocked()) - { - return; - } - - _isSending = true; - } - - if (_memoryStreamOffset >= _memoryStream.Length) - { - _memoryStream.SetLength(0); - _memoryStreamOffset = 0L; - - ArraySegment sendPiece; - while (_sendQueue.TryDequeue(out sendPiece)) - { - _memoryStream.Write(sendPiece.Array, sendPiece.Offset, sendPiece.Count); - if (_memoryStream.Length >= MaxSendPacketSize) - { - break; - } - } - } - - int sendingBytes = Math.Min((int)_memoryStream.Length - (int)_memoryStreamOffset, MaxSendPacketSize); - - _sendSocketArgs.SetBuffer(_memoryStream.GetBuffer(), (int)_memoryStreamOffset, sendingBytes); - _memoryStreamOffset += sendingBytes; - - NotifySendStarting(_sendSocketArgs.Count); - var firedAsync = _sendSocketArgs.AcceptSocket.SendAsync(_sendSocketArgs); - if (firedAsync) - { - continueSendSynchronously = false; - } - else - { - continueSendSynchronously = ProcessSend(_sendSocketArgs); - } - } while (continueSendSynchronously); - } - catch (ObjectDisposedException) - { - ReturnSendingSocketArgs(); - } - } - - private void OnSendAsyncCompleted(object sender, SocketAsyncEventArgs e) - { - if (ProcessSend(e)) - { - TrySend(); - } - } - - private bool ProcessSend(SocketAsyncEventArgs socketArgs) - { - if (socketArgs.SocketError != SocketError.Success) - { - NotifySendCompleted(0); - ReturnSendingSocketArgs(); - CloseInternal(socketArgs.SocketError, "Socket send error."); - return false; - } - else - { - NotifySendCompleted(socketArgs.Count); - - if (_isClosed) - { - ReturnSendingSocketArgs(); - return false; - } - else - { - lock (_sendLock) - { - _isSending = false; - } - - return true; - } - } - } - - public void ReceiveAsync(Action>> callback) - { - if (callback == null) - { - throw new ArgumentNullException("callback"); - } - - lock (_receivingLock) - { - if (_receiveCallback != null) - { - Log.Fatal("ReceiveAsync called again while previous call was not fulfilled"); - throw new InvalidOperationException( - "ReceiveAsync called again while previous call was not fulfilled"); - } - - _receiveCallback = callback; - } - - TryDequeueReceivedData(); - } - - private void StartReceive() - { - try - { - bool continueReceiveSynchronously = true; - - do - { - var buffer = BufferManager.CheckOut(); - if (buffer.Array == null || buffer.Count == 0 || buffer.Array.Length < buffer.Offset + buffer.Count) - { - throw new Exception("Invalid buffer allocated"); - } - // TODO AN: do we need to lock on _receiveSocketArgs?.. - lock (_receiveSocketArgs) - { - _receiveSocketArgs.SetBuffer(buffer.Array, buffer.Offset, buffer.Count); - if (_receiveSocketArgs.Buffer == null) - { - throw new Exception("Buffer was not set"); - } - } - - NotifyReceiveStarting(); - bool firedAsync; - lock (_receiveSocketArgs) - { - if (_receiveSocketArgs.Buffer == null) - { - throw new Exception("Buffer was lost"); - } - - firedAsync = _receiveSocketArgs.AcceptSocket.ReceiveAsync(_receiveSocketArgs); - } - - if (firedAsync) - { - continueReceiveSynchronously = false; - } - else - { - var processReceiveSuccess = ProcessReceive(_receiveSocketArgs); - if (processReceiveSuccess) - { - TryDequeueReceivedData(); - } - - continueReceiveSynchronously = processReceiveSuccess; - } - } while (continueReceiveSynchronously); - } - catch (ObjectDisposedException) - { - ReturnReceivingSocketArgs(); - } - } - - private void OnReceiveAsyncCompleted(object sender, SocketAsyncEventArgs e) - { - if (ProcessReceive(e)) - { - TryDequeueReceivedData(); - StartReceive(); - } - } - - private bool ProcessReceive(SocketAsyncEventArgs socketArgs) - { - // socket closed normally or some error occurred - if (socketArgs.BytesTransferred == 0 || socketArgs.SocketError != SocketError.Success) - { - NotifyReceiveCompleted(0); - ReturnReceivingSocketArgs(); - CloseInternal(socketArgs.SocketError, - socketArgs.SocketError != SocketError.Success ? "Socket receive error" : "Socket closed"); - return false; - } - - NotifyReceiveCompleted(socketArgs.BytesTransferred); - - lock (_receivingLock) - { - var buf = new ArraySegment(socketArgs.Buffer, socketArgs.Offset, socketArgs.Count); - _receiveQueue.Enqueue(new ReceivedData(buf, socketArgs.BytesTransferred)); - } - - lock (_receiveSocketArgs) - { - if (socketArgs.Buffer == null) - { - throw new Exception("Cleaning already null buffer"); - } - - socketArgs.SetBuffer(null, 0, 0); - } - - return true; - } - - private void TryDequeueReceivedData() - { - Action>> callback; - List res; - lock (_receivingLock) - { - // no awaiting callback or no data to dequeue - if (_receiveCallback == null || _receiveQueue.Count == 0) - { - return; - } - - res = new List(_receiveQueue.Count); - while (_receiveQueue.Count > 0) - { - res.Add(_receiveQueue.Dequeue()); - } - - callback = _receiveCallback; - _receiveCallback = null; - } - - var data = new ArraySegment[res.Count]; - int bytes = 0; - for (int i = 0; i < data.Length; ++i) - { - var d = res[i]; - bytes += d.DataLen; - data[i] = new ArraySegment(d.Buf.Array, d.Buf.Offset, d.DataLen); - } - - lock (_closeLock) - { - if (!_isClosed) - { - callback(this, data); - } - } - - for (int i = 0, n = res.Count; i < n; ++i) - { - BufferManager.CheckIn(res[i].Buf); // dispose buffers - } - - NotifyReceiveDispatched(bytes); - } - - public void Close(string reason) - { - CloseInternal(SocketError.Success, reason ?? "Normal socket close."); // normal socket closing - } - - private void CloseInternal(SocketError socketError, string reason) - { - lock (_closeLock) - { - if (_isClosing) - { - return; - } - - _isClosing = true; - } - - if (_socket != null) - { - Helper.EatException(() => _socket.Shutdown(SocketShutdown.Both)); - Helper.EatException(() => _socket.Close()); - } - - lock (_closeLock) - { - _isClosed = true; - } - - NotifyClosed(); - - if (_verbose) - { - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Received bytes: {totalBytesReceived}, Sent bytes: {totalBytesSent}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - TotalBytesReceived, TotalBytesSent); - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Send calls: {sendCalls}, callbacks: {sendCallbacks}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - SendCalls, SendCallbacks); - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Receive calls: {receiveCalls}, callbacks: {receiveCallbacks}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - ReceiveCalls, ReceiveCallbacks); - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Close reason: [{socketError}] {reason}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - socketError, reason); - } - - lock (_sendLock) - { - if (!_isSending) - { - ReturnSendingSocketArgs(); - } - } - - var handler = ConnectionClosed; - if (handler != null) - { - handler(this, socketError); - } - } - - private void ReturnSendingSocketArgs() - { - var socketArgs = Interlocked.Exchange(ref _sendSocketArgs, null); - if (socketArgs != null) - { - socketArgs.Completed -= OnSendAsyncCompleted; - socketArgs.AcceptSocket = null; - if (socketArgs.Buffer != null) - { - socketArgs.SetBuffer(null, 0, 0); - } - - SocketArgsPool.Return(socketArgs); - } - } - - private void ReturnReceivingSocketArgs() - { - var socketArgs = Interlocked.Exchange(ref _receiveSocketArgs, null); - if (socketArgs != null) - { - socketArgs.Completed -= OnReceiveAsyncCompleted; - socketArgs.AcceptSocket = null; - if (socketArgs.Buffer != null) - { - BufferManager.CheckIn( - new ArraySegment(socketArgs.Buffer, socketArgs.Offset, socketArgs.Count)); - socketArgs.SetBuffer(null, 0, 0); - } - - SocketArgsPool.Return(socketArgs); - } - } - - public void SetClientConnectionName(string clientConnectionName) - { - _clientConnectionName = clientConnectionName; - } - - public override string ToString() - { - return RemoteEndPoint.ToString(); - } - - private struct ReceivedData - { - public readonly ArraySegment Buf; - public readonly int DataLen; - - public ReceivedData(ArraySegment buf, int dataLen) - { - Buf = buf; - DataLen = dataLen; - } - } -} diff --git a/src/EventStore.Transport.Tcp/TcpConnectionBase.cs b/src/EventStore.Transport.Tcp/TcpConnectionBase.cs deleted file mode 100644 index 2b9f7d41b6..0000000000 --- a/src/EventStore.Transport.Tcp/TcpConnectionBase.cs +++ /dev/null @@ -1,240 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading; -using EventStore.Common.Utils; - -namespace EventStore.Transport.Tcp; - -public class TcpConnectionBase : IMonitoredTcpConnection -{ - public IPEndPoint RemoteEndPoint - { - get { return _remoteEndPoint; } - } - - public IPEndPoint LocalEndPoint - { - get { return _localEndPoint; } - } - - public bool IsInitialized - { - get { return _socket != null; } - } - - public bool IsClosed - { - get { return _isClosed; } - } - - public bool InSend - { - get { return Interlocked.Read(ref _lastSendStarted) >= 0; } - } - - public bool InReceive - { - get { return Interlocked.Read(ref _lastReceiveStarted) >= 0; } - } - - public int PendingSendBytes - { - get { return _pendingSendBytes; } - } - - public int InSendBytes - { - get { return _inSendBytes; } - } - - public int PendingReceivedBytes - { - get { return _pendingReceivedBytes; } - } - - public long TotalBytesSent - { - get { return Interlocked.Read(ref _totalBytesSent); } - } - - public long TotalBytesReceived - { - get { return Interlocked.Read(ref _totalBytesReceived); } - } - - public int SendCalls - { - get { return _sentAsyncs; } - } - - public int SendCallbacks - { - get { return _sentAsyncCallbacks; } - } - - public int ReceiveCalls - { - get { return _recvAsyncs; } - } - - public int ReceiveCallbacks - { - get { return _recvAsyncCallbacks; } - } - - public bool IsReadyForSend - { - get - { - try - { - return !_isClosed && _socket.Poll(0, SelectMode.SelectWrite); - } - catch (ObjectDisposedException) - { - //TODO: why do we get this? - return false; - } - } - } - - public bool IsReadyForReceive - { - get - { - try - { - return !_isClosed && _socket.Poll(0, SelectMode.SelectRead); - } - catch (ObjectDisposedException) - { - //TODO: why do we get this? - return false; - } - } - } - - public bool IsFaulted - { - get - { - try - { - return !_isClosed && _socket.Poll(0, SelectMode.SelectError); - } - catch (ObjectDisposedException) - { - //TODO: why do we get this? - return false; - } - } - } - - public DateTime? LastSendStarted - { - get - { - var ticks = Interlocked.Read(ref _lastSendStarted); - return ticks >= 0 ? new DateTime(ticks) : (DateTime?)null; - } - } - - public DateTime? LastReceiveStarted - { - get - { - var ticks = Interlocked.Read(ref _lastReceiveStarted); - return ticks >= 0 ? new DateTime(ticks) : (DateTime?)null; - } - } - - private Socket _socket; - protected readonly IPEndPoint _remoteEndPoint; - private IPEndPoint _localEndPoint; - - private long _lastSendStarted = -1; - private long _lastReceiveStarted = -1; - private bool _isClosed; - - private int _pendingSendBytes; - private int _inSendBytes; - private int _pendingReceivedBytes; - private long _totalBytesSent; - private long _totalBytesReceived; - - private int _sentAsyncs; - private int _sentAsyncCallbacks; - private int _recvAsyncs; - private int _recvAsyncCallbacks; - - public TcpConnectionBase(IPEndPoint remoteEndPoint) - { - Ensure.NotNull(remoteEndPoint, "remoteEndPoint"); - _remoteEndPoint = remoteEndPoint; - - TcpConnectionMonitor.Default.Register(this); - } - - protected void InitConnectionBase(Socket socket) - { - Ensure.NotNull(socket, "socket"); - - _socket = socket; - _localEndPoint = Helper.EatException(() => (IPEndPoint)socket.LocalEndPoint); - } - - protected void NotifySendScheduled(int bytes) - { - Interlocked.Add(ref _pendingSendBytes, bytes); - } - - protected void NotifySendStarting(int bytes) - { - if (Interlocked.CompareExchange(ref _lastSendStarted, DateTime.UtcNow.Ticks, -1) != -1) - { - throw new Exception("Concurrent send detected."); - } - - Interlocked.Add(ref _pendingSendBytes, -bytes); - Interlocked.Add(ref _inSendBytes, bytes); - Interlocked.Increment(ref _sentAsyncs); - } - - protected void NotifySendCompleted(int bytes) - { - Interlocked.Exchange(ref _lastSendStarted, -1); - Interlocked.Add(ref _inSendBytes, -bytes); - Interlocked.Add(ref _totalBytesSent, bytes); - Interlocked.Increment(ref _sentAsyncCallbacks); - } - - protected void NotifyReceiveStarting() - { - if (Interlocked.CompareExchange(ref _lastReceiveStarted, DateTime.UtcNow.Ticks, -1) != -1) - { - throw new Exception("Concurrent receive detected."); - } - - Interlocked.Increment(ref _recvAsyncs); - } - - protected void NotifyReceiveCompleted(int bytes) - { - Interlocked.Exchange(ref _lastReceiveStarted, -1); - Interlocked.Add(ref _pendingReceivedBytes, bytes); - Interlocked.Add(ref _totalBytesReceived, bytes); - Interlocked.Increment(ref _recvAsyncCallbacks); - } - - protected void NotifyReceiveDispatched(int bytes) - { - Interlocked.Add(ref _pendingReceivedBytes, -bytes); - } - - protected void NotifyClosed() - { - _isClosed = true; - TcpConnectionMonitor.Default.Unregister(this); - } -} diff --git a/src/EventStore.Transport.Tcp/TcpConnectionMonitor.cs b/src/EventStore.Transport.Tcp/TcpConnectionMonitor.cs deleted file mode 100644 index a4a0b67323..0000000000 --- a/src/EventStore.Transport.Tcp/TcpConnectionMonitor.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Linq; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp; - -public class TcpConnectionMonitor -{ - public static readonly TcpConnectionMonitor Default = new TcpConnectionMonitor(); - private static readonly ILogger Log = Serilog.Log.ForContext(); - - private readonly object _statsLock = new object(); - - private readonly ConcurrentDictionary _connections = - new ConcurrentDictionary(); - - private long _sentTotal; - private long _receivedTotal; - private long _sentSinceLastRun; - private long _receivedSinceLastRun; - private long _pendingSendOnLastRun; - private long _inSendOnLastRun; - private long _pendingReceivedOnLastRun; - - private bool _anySendBlockedOnLastRun; - private DateTime _lastUpdateTime; - - private TcpConnectionMonitor() - { - } - - public void Register(IMonitoredTcpConnection connection) - { - _connections.TryAdd(connection, new ConnectionData(connection)); - } - - public void Unregister(IMonitoredTcpConnection connection) - { - _connections.TryRemove(connection, out _); - } - - public TcpStats GetTcpStats() - { - ConnectionData[] connections = _connections.Values.ToArray(); - lock (_statsLock) - { - var stats = AnalyzeConnections(connections, DateTime.UtcNow - _lastUpdateTime); - _lastUpdateTime = DateTime.UtcNow; - return stats; - } - } - - public IMonitoredTcpConnection[] GetTcpConnectionStats() - { - GetTcpStats(); - var monitoredConnections = _connections.Values.Select(conn => conn.Connection).ToArray(); - return monitoredConnections; - } - - private TcpStats AnalyzeConnections(ConnectionData[] connections, TimeSpan measurePeriod) - { - _receivedSinceLastRun = 0; - _sentSinceLastRun = 0; - _pendingSendOnLastRun = 0; - _inSendOnLastRun = 0; - _pendingReceivedOnLastRun = 0; - _anySendBlockedOnLastRun = false; - - foreach (var connection in connections) - { - AnalyzeConnection(connection); - } - - var stats = new TcpStats(connections.Length, - _sentTotal, - _receivedTotal, - _sentSinceLastRun, - _receivedSinceLastRun, - _pendingSendOnLastRun, - _inSendOnLastRun, - _pendingReceivedOnLastRun, - measurePeriod); - - return stats; - } - - private void AnalyzeConnection(ConnectionData connectionData) - { - var connection = connectionData.Connection; - if (!connection.IsInitialized) - { - return; - } - - if (connection.IsFaulted) - { - Log.Information("# {connection} is faulted", connection); - return; - } - - UpdateStatistics(connectionData); - - CheckPendingReceived(connection); - CheckPendingSend(connection); - CheckMissingSendCallback(connectionData, connection); - CheckMissingReceiveCallback(connectionData, connection); - } - - private void UpdateStatistics(ConnectionData connectionData) - { - var connection = connectionData.Connection; - long totalBytesSent = connection.TotalBytesSent; - long totalBytesReceived = connection.TotalBytesReceived; - long pendingSend = connection.PendingSendBytes; - long inSend = connection.InSendBytes; - long pendingReceived = connection.PendingReceivedBytes; - - _sentSinceLastRun += totalBytesSent - connectionData.LastTotalBytesSent; - _receivedSinceLastRun += totalBytesReceived - connectionData.LastTotalBytesReceived; - - _sentTotal += _sentSinceLastRun; - _receivedTotal += _receivedSinceLastRun; - - _pendingSendOnLastRun += pendingSend; - _inSendOnLastRun += inSend; - _pendingReceivedOnLastRun = pendingReceived; - - connectionData.LastTotalBytesSent = totalBytesSent; - connectionData.LastTotalBytesReceived = totalBytesReceived; - } - - private static void CheckMissingReceiveCallback(ConnectionData connectionData, - IMonitoredTcpConnection connection) - { - bool inReceive = connection.InReceive; - bool isReadyForReceive = connection.IsReadyForReceive; - DateTime? lastReceiveStarted = connection.LastReceiveStarted; - - int sinceLastReceive = (int)(DateTime.UtcNow - lastReceiveStarted.GetValueOrDefault()).TotalMilliseconds; - bool missingReceiveCallback = inReceive && isReadyForReceive && sinceLastReceive > 500; - - if (missingReceiveCallback && connectionData.LastMissingReceiveCallBack) - { - Log.Error( - "# {connection} {sinceLastReceive}ms since last Receive started. No completion callback received, but socket status is READY_FOR_RECEIVE", - connection, sinceLastReceive); - } - - connectionData.LastMissingReceiveCallBack = missingReceiveCallback; - } - - private void CheckMissingSendCallback(ConnectionData connectionData, IMonitoredTcpConnection connection) - { - // snapshot all data? - bool inSend = connection.InSend; - bool isReadyForSend = connection.IsReadyForSend; - DateTime? lastSendStarted = connection.LastSendStarted; - int inSendBytes = connection.InSendBytes; - - int sinceLastSend = (int)(DateTime.UtcNow - lastSendStarted.GetValueOrDefault()).TotalMilliseconds; - bool missingSendCallback = inSend && isReadyForSend && sinceLastSend > 500; - - if (missingSendCallback && connectionData.LastMissingSendCallBack) - { - // _anySendBlockedOnLastRun = true; - Log.Error( - "# {connection} {sinceLastSend}ms since last send started. No completion callback received, but socket status is READY_FOR_SEND. In send: {inSendBytes}", - connection, sinceLastSend, inSendBytes); - } - - connectionData.LastMissingSendCallBack = missingSendCallback; - } - - private static void CheckPendingSend(IMonitoredTcpConnection connection) - { - int pendingSendBytes = connection.PendingSendBytes; - if (pendingSendBytes > 128 * 1024) - { - Log.Information("# {connection} {pendingSendKiloBytes}kb pending send", connection, pendingSendBytes / 1024); - } - } - - private static void CheckPendingReceived(IMonitoredTcpConnection connection) - { - int pendingReceivedBytes = connection.PendingReceivedBytes; - if (pendingReceivedBytes > 128 * 1024) - { - Log.Information("# {connection} {pendingReceivedKiloBytes}kb are not dispatched", connection, - pendingReceivedBytes / 1024); - } - } - - public bool IsSendBlocked() - { - return _anySendBlockedOnLastRun; - } - - private class ConnectionData - { - public readonly IMonitoredTcpConnection Connection; - public bool LastMissingSendCallBack; - public bool LastMissingReceiveCallBack; - public long LastTotalBytesSent; - public long LastTotalBytesReceived; - - public ConnectionData(IMonitoredTcpConnection connection) - { - Connection = connection; - } - } -} diff --git a/src/EventStore.Transport.Tcp/TcpConnectionSsl.cs b/src/EventStore.Transport.Tcp/TcpConnectionSsl.cs deleted file mode 100644 index ad7d9135c0..0000000000 --- a/src/EventStore.Transport.Tcp/TcpConnectionSsl.cs +++ /dev/null @@ -1,763 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Security; -using System.Net.Sockets; -using System.Security.Authentication; -using System.Security.Cryptography.X509Certificates; -using System.Threading; -using System.Threading.Tasks; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp; - -public class TcpConnectionSsl : TcpConnectionBase, ITcpConnection -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - - public static ITcpConnection CreateConnectingConnection(Guid connectionId, - string targetHost, - string[] otherNames, - IPEndPoint remoteEndPoint, - CertificateDelegates.ServerCertificateValidator serverCertValidator, - Func clientCertificatesSelector, - TcpClientConnector connector, - TimeSpan connectionTimeout, - Action onConnectionEstablished, - Action onConnectionFailed, - bool verbose) - { - var connection = new TcpConnectionSsl(connectionId, remoteEndPoint, verbose); - // ReSharper disable ImplicitlyCapturedClosure - connector.InitConnect(remoteEndPoint, - (socket) => - { - connection.InitClientSocket(socket); - }, - (_, socket) => - { - connection.InitSslStream(targetHost, otherNames, serverCertValidator, clientCertificatesSelector, verbose); - onConnectionEstablished?.Invoke(connection); - }, - (_, socketError) => - { - onConnectionFailed?.Invoke(connection, socketError); - }, connection, connectionTimeout); - // ReSharper restore ImplicitlyCapturedClosure - return connection; - } - - public static ITcpConnection CreateServerFromSocket(Guid connectionId, - IPEndPoint remoteEndPoint, - Socket socket, - Func serverCertificateSelector, - Func intermediatesSelector, - CertificateDelegates.ClientCertificateValidator clientCertValidator, - bool verbose) - { - var connection = new TcpConnectionSsl(connectionId, remoteEndPoint, verbose); - connection.InitServerSocket(socket, serverCertificateSelector, intermediatesSelector, clientCertValidator, verbose); - return connection; - } - - public event Action ConnectionClosed; - - public Guid ConnectionId - { - get { return _connectionId; } - } - - public int SendQueueSize - { - get { return _sendQueue.Count; } - } - - public string ClientConnectionName - { - get { return _clientConnectionName; } - } - - private readonly Guid _connectionId; - private readonly bool _verbose; - public string _clientConnectionName; - - private Socket _socket; - - private readonly ConcurrentQueueWrapper> _sendQueue = new(); - - private readonly ConcurrentQueueWrapper - _receiveQueue = new(); - - private readonly MemoryStream _memoryStream = new(); - private long _memoryStreamOffset; - - private readonly object _streamLock = new(); - private readonly object _closeLock = new(); - private bool _isSending; - private int _receiveHandling; - private volatile bool _isClosed; - private volatile bool _isClosing; - - private Action>> _receiveCallback; - - private SslStream _sslStream; - private bool _isAuthenticated; - private int _sendingBytes; - private CertificateDelegates.ServerCertificateValidator _serverCertValidator; - private CertificateDelegates.ClientCertificateValidator _clientCertValidator; - private string[] _otherNames; - private readonly byte[] _receiveBuffer = new byte[TcpConnection.BufferManager.ChunkSize]; - - private TcpConnectionSsl(Guid connectionId, IPEndPoint remoteEndPoint, bool verbose) : base(remoteEndPoint) - { - Ensure.NotEmptyGuid(connectionId, "connectionId"); - - _connectionId = connectionId; - _verbose = verbose; - } - - private void InitServerSocket( - Socket socket, - Func serverCertificateSelector, - Func intermediatesSelector, - CertificateDelegates.ClientCertificateValidator clientCertValidator, - bool verbose) - { - InitConnectionBase(socket); - if (verbose) - { - Console.WriteLine("TcpConnectionSsl::InitClientSocket({0}, L{1})", RemoteEndPoint, LocalEndPoint); - } - - _clientCertValidator = clientCertValidator; - - lock (_streamLock) - { - try - { - socket.NoDelay = true; - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.Shutdown, "Socket is disposed."); - return; - } - catch (SocketException) - { - CloseInternal(SocketError.Shutdown, "Socket is disposed."); - return; - } - - try - { - _sslStream = new SslStream(new NetworkStream(socket, true), false); - } - catch (IOException exc) - { - Log.Debug(exc, "[S{remoteEndPoint}, L{localEndPoint}]: IOException on NetworkStream. The socket has already been disposed.", RemoteEndPoint, - LocalEndPoint); - return; - } - - Task.Run(async () => await AuthenticateAsServerAsync(serverCertificateSelector, intermediatesSelector)); - } - } - - private async Task AuthenticateAsServerAsync( - Func serverCertificateSelector, - Func intermediatesSelector) - { - try - { - var enabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - var certificate = serverCertificateSelector?.Invoke(); - var intermediates = intermediatesSelector?.Invoke(); - Ensure.NotNull(certificate, "certificate"); - - await _sslStream.AuthenticateAsServerAsync(new SslServerAuthenticationOptions - { - ServerCertificateContext = SslStreamCertificateContext.Create( - certificate!, intermediates, offline: true), - ClientCertificateRequired = true, - EnabledSslProtocols = enabledSslProtocols, - CertificateRevocationCheckMode = X509RevocationMode.NoCheck, - RemoteCertificateValidationCallback = ValidateClientCertificate, - ApplicationProtocols = [], - AllowRenegotiation = false, - }); - - lock (_streamLock) - { - if (_verbose) - { - DisplaySslStreamInfo(_sslStream); - } - - _isAuthenticated = true; - } - } - catch (AuthenticationException exc) - { - Log.Information(exc, - "[S{remoteEndPoint}, L{localEndPoint}]: Authentication exception on AuthenticateAsServerAsync.", - RemoteEndPoint, LocalEndPoint); - CloseInternal(SocketError.SocketError, exc.Message); - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.SocketError, "SslStream disposed."); - } - catch (Exception exc) - { - Log.Information(exc, - "[S{remoteEndPoint}, L{localEndPoint}]: Exception on AuthenticateAsServerAsync.", - RemoteEndPoint, LocalEndPoint); - CloseInternal(SocketError.SocketError, exc.Message); - } - - StartReceive(); - TrySend(); - } - - private void InitClientSocket(Socket socket) => - _socket = socket; - - private void InitSslStream(string targetHost, string[] otherNames, CertificateDelegates.ServerCertificateValidator serverCertValidator, Func clientCertificatesSelector, bool verbose) - { - Ensure.NotNull(targetHost, "targetHost"); - InitConnectionBase(_socket); - if (verbose) - { - Console.WriteLine("TcpConnectionSsl::InitClientSslStream({0}, L{1})", RemoteEndPoint, LocalEndPoint); - } - - _serverCertValidator = serverCertValidator; - _otherNames = otherNames; - - lock (_streamLock) - { - try - { - _socket.NoDelay = true; - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.Shutdown, "Socket is disposed."); - return; - } - catch (SocketException) - { - CloseInternal(SocketError.Shutdown, "Socket is disposed."); - return; - } - - try - { - _sslStream = new SslStream(new NetworkStream(_socket, true), false, ValidateServerCertificate, null); - } - catch (IOException exc) - { - Log.Debug(exc, "[S{remoteEndPoint}, L{localEndPoint}]: IOException on NetworkStream. The socket has already been disposed.", RemoteEndPoint, - LocalEndPoint); - return; - } - - try - { - var enabledSslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - var clientCertificates = clientCertificatesSelector?.Invoke(); - _sslStream.BeginAuthenticateAsClient(targetHost, clientCertificates, enabledSslProtocols, false, OnEndAuthenticateAsClient, _sslStream); - } - catch (AuthenticationException exc) - { - Log.Information(exc, - "[S{remoteEndPoint}, L{localEndPoint}]: Authentication exception on BeginAuthenticateAsClient.", - RemoteEndPoint, LocalEndPoint); - CloseInternal(SocketError.SocketError, exc.Message); - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.SocketError, "SslStream disposed."); - } - catch (Exception exc) - { - Log.Information(exc, - "[S{remoteEndPoint}, {localEndPoint}]: Exception on BeginAuthenticateAsClient.", RemoteEndPoint, - LocalEndPoint); - CloseInternal(SocketError.SocketError, exc.Message); - } - } - } - - private void OnEndAuthenticateAsClient(IAsyncResult ar) - { - try - { - lock (_streamLock) - { - var sslStream = (SslStream)ar.AsyncState; - sslStream.EndAuthenticateAsClient(ar); - if (_verbose) - { - DisplaySslStreamInfo(sslStream); - } - - _isAuthenticated = true; - } - - StartReceive(); - TrySend(); - } - catch (AuthenticationException exc) - { - Log.Information(exc, - "[S{remoteEndPoint}, L{localEndPoint}]: Authentication exception on EndAuthenticateAsClient.", - RemoteEndPoint, LocalEndPoint); - CloseInternal(SocketError.SocketError, exc.Message); - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.SocketError, "SslStream disposed."); - } - catch (Exception exc) - { - Log.Information(exc, "[S{remoteEndPoint}, L{localEndPoint}]: Exception on EndAuthenticateAsClient.", - RemoteEndPoint, LocalEndPoint); - CloseInternal(SocketError.SocketError, exc.Message); - } - } - - // The following method is invoked by the RemoteCertificateValidationDelegate. - public bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, - SslPolicyErrors sslPolicyErrors) - { - var (isValid, error) = _serverCertValidator(certificate, chain, sslPolicyErrors, _otherNames); - if (!isValid && error != null) - { - Log.Error("Server certificate validation error: {e}", error); - } - return isValid; - } - - public bool ValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, - SslPolicyErrors sslPolicyErrors) - { - var (isValid, error) = _clientCertValidator(certificate, chain, sslPolicyErrors); - if (!isValid && error != null) - { - Log.Error("Client certificate validation error: {e}", error); - } - return isValid; - } - - private void DisplaySslStreamInfo(SslStream stream) - { - Log.Information("[S{remoteEndPoint}, L{localEndPoint}]", RemoteEndPoint, LocalEndPoint); - try - { - Log.Verbose("Cipher suite: {cipherSuite}", stream.NegotiatedCipherSuite); - } - catch (NotImplementedException) - { - } - - Log.Information("Protocol: {sslProtocol}", stream.SslProtocol); - Log.Information("Is authenticated: {isAuthenticated} as server? {isServer}", stream.IsAuthenticated, - stream.IsServer); - Log.Information("IsSigned: {isSigned}", stream.IsSigned); - Log.Information("Is Encrypted: {isEncrypted}", stream.IsEncrypted); - Log.Information("Can read: {canRead}, write {canWrite}", stream.CanRead, stream.CanWrite); - Log.Information("Can timeout: {canTimeout}", stream.CanTimeout); - try - { - Log.Information("Certificate revocation list checked: {checkCertRevocationStatus}", - stream.CheckCertRevocationStatus); - } - catch (NotImplementedException) - { - } - - X509Certificate localCert = stream.LocalCertificate; - if (localCert != null) - { - Log.Information( - "Local certificate was issued to {subject} and is valid from {effectiveDate} until {expirationDate}.", - localCert.Subject, localCert.GetEffectiveDateString(), localCert.GetExpirationDateString()); - } - else - { - Log.Information("Local certificate is null."); - } - - // Display the properties of the client's certificate. - X509Certificate remoteCert = stream.RemoteCertificate; - if (remoteCert != null) - { - Log.Information( - "Remote certificate was issued to {subject} and is valid from {remoteCertEffectiveDate} until {remoteCertExpirationDate}.", - remoteCert.Subject, remoteCert.GetEffectiveDateString(), remoteCert.GetExpirationDateString()); - } - else - { - Log.Information("Remote certificate is null."); - } - } - - public void EnqueueSend(IEnumerable> data) - { - lock (_streamLock) - { - int bytes = 0; - foreach (var segment in data) - { - _sendQueue.Enqueue(segment); - bytes += segment.Count; - } - - NotifySendScheduled(bytes); - } - - TrySend(); - } - - private void TrySend() - { - bool continueSendSynchronously = true; - try - { - do - { - lock (_streamLock) - { - if (_isSending || (_sendQueue.IsEmpty && _memoryStreamOffset >= _memoryStream.Length) || _sslStream == null || !_isAuthenticated) - { - return; - } - - if (TcpConnectionMonitor.Default.IsSendBlocked()) - { - return; - } - - _isSending = true; - } - - if (_memoryStreamOffset >= _memoryStream.Length) - { - _memoryStream.SetLength(0); - _memoryStreamOffset = 0L; - - ArraySegment sendPiece; - while (_sendQueue.TryDequeue(out sendPiece)) - { - _memoryStream.Write(sendPiece.Array, sendPiece.Offset, sendPiece.Count); - if (_memoryStream.Length >= TcpConnection.MaxSendPacketSize) - { - break; - } - } - } - - _sendingBytes = Math.Min((int)_memoryStream.Length - (int)_memoryStreamOffset, TcpConnection.MaxSendPacketSize); - - NotifySendStarting(_sendingBytes); - var result = _sslStream.BeginWrite(_memoryStream.GetBuffer(), (int)_memoryStreamOffset, _sendingBytes, OnEndWrite, null); - _memoryStreamOffset += _sendingBytes; - continueSendSynchronously = result.CompletedSynchronously; - if (continueSendSynchronously) - { - EndWrite(result); - } - } while (continueSendSynchronously); - } - catch (SocketException exc) - { - Log.Debug(exc, "SocketException '{e}' during BeginWrite.", exc.SocketErrorCode); - CloseInternal(exc.SocketErrorCode, "SocketException during BeginWrite."); - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.SocketError, "SslStream disposed."); - } - catch (Exception exc) - { - Log.Debug(exc, "Exception during BeginWrite."); - CloseInternal(SocketError.SocketError, "Exception during BeginWrite"); - } - } - - private void OnEndWrite(IAsyncResult ar) - { - if (ar.CompletedSynchronously) - { - return; - } - - EndWrite(ar); - TrySend(); - } - - private void EndWrite(IAsyncResult ar) - { - try - { - _sslStream.EndWrite(ar); - NotifySendCompleted(_sendingBytes); - - lock (_streamLock) - { - _isSending = false; - } - } - catch (SocketException exc) - { - Log.Debug(exc, "SocketException '{e}' during EndWrite.", exc.SocketErrorCode); - NotifySendCompleted(0); - CloseInternal(exc.SocketErrorCode, "SocketException during EndWrite."); - } - catch (ObjectDisposedException) - { - NotifySendCompleted(0); - CloseInternal(SocketError.SocketError, "SslStream disposed."); - } - catch (Exception exc) - { - Log.Debug(exc, "Exception during EndWrite."); - NotifySendCompleted(0); - CloseInternal(SocketError.SocketError, "Exception during EndWrite."); - } - } - - public void ReceiveAsync(Action>> callback) - { - Ensure.NotNull(callback, "callback"); - - if (Interlocked.Exchange(ref _receiveCallback, callback) != null) - { - Log.Fatal("ReceiveAsync called again while previous call wasn't fulfilled"); - throw new InvalidOperationException("ReceiveAsync called again while previous call wasn't fulfilled"); - } - - TryDequeueReceivedData(); - } - - private void StartReceive() - { - try - { - bool continueReceiveSynchronously = true; - - do - { - NotifyReceiveStarting(); - var result = _sslStream.BeginRead(_receiveBuffer, 0, _receiveBuffer.Length, OnEndRead, null); - continueReceiveSynchronously = result.CompletedSynchronously; - if (continueReceiveSynchronously) - { - EndRead(result); - } - } while (continueReceiveSynchronously); - } - catch (SocketException exc) - { - Log.Debug(exc, "SocketException '{e}' during BeginRead.", exc.SocketErrorCode); - CloseInternal(exc.SocketErrorCode, "SocketException during BeginRead."); - } - catch (ObjectDisposedException) - { - CloseInternal(SocketError.SocketError, "SslStream disposed."); - } - catch (Exception exc) - { - Log.Debug(exc, "Exception during BeginRead."); - CloseInternal(SocketError.SocketError, "Exception during BeginRead."); - } - } - - private void OnEndRead(IAsyncResult ar) - { - if (ar.CompletedSynchronously) - { - return; - } - - EndRead(ar); - StartReceive(); - } - - private void EndRead(IAsyncResult ar) - { - int bytesRead; - try - { - bytesRead = _sslStream.EndRead(ar); - } - catch (SocketException exc) - { - Log.Debug(exc, "SocketException '{e}' during EndRead.", exc.SocketErrorCode); - NotifyReceiveCompleted(0); - CloseInternal(exc.SocketErrorCode, "SocketException during EndRead."); - return; - } - catch (ObjectDisposedException) - { - NotifyReceiveCompleted(0); - CloseInternal(SocketError.SocketError, "SslStream disposed."); - return; - } - catch (Exception exc) - { - Log.Debug(exc, "Exception during EndRead."); - NotifyReceiveCompleted(0); - CloseInternal(SocketError.SocketError, "Exception during EndRead."); - return; - } - - if (bytesRead <= 0) // socket closed normally - { - NotifyReceiveCompleted(0); - CloseInternal(SocketError.Success, "Socket closed."); - return; - } - - NotifyReceiveCompleted(bytesRead); - - var buffer = TcpConnection.BufferManager.CheckOut(); - if (buffer.Array == null || buffer.Count == 0 || buffer.Array.Length < buffer.Offset + buffer.Count) - { - throw new Exception("Invalid buffer allocated."); - } - - Buffer.BlockCopy(_receiveBuffer, 0, buffer.Array, buffer.Offset, bytesRead); - var buf = new ArraySegment(buffer.Array, buffer.Offset, buffer.Count); - _receiveQueue.Enqueue(new ReceivedData(buf, bytesRead)); - - TryDequeueReceivedData(); - } - - private void TryDequeueReceivedData() - { - if (Interlocked.CompareExchange(ref _receiveHandling, 1, 0) != 0) - { - return; - } - - do - { - if (!_receiveQueue.IsEmpty && _receiveCallback != null) - { - var callback = Interlocked.Exchange(ref _receiveCallback, null); - if (callback == null) - { - Log.Fatal("Some threading issue in TryDequeueReceivedData! Callback is null!"); - throw new Exception("Some threading issue in TryDequeueReceivedData! Callback is null!"); - } - - var res = new List(_receiveQueue.Count); - ReceivedData piece; - while (_receiveQueue.TryDequeue(out piece)) - { - res.Add(piece); - } - - var data = new ArraySegment[res.Count]; - int bytes = 0; - for (int i = 0; i < data.Length; ++i) - { - var d = res[i]; - bytes += d.DataLen; - data[i] = new ArraySegment(d.Buf.Array, d.Buf.Offset, d.DataLen); - } - - lock (_closeLock) - { - if (!_isClosed) - { - callback(this, data); - } - } - - for (int i = 0, n = res.Count; i < n; ++i) - { - TcpConnection.BufferManager.CheckIn(res[i].Buf); // dispose buffers - } - - NotifyReceiveDispatched(bytes); - } - - Interlocked.Exchange(ref _receiveHandling, 0); - } while (!_receiveQueue.IsEmpty - && _receiveCallback != null - && Interlocked.CompareExchange(ref _receiveHandling, 1, 0) == 0); - } - - public void Close(string reason) => - CloseInternal(SocketError.Success, reason ?? "Normal socket close."); // normal socket closing - - private void CloseInternal(SocketError socketError, string reason) - { - lock (_closeLock) - { - if (_isClosing) - { - return; - } - - _isClosing = true; - } - - if (_sslStream != null) - { - Helper.EatException(() => _sslStream.Close()); - } - - if (_socket != null) - { - Helper.EatException(() => _socket.Dispose()); - } - - lock (_closeLock) - { - _isClosed = true; - } - - NotifyClosed(); - - if (_verbose) - { - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Received bytes: {totalBytesReceived}, Sent bytes: {totalBytesSent}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - TotalBytesReceived, TotalBytesSent); - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Send calls: {sendCalls}, callbacks: {sendCallbacks}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - SendCalls, SendCallbacks); - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Receive calls: {receiveCalls}, callbacks: {receiveCallbacks}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - ReceiveCalls, ReceiveCallbacks); - Log.Information( - "ES {connectionType} closed [{dateTime:HH:mm:ss.fff}: N{remoteEndPoint}, L{localEndPoint}, {connectionId:B}]:Close reason: [{e}] {reason}", - GetType().Name, DateTime.UtcNow, RemoteEndPoint, LocalEndPoint, _connectionId, - socketError, reason); - } - - var handler = ConnectionClosed; - handler?.Invoke(this, socketError); - } - - public void SetClientConnectionName(string clientConnectionName) => - _clientConnectionName = clientConnectionName; - - public override string ToString() => - "S" + RemoteEndPoint; - - private struct ReceivedData(ArraySegment buf, int dataLen) - { - public readonly ArraySegment Buf = buf; - public readonly int DataLen = dataLen; - } -} diff --git a/src/EventStore.Transport.Tcp/TcpServerListener.cs b/src/EventStore.Transport.Tcp/TcpServerListener.cs deleted file mode 100644 index 338241df8e..0000000000 --- a/src/EventStore.Transport.Tcp/TcpServerListener.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using EventStore.Common.Utils; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp; - -public class TcpServerListener -{ - private static readonly ILogger Log = Serilog.Log.ForContext(); - - private readonly IPEndPoint _serverEndPoint; - private readonly Socket _listeningSocket; - private readonly SocketArgsPool _acceptSocketArgsPool; - private Action _onSocketAccepted; - - public TcpServerListener(IPEndPoint serverEndPoint) - { - Ensure.NotNull(serverEndPoint, "serverEndPoint"); - - _serverEndPoint = serverEndPoint; - - _listeningSocket = new Socket(serverEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp); - - _acceptSocketArgsPool = new SocketArgsPool("TcpServerListener.AcceptSocketArgsPool", - TcpConfiguration.ConcurrentAccepts * 2, - CreateAcceptSocketArgs); - } - - private SocketAsyncEventArgs CreateAcceptSocketArgs() - { - var socketArgs = new SocketAsyncEventArgs(); - socketArgs.Completed += AcceptCompleted; - return socketArgs; - } - - public void StartListening(Action callback, string securityType) - { - Ensure.NotNull(callback, "callback"); - - _onSocketAccepted = callback; - - Log.Information("Starting {securityType} TCP listening on TCP endpoint: {serverEndPoint}.", securityType, - _serverEndPoint); - try - { - _listeningSocket.ExclusiveAddressUse = true; - _listeningSocket.Bind(_serverEndPoint); - _listeningSocket.Listen(TcpConfiguration.AcceptBacklogCount); - } - catch (Exception) - { - Log.Information("Failed to listen on TCP endpoint: {serverEndPoint}.", _serverEndPoint); - Helper.EatException(() => _listeningSocket.Close(TcpConfiguration.SocketCloseTimeoutSecs)); - throw; - } - - for (int i = 0; i < TcpConfiguration.ConcurrentAccepts; ++i) - { - StartAccepting(); - } - } - - private void StartAccepting() - { - var socketArgs = _acceptSocketArgsPool.Get(); - - try - { - var firedAsync = _listeningSocket.AcceptAsync(socketArgs); - if (!firedAsync) - { - ProcessAccept(socketArgs); - } - } - catch (ObjectDisposedException) - { - HandleBadAccept(socketArgs); - } - } - - private void AcceptCompleted(object sender, SocketAsyncEventArgs e) - { - ProcessAccept(e); - } - - private void ProcessAccept(SocketAsyncEventArgs e) - { - if (e.SocketError != SocketError.Success) - { - HandleBadAccept(e); - } - else - { - var acceptSocket = e.AcceptSocket; - e.AcceptSocket = null; - _acceptSocketArgsPool.Return(e); - - OnSocketAccepted(acceptSocket); - } - - StartAccepting(); - } - - private void HandleBadAccept(SocketAsyncEventArgs socketArgs) - { - Helper.EatException( - () => - { - if (socketArgs.AcceptSocket != null) // avoid annoying exceptions - { - socketArgs.AcceptSocket.Close(TcpConfiguration.SocketCloseTimeoutSecs); - } - }); - socketArgs.AcceptSocket = null; - _acceptSocketArgsPool.Return(socketArgs); - } - - private void OnSocketAccepted(Socket socket) - { - IPEndPoint socketEndPoint; - try - { - socketEndPoint = (IPEndPoint)socket.RemoteEndPoint; - } - catch (Exception) - { - return; - } - - _onSocketAccepted(socketEndPoint, socket); - } - - public void Stop() - { - Helper.EatException(() => _listeningSocket.Close(TcpConfiguration.SocketCloseTimeoutSecs)); - } -} diff --git a/src/EventStore.Transport.Tcp/TcpStats.cs b/src/EventStore.Transport.Tcp/TcpStats.cs deleted file mode 100644 index 7cf7c51d97..0000000000 --- a/src/EventStore.Transport.Tcp/TcpStats.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; - -namespace EventStore.Transport.Tcp; - -public class TcpStats -{ - /// - ///Number of TCP connections to Event Store - /// - public readonly int Connections; - /// - ///Total bytes sent from TCP connections - /// - public readonly long SentBytesTotal; - /// - ///Total bytes received by TCP connections - /// - public readonly long ReceivedBytesTotal; - /// - ///Total bytes sent to TCP connections since last run - /// - public readonly long SentBytesSinceLastRun; - /// - ///Total bytes received by TCP connections since last run - /// - public readonly long ReceivedBytesSinceLastRun; - /// - ///Sending speed in bytes per second - /// - public readonly double SendingSpeed; - /// - ///Receiving speed in bytes per second. - /// - public readonly double ReceivingSpeed; - /// - ///Number of bytes waiting to be sent to connections - /// - public readonly long PendingSend; - /// - ///Number of bytes sent to connections but not yet acknowledged by the receiving party - /// - public readonly long InSend; - /// - ///Number of bytes waiting to be received by connections - /// - public readonly long PendingReceived; - /// - ///Time elapsed since last stats read - /// - public readonly TimeSpan MeasureTime; - - public TcpStats(int connections, - long sentBytesTotal, - long receivedBytesTotal, - long sentBytesSinceLastRunSinceLastRun, - long receivedBytesSinceLastRun, - long pendingSend, - long inSend, - long pendingReceived, - TimeSpan measureTime) - { - Connections = connections; - SentBytesTotal = sentBytesTotal; - ReceivedBytesTotal = receivedBytesTotal; - SentBytesSinceLastRun = sentBytesSinceLastRunSinceLastRun; - ReceivedBytesSinceLastRun = receivedBytesSinceLastRun; - PendingSend = pendingSend; - InSend = inSend; - PendingReceived = pendingReceived; - MeasureTime = measureTime; - SendingSpeed = (MeasureTime.TotalSeconds < 0.00001) ? 0 : SentBytesSinceLastRun / MeasureTime.TotalSeconds; - ReceivingSpeed = (MeasureTime.TotalSeconds < 0.00001) - ? 0 - : ReceivedBytesSinceLastRun / MeasureTime.TotalSeconds; - } -} diff --git a/src/EventStore.Transport.Tcp/TcpTypedConnection.cs b/src/EventStore.Transport.Tcp/TcpTypedConnection.cs deleted file mode 100644 index 149a4f9bf9..0000000000 --- a/src/EventStore.Transport.Tcp/TcpTypedConnection.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using EventStore.Transport.Tcp.Formatting; -using EventStore.Transport.Tcp.Framing; -using ILogger = Serilog.ILogger; - -namespace EventStore.Transport.Tcp; - -public class TcpTypedConnection -{ - private static readonly ILogger Log = - Serilog.Log.ForContext(Serilog.Core.Constants.SourceContextPropertyName, "TcpTypedConnection"); - - public event Action, SocketError> ConnectionClosed; - - private readonly ITcpConnection _connection; - private readonly IMessageFormatter _formatter; - private readonly IMessageFramer> _framer; - - private Action, T> _receiveCallback; - - public EndPoint RemoteEndPoint - { - get { return _connection.RemoteEndPoint; } - } - - public EndPoint LocalEndPoint - { - get { return _connection.LocalEndPoint; } - } - - public int SendQueueSize - { - get { return _connection.SendQueueSize; } - } - - public TcpTypedConnection(ITcpConnection connection, - IMessageFormatter formatter, - IMessageFramer> framer) - { - if (formatter == null) - { - throw new ArgumentNullException("formatter"); - } - - if (framer == null) - { - throw new ArgumentNullException("framer"); - } - - _connection = connection; - _formatter = formatter; - _framer = framer; - - connection.ConnectionClosed += OnConnectionClosed; - - //Setup callback for incoming messages - framer.RegisterMessageArrivedCallback(IncomingMessageArrived); - } - - private void OnConnectionClosed(ITcpConnection connection, SocketError socketError) - { - connection.ConnectionClosed -= OnConnectionClosed; - - var handler = ConnectionClosed; - if (handler != null) - { - handler(this, socketError); - } - } - - public void EnqueueSend(T message) - { - var data = _formatter.ToArraySegment(message); - _connection.EnqueueSend(_framer.FrameData(data)); - } - - public void ReceiveAsync(Action, T> callback) - { - if (_receiveCallback != null) - { - throw new InvalidOperationException("ReceiveAsync should be called just once."); - } - - if (callback == null) - { - throw new ArgumentNullException("callback"); - } - - _receiveCallback = callback; - - _connection.ReceiveAsync(OnRawDataReceived); - } - - private void OnRawDataReceived(ITcpConnection connection, IEnumerable> data) - { - try - { - _framer.UnFrameData(data); - } - catch (PackageFramingException exc) - { - Log.Information(exc, "Invalid TCP frame received."); - Close("Invalid TCP frame received."); - return; - } - - connection.ReceiveAsync(OnRawDataReceived); - } - - private void IncomingMessageArrived(ArraySegment message) - { - _receiveCallback(this, _formatter.From(message)); - } - - public void Close(string reason = null) - { - _connection.Close(reason); - } -} diff --git a/src/EventStore.sln b/src/EventStore.sln index 7262d86925..b2a2503a1e 100644 --- a/src/EventStore.sln +++ b/src/EventStore.sln @@ -19,8 +19,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventStore.TestClient", "Ev EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventStore.Core", "EventStore.Core\EventStore.Core.csproj", "{9957CA10-8A8A-419F-92BD-7A8BE3014345}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventStore.Transport.Tcp", "EventStore.Transport.Tcp\EventStore.Transport.Tcp.csproj", "{DCAD0184-4AFE-49EA-BE31-5D68CA4B59CA}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EventStore.ClusterNode", "EventStore.ClusterNode\EventStore.ClusterNode.csproj", "{FDFCE363-84A4-4A6F-B97C-D9F57D8D124F}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A19631BB-65CF-4726-8732-4EFD977E9B32}" @@ -63,6 +61,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CB56B2BD EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrogonEventStore.SemanticConventions", "TrogonEventStore.SemanticConventions\TrogonEventStore.SemanticConventions.csproj", "{05D9513D-A26A-410A-A145-4A17548EB79C}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EventStore.TestClient.Tests", "EventStore.TestClient.Tests\EventStore.TestClient.Tests.csproj", "{0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -349,6 +349,18 @@ Global {05D9513D-A26A-410A-A145-4A17548EB79C}.Release|ARM64.Build.0 = Release|Any CPU {05D9513D-A26A-410A-A145-4A17548EB79C}.Release|x64.ActiveCfg = Release|Any CPU {05D9513D-A26A-410A-A145-4A17548EB79C}.Release|x64.Build.0 = Release|Any CPU + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Debug|ARM64.Build.0 = Debug|ARM64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Debug|x64.ActiveCfg = Debug|x64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Debug|x64.Build.0 = Debug|x64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Release|Any CPU.Build.0 = Release|Any CPU + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Release|ARM64.ActiveCfg = Release|ARM64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Release|ARM64.Build.0 = Release|ARM64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Release|x64.ActiveCfg = Release|x64 + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -363,6 +375,7 @@ Global {58219323-BD46-4F67-A83A-410F56F84EE3} = {CB56B2BD-5ABA-49C7-BD57-21B9CD5C3205} {41EEB66F-D77B-4FF3-BF68-DA3748C80BB5} = {CB56B2BD-5ABA-49C7-BD57-21B9CD5C3205} {CA966906-51C8-4520-924E-988FA396B712} = {CB56B2BD-5ABA-49C7-BD57-21B9CD5C3205} + {0ADF086C-FE82-4AB9-91E3-ABDDC4DDC9B4} = {CB56B2BD-5ABA-49C7-BD57-21B9CD5C3205} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CD63E89D-F4FA-42A7-A10B-50CF854078FA} diff --git a/src/Protos/ClientAPI/ClientMessageDtos.proto b/src/Protos/ClientAPI/ClientMessageDtos.proto deleted file mode 100755 index a94dc2fdc7..0000000000 --- a/src/Protos/ClientAPI/ClientMessageDtos.proto +++ /dev/null @@ -1,450 +0,0 @@ -syntax = "proto3"; - -package EventStore.Client.Messages; - -enum OperationResult -{ - Success = 0; - PrepareTimeout = 1; - CommitTimeout = 2; - ForwardTimeout = 3; - WrongExpectedVersion = 4; - StreamDeleted = 5; - InvalidTransaction = 6; - AccessDenied = 7; -} - -message NewEvent { - bytes event_id = 1; - string event_type = 2; - int32 data_content_type = 3; - int32 metadata_content_type = 4; - bytes data = 5; - bytes metadata = 6; -} - -message EventRecord { - string event_stream_id = 1; - int64 event_number = 2; - bytes event_id = 3; - string event_type = 4; - int32 data_content_type = 5; - int32 metadata_content_type = 6; - bytes data = 7; - bytes metadata = 8; - int64 created = 9; - int64 created_epoch = 10; -} - -message ResolvedIndexedEvent { - EventRecord event = 1; - EventRecord link = 2; -} - -message ResolvedEvent { - EventRecord event = 1; - EventRecord link = 2; - int64 commit_position = 3; - int64 prepare_position = 4; -} - -message WriteEvents { - string event_stream_id = 1; - int64 expected_version = 2; - repeated NewEvent events = 3; - bool require_leader = 4; -} - -message WriteEventsCompleted { - OperationResult result = 1; - string message = 2; - int64 first_event_number = 3; - int64 last_event_number = 4; - int64 prepare_position = 5; - int64 commit_position = 6; - int64 current_version = 7; -} - -message DeleteStream { - string event_stream_id = 1; - int64 expected_version = 2; - bool require_leader = 3; - bool hard_delete = 4; -} - -message DeleteStreamCompleted { - OperationResult result = 1; - string message = 2; - int64 prepare_position = 3; - int64 commit_position = 4; - int64 current_version = 5; -} - -message TransactionStart { - string event_stream_id = 1; - int64 expected_version = 2; - bool require_leader = 3; -} - -message TransactionStartCompleted { - int64 transaction_id = 1; - OperationResult result = 2; - string message = 3; -} - -message TransactionWrite { - int64 transaction_id = 1; - repeated NewEvent events = 2; - bool require_leader = 3; -} - -message TransactionWriteCompleted { - int64 transaction_id = 1; - OperationResult result = 2; - string message = 3; -} - -message TransactionCommit { - int64 transaction_id = 1; - bool require_leader = 2; -} - -message TransactionCommitCompleted { - int64 transaction_id = 1; - OperationResult result = 2; - string message = 3; - int64 first_event_number = 4; - int64 last_event_number = 5; - int64 prepare_position = 6; - int64 commit_position = 7; -} - -message ReadEvent { - string event_stream_id = 1; - int64 event_number = 2; - bool resolve_link_tos = 3; - bool require_leader = 4; -} - -message ReadEventCompleted { - - enum ReadEventResult { - Success = 0; - NotFound = 1; - NoStream = 2; - StreamDeleted = 3; - Error = 4; - AccessDenied = 5; - } - - ReadEventResult result = 1; - ResolvedIndexedEvent event = 2; - - string error = 3; -} - -message ReadStreamEvents { - string event_stream_id = 1; - int64 from_event_number = 2; - int32 max_count = 3; - bool resolve_link_tos = 4; - bool require_leader = 5; -} - -message ReadStreamEventsCompleted { - - enum ReadStreamResult { - Success = 0; - NoStream = 1; - StreamDeleted = 2; - NotModified = 3; - Error = 4; - AccessDenied = 5; - } - - repeated ResolvedIndexedEvent events = 1; - ReadStreamResult result = 2; - int64 next_event_number = 3; - int64 last_event_number = 4; - bool is_end_of_stream = 5; - int64 last_commit_position = 6; - - string error = 7; -} - -message ReadAllEvents { - int64 commit_position = 1; - int64 prepare_position = 2; - int32 max_count = 3; - bool resolve_link_tos = 4; - bool require_leader = 5; -} - -message ReadAllEventsCompleted { - - enum ReadAllResult { - Success = 0; - NotModified = 1; - Error = 2; - AccessDenied = 3; - } - - int64 commit_position = 1; - int64 prepare_position = 2; - repeated ResolvedEvent events = 3; - int64 next_commit_position = 4; - int64 next_prepare_position = 5; - - ReadAllResult result = 6; - string error = 7; -} - -message Filter{ - - enum FilterContext { - StreamId = 0; - EventType = 1; - - } - - enum FilterType { - Regex = 0; - Prefix = 1; - } - - FilterContext context = 1; - FilterType type = 2; - repeated string data = 3; -} - -message FilteredReadAllEvents { - int64 commit_position = 1; - int64 prepare_position = 2; - int32 max_count = 3; - int32 max_search_window = 4; - bool resolve_link_tos = 5; - bool require_leader = 6; - Filter filter = 7; -} - -message FilteredReadAllEventsCompleted { - - enum FilteredReadAllResult { - Success = 0; - NotModified = 1; - Error = 2; - AccessDenied = 3; - } - - int64 commit_position = 1; - int64 prepare_position = 2; - repeated ResolvedEvent events = 3; - int64 next_commit_position = 4; - int64 next_prepare_position = 5; - bool is_end_of_stream = 6; - - FilteredReadAllResult result = 7; - string error = 8; -} - -message CreatePersistentSubscription { - string subscription_group_name = 1; - string event_stream_id = 2; - bool resolve_link_tos = 3; - int64 start_from = 4; - int32 message_timeout_milliseconds = 5; - bool record_statistics = 6; - int32 live_buffer_size = 7; - int32 read_batch_size = 8; - int32 buffer_size = 9; - int32 max_retry_count = 10; - bool prefer_round_robin = 11; - int32 checkpoint_after_time = 12; - int32 checkpoint_max_count = 13; - int32 checkpoint_min_count = 14; - int32 subscriber_max_count = 15; - string named_consumer_strategy = 16; -} - -message DeletePersistentSubscription { - string subscription_group_name = 1; - string event_stream_id = 2; -} - -message UpdatePersistentSubscription { - string subscription_group_name = 1; - string event_stream_id = 2; - bool resolve_link_tos = 3; - int64 start_from = 4; - int32 message_timeout_milliseconds = 5; - bool record_statistics = 6; - int32 live_buffer_size = 7; - int32 read_batch_size = 8; - int32 buffer_size = 9; - int32 max_retry_count = 10; - bool prefer_round_robin = 11; - int32 checkpoint_after_time = 12; - int32 checkpoint_max_count = 13; - int32 checkpoint_min_count = 14; - int32 subscriber_max_count = 15; - string named_consumer_strategy = 16; -} - -message UpdatePersistentSubscriptionCompleted { - enum UpdatePersistentSubscriptionResult { - Success = 0; - DoesNotExist = 1; - Fail = 2; - AccessDenied=3; - } - UpdatePersistentSubscriptionResult result = 1; - string reason = 2; -} - -message CreatePersistentSubscriptionCompleted { - enum CreatePersistentSubscriptionResult { - Success = 0; - AlreadyExists = 1; - Fail = 2; - AccessDenied=3; - } - CreatePersistentSubscriptionResult result = 1; - string reason = 2; -} - -message DeletePersistentSubscriptionCompleted { - enum DeletePersistentSubscriptionResult { - Success = 0; - DoesNotExist = 1; - Fail = 2; - AccessDenied = 3; - } - DeletePersistentSubscriptionResult result = 1; - string reason = 2; -} - -message ConnectToPersistentSubscription { - string subscription_id = 1; - string event_stream_id = 2; - int32 allowed_in_flight_messages = 3; - -} - -message PersistentSubscriptionAckEvents { - string subscription_id = 1; - repeated bytes processed_event_ids = 2; -} - -message PersistentSubscriptionNakEvents { - enum NakAction { - Unknown = 0; - Park = 1; - Retry = 2; - Skip = 3; - Stop = 4; - } - - string subscription_id = 1; - repeated bytes processed_event_ids = 2; - string message = 3; - NakAction action = 4; -} - -message PersistentSubscriptionConfirmation { - int64 last_commit_position = 1; - string subscription_id = 2; - int64 last_event_number = 3; -} - -message PersistentSubscriptionStreamEventAppeared { - ResolvedIndexedEvent event = 1; - int32 retryCount = 2; -} - -message SubscribeToStream { - string event_stream_id = 1; - bool resolve_link_tos = 2; -} - -message FilteredSubscribeToStream { - string event_stream_id = 1; - bool resolve_link_tos = 2; - Filter filter = 3; - int32 checkpoint_interval = 4; -} - -message CheckpointReached { - int64 commit_position = 1; - int64 prepare_position = 2; -} - -message SubscriptionConfirmation { - int64 last_commit_position = 1; - int64 last_event_number = 2; -} - -message StreamEventAppeared { - ResolvedEvent event = 1; -} - -message UnsubscribeFromStream { -} - -message SubscriptionDropped { - - enum SubscriptionDropReason { - Unsubscribed = 0; - AccessDenied = 1; - NotFound=2; - PersistentSubscriptionDeleted=3; - SubscriberMaxCountReached=4; - } - - SubscriptionDropReason reason = 1; -} - -message NotHandled { - - enum NotHandledReason { - NotReady = 0; - TooBusy = 1; - NotLeader = 2; - IsReadOnly = 3; - } - - NotHandledReason reason = 1; - bytes additional_info = 2; - - message LeaderInfo { - string external_tcp_address = 1; - int32 external_tcp_port = 2; - string http_address = 3; - int32 http_port = 4; - string external_secure_tcp_address = 5; - int32 external_secure_tcp_port = 6; - } -} - -message ScavengeDatabase { -} - -message ScavengeDatabaseResponse { - - enum ScavengeResult { - Started = 0; - InProgress = 1; - Unauthorized = 2; - } - - ScavengeResult result = 1; - string scavengeId = 2; - -} - -message IdentifyClient { - int32 version = 1; - string connection_name = 2; -} - -message ClientIdentified { -} diff --git a/src/Protos/Grpc/cluster.proto b/src/Protos/Grpc/cluster.proto index e7e5cd25a1..93d75be19f 100644 --- a/src/Protos/Grpc/cluster.proto +++ b/src/Protos/Grpc/cluster.proto @@ -128,10 +128,8 @@ message MemberInfo { VNodeState state = 3; bool is_alive = 4; EndPoint http_end_point = 5; - EndPoint internal_tcp = 6; - EndPoint external_tcp = 7; - bool internal_tcp_uses_tls = 8; - bool external_tcp_uses_tls = 9; + reserved 6, 7, 8, 9; + reserved "internal_tcp", "external_tcp", "internal_tcp_uses_tls", "external_tcp_uses_tls"; int64 last_commit_position = 10; int64 writer_checkpoint = 11; @@ -142,8 +140,9 @@ message MemberInfo { int32 node_priority = 16; bool is_read_only_replica = 17; - string advertise_host_to_client_as = 18; - uint32 advertise_http_port_to_client_as = 19; - uint32 advertise_tcp_port_to_client_as = 20; + string advertise_host_to_client_as = 18; + uint32 advertise_http_port_to_client_as = 19; + reserved 20; + reserved "advertise_tcp_port_to_client_as"; string es_version = 21; } diff --git a/src/Protos/Grpc/forwarding.proto b/src/Protos/Grpc/forwarding.proto index 621ceb77d4..47387edf76 100644 --- a/src/Protos/Grpc/forwarding.proto +++ b/src/Protos/Grpc/forwarding.proto @@ -188,8 +188,8 @@ enum NotHandledReason { } message LeaderInfo { - EndPoint external_tcp = 1; - bool is_secure = 2; + reserved 1, 2; + reserved "external_tcp", "is_secure"; EndPoint http = 3; } diff --git a/src/Protos/Grpc/monitoring.proto b/src/Protos/Grpc/monitoring.proto index dc1a31db9e..1d10268736 100644 --- a/src/Protos/Grpc/monitoring.proto +++ b/src/Protos/Grpc/monitoring.proto @@ -6,7 +6,6 @@ import "google/protobuf/struct.proto"; service Monitoring { rpc Stats(StatsReq) returns (stream StatsResp); - rpc TcpStats(TcpStatsReq) returns (TcpStatsResp); rpc ReplicationStats(ReplicationStatsReq) returns (ReplicationStatsResp); } @@ -22,26 +21,6 @@ message StatsResp { google.protobuf.Value structured_stats = 2; } -message TcpStatsReq { -} - -message TcpStatsResp { - repeated TcpConnectionStats connections = 1; -} - -message TcpConnectionStats { - string remote_endpoint = 1; - string local_endpoint = 2; - string client_connection_name = 3; - string connection_id = 4; - int64 total_bytes_sent = 5; - int64 total_bytes_received = 6; - int32 pending_send_bytes = 7; - int32 pending_received_bytes = 8; - bool is_external_connection = 9; - bool is_ssl_connection = 10; -} - message ReplicationStatsReq { } From 7d172a123fc560a8a2f644037550f51f88b9d51c Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 12 Sep 2026 15:09:30 -0400 Subject: [PATCH 2/4] fix(cluster): preserve isolated replication topology Signed-off-by: Yordis Prieto --- docs/admin-ui.md | 2 +- docs/cluster.md | 15 ++-- docs/networking.md | 88 +++++++++++++++---- docs/security.md | 8 +- docs/upgrade-guide.md | 17 ++-- proto.lock | 5 ++ .../Services/ReplicationEndpointPolicy.cs | 32 +++++++ src/EventStore.ClusterNode/Program.cs | 25 +++++- .../Cluster/MemberInfoTests.cs | 58 ++++++++++++ .../Helpers/MiniClusterNode.cs | 14 ++- .../when_archiving_and_restoring_a_cluster.cs | 1 + .../Integration/specification_with_cluster.cs | 8 +- .../Regression/GrpcOnlySurfaceParityTests.cs | 33 ++++++- .../connecting_to_read_only_replica.cs | 2 +- .../GrpcReplicaServiceFactoryTests.cs | 31 ++++++- .../GrpcReplicaServiceSupervisorTests.cs | 9 +- .../when_building/with_default_settings.cs | 6 ++ .../ClusterVNodeOptionsValidatorTests.cs | 38 ++++++++ src/EventStore.Core/Cluster/ClusterInfo.cs | 9 +- src/EventStore.Core/Cluster/MemberInfo.cs | 33 ++++--- src/EventStore.Core/ClusterVNode.cs | 43 +++++++-- .../Configuration/ClusterVNodeOptions.cs | 24 ++++- .../ClusterVNodeOptionsExtensions.cs | 24 +++++ .../ClusterVNodeOptionsValidator.cs | 29 ++++++ .../Data/GossipAdvertiseInfo.cs | 7 +- src/EventStore.Core/Data/VNodeInfo.cs | 9 +- .../Services/ElectionsService.cs | 2 +- .../Services/Gossip/NodeGossipService.cs | 3 +- .../GrpcReplicaServiceSupervisor.cs | 5 +- .../Replication/ReplicationGrpcClient.cs | 31 ++++++- src/Protos/Grpc/cluster.proto | 1 + 31 files changed, 525 insertions(+), 87 deletions(-) create mode 100644 src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs diff --git a/docs/admin-ui.md b/docs/admin-ui.md index 0a19e0ae61..d569a03ab5 100644 --- a/docs/admin-ui.md +++ b/docs/admin-ui.md @@ -24,7 +24,7 @@ The _Observability_ page focuses on runtime diagnostics: - queue groups and individual queue rows - current and last processed messages -- active connections on the shared HTTP and gRPC endpoint, including client identity, protocol, security, traffic rates, totals, and pending bytes +- active connections on the node and replication HTTP/gRPC endpoints, including client identity, protocol, security, traffic rates, totals, and pending bytes - live gRPC replication sessions, byte totals, pending bytes, and send queue depth - snapshot output for copy-paste debugging diff --git a/docs/cluster.md b/docs/cluster.md index ee826e4a1e..8563417b03 100644 --- a/docs/cluster.md +++ b/docs/cluster.md @@ -55,13 +55,14 @@ The multi-address DNS name cluster discovery only works for clusters that use ce ### Internal communication -Cluster nodes use gRPC over each node's HTTP(S) endpoint for replication and -follower-to-leader request forwarding. Ensure every node can reach every other -node at its advertised HTTP(S) endpoint. There is no separate replication -listener or TCP port to expose. +Cluster nodes use gRPC over each node's dedicated replication HTTP(S) endpoint +for database replication. Gossip, elections, and follower-to-leader request +forwarding use the node HTTP(S) endpoint. Ensure every node can reach both +advertised endpoints on every other node. -Learn more about the shared [HTTP(S) configuration](networking.md#http-configuration) -before configuring cluster firewall or network policy rules. +Learn more about the [node](networking.md#http-configuration) and +[replication](networking.md#internal-cluster-traffic) endpoints before +configuring cluster firewall or network policy rules. ## Cluster with DNS @@ -114,7 +115,7 @@ The setting accepts a comma-separated list of IP addresses or host names with th TrogonEventStore uses a quorum-based replication model. When working normally, a cluster has one node known as a leader, and the remaining nodes are followers. The leader node is responsible for coordinating writes while it is the leader. Cluster nodes use a consensus algorithm to determine which node should be the leader and which should be followers. TrogonEventStore bases the decision as to which node should be the leader on a number of factors. For a cluster node to have this information available to them, the nodes gossip -with other nodes in the cluster over the shared HTTP(S) endpoint. +with other nodes in the cluster over the node HTTP(S) endpoint. The gossip protocol configuration can be changed using the settings listed below. Pay attention to the settings related to time, like intervals and timeouts, when running in a cloud environment. diff --git a/docs/networking.md b/docs/networking.md index 0af003372a..cff4ea7f02 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -4,22 +4,22 @@ title: Networking ## Network configuration -TrogonEventStore uses one HTTP(S) endpoint for all network traffic: +TrogonEventStore provides two HTTP(S) interfaces: -- gRPC carries database client APIs, cluster replication, and follower-to-leader request forwarding. -- HTTP carries the Admin UI, health probes, metrics, and other operator endpoints. +- The node endpoint carries database client APIs, cluster coordination, follower-to-leader request forwarding, the Admin UI, health probes, metrics, and other operator endpoints. +- The replication endpoint carries node-to-node database replication over gRPC. The node does not open a separate legacy EventStore TCP protocol listener or -accept its TCP transport configuration. gRPC and HTTP still use TCP at the -operating-system transport layer. In a cluster, every node must be able to reach -every other node at its advertised HTTP(S) endpoint. Use network policy and -ingress rules to control which clients can reach the shared listener. +accept the legacy TCP client protocol. gRPC traffic on both interfaces uses +HTTP/2 over TCP at the operating-system transport layer. Keep the replication +endpoint on the private cluster network and expose the node endpoint according +to client and operator requirements. ## HTTP configuration -HTTP(S) is the node's only network endpoint. HTTP/2 carries gRPC traffic for -database clients and internal cluster operations, while regular HTTP routes -serve the Admin UI, health checks, metrics, and supported diagnostics. +HTTP/2 on the node endpoint carries gRPC traffic for database clients and +internal cluster coordination, while regular HTTP routes serve the Admin UI, +health checks, metrics, and supported diagnostics. The HTTP endpoint always binds to the IP address configured in the `NodeIp` setting (previously referred to as `ExtIp`). | Format | Syntax | @@ -147,13 +147,51 @@ This is configured with `Kestrel.Limits.Http2.InitialStreamWindowSize` in the se ## Internal cluster traffic -Cluster replication and follower-to-leader request forwarding use gRPC over -the same `NodeIp` and `NodePort` binding used by client gRPC calls. There is no -separate replication interface or port. +Cluster replication uses a dedicated HTTP/2 listener so it can remain isolated +from client and operator traffic while using gRPC. Follower-to-leader request +forwarding and cluster coordination continue to use the node endpoint. + +The replication listener binds to `ReplicationIp`: + +| Format | Syntax | +|:---------------------|:----------------------------| +| Command line | `--replication-ip` | +| YAML | `ReplicationIp` | +| Environment variable | `EVENTSTORE_REPLICATION_IP` | + +**Default**: `127.0.0.1` (loopback). + +For a multi-node cluster, bind this setting to an interface reachable by the +other database nodes. `0.0.0.0` binds all IPv4 interfaces. + +The replication listener uses `ReplicationPort`: + +| Format | Syntax | +|:---------------------|:------------------------------| +| Command line | `--replication-port` | +| YAML | `ReplicationPort` | +| Environment variable | `EVENTSTORE_REPLICATION_PORT` | + +**Default**: `1112` When TLS is enabled, internal gRPC connections use the configured node -certificate. `DisableTls` disables encryption for the shared HTTP(S) endpoint -while preserving authentication and authorization. +certificate. `DisableTls` disables encryption for both HTTP(S) listeners while +preserving authentication and authorization. + +Replication connections use HTTP/2 keepalive pings for failure detection. The +existing replication heartbeat settings configure the client-side ping interval +and acknowledgement timeout: + +| Format | Interval | Timeout | +|:---------------------|:-------------------------------------------|:------------------------------------------| +| Command line | `--replication-heartbeat-interval` | `--replication-heartbeat-timeout` | +| YAML | `ReplicationHeartbeatInterval` | `ReplicationHeartbeatTimeout` | +| Environment variable | `EVENTSTORE_REPLICATION_HEARTBEAT_INTERVAL` | `EVENTSTORE_REPLICATION_HEARTBEAT_TIMEOUT` | + +**Default**: `700` ms for both settings. + +Values below `1000` ms remain valid for configuration compatibility and use the +HTTP/2 transport minimum of `1000` ms. ## Network address translation @@ -161,7 +199,7 @@ Due to NAT (network address translation), or other reasons a node may not be bou Options described below allow you to tell the node that even though it is bound to a given address it should not gossip that address. When returning links over HTTP, TrogonEventStore will also use the specified addresses instead of physical addresses, so the clients that use HTTP can follow those links. -Another case when you might want to specify the advertised address although there's no address translation involved. When you configure TrogonEventStore to bind to `0.0.0.0`, it will use the first non-loopback address for gossip. It might or might not be the address you want it to use. Configure `NodeHostAdvertiseAs` when other nodes and clients must connect using a specific IP address or hostname. +Another case when you might want to specify the advertised address although there's no address translation involved. When you configure TrogonEventStore to bind to `0.0.0.0`, it will use the first non-loopback address for gossip. It might or might not be the address you want it to use. Configure `NodeHostAdvertiseAs` for the node endpoint and `ReplicationHostAdvertiseAs` for the replication endpoint when other nodes must connect using specific IP addresses or hostnames. You might also override the advertised address when secure cluster certificates contain DNS names rather than IP addresses. @@ -194,6 +232,21 @@ If you want the node to advertise itself using the hostname rather than its IP a Please note that the `ExtHostAdvertiseAs` parameter has been deprecated as of version 23.10.0 and will be removed in future versions. It is recommended to use the `NodeHostAdvertiseAs` parameter instead. ::: +## Replication endpoint advertisement + +If the bound replication address or port is not reachable as-is from the other +nodes, override the endpoint advertised through internal gossip. + +| Format | Host syntax | Port syntax | +|:---------------------|:---------------------------------------------|:-------------------------------------------------| +| Command line | `--replication-host-advertise-as` | `--replication-tcp-port-advertise-as` | +| YAML | `ReplicationHostAdvertiseAs` | `ReplicationTcpPortAdvertiseAs` | +| Environment variable | `EVENTSTORE_REPLICATION_HOST_ADVERTISE_AS` | `EVENTSTORE_REPLICATION_TCP_PORT_ADVERTISE_AS` | + +The port option retains its existing name for configuration compatibility, but +the advertised endpoint now carries gRPC replication rather than the removed +TCP replication protocol. + ### Advertise to clients In some cases, the cluster needs to advertise itself to clients using a completely different set of addresses and ports. Usually, you need to do it because addresses and ports configured for the HTTP protocol are not available as-is to the outside world. One of the examples is running a cluster in Docker Compose. In such environment, HTTP uses internal hostnames in the Docker network, which isn't accessible on the host. So, in order to connect to the cluster from the host machine, you need to use `localhost` and translated HTTP ports to reach the cluster nodes. @@ -249,4 +302,5 @@ You can disable the Prometheus metrics endpoint by setting `DisableStatsOnHttp` Database client APIs, replication, and follower-to-leader forwarding use gRPC. The remaining HTTP routes are operator surfaces, not an application event API. The server has no legacy EventStore TCP protocol listener, TCP client protocol, -replication TCP port, or TCP heartbeat configuration. +or TCP replication protocol. The separately configurable replication listener +is HTTP/2-only and accepts only the replication gRPC service. diff --git a/docs/security.md b/docs/security.md index d83d6ffabd..305816f944 100644 --- a/docs/security.md +++ b/docs/security.md @@ -515,10 +515,10 @@ Import-Certificate -FilePath .\ca.crt -CertStoreLocation Cert:\LocalMachine\CA ### Internal gRPC security When TLS is enabled, cluster replication and follower-to-leader forwarding use -the configured node certificate on the shared HTTPS endpoint. Internal gRPC TLS -cannot be disabled independently. Use [`DisableTls`](#disable-tls) to disable -transport encryption for the entire endpoint while preserving authentication -and authorization. +the configured node certificate on the replication and node HTTPS endpoints. +Internal gRPC TLS cannot be disabled independently. Use +[`DisableTls`](#disable-tls) to disable transport encryption for both listeners +while preserving authentication and authorization. ## Authentication diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index 689c5b3884..fc1a272634 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -13,9 +13,9 @@ from this repository. - Record the current server version, configuration file, container image, and command-line arguments. - Check that clients are using gRPC. -- Remove legacy EventStore TCP protocol listener, replication port, - advertisement, and heartbeat settings. They are not supported by the current - server. +- Remove legacy EventStore TCP client-listener settings. Keep the replication + endpoint, advertisement, and heartbeat settings because they now configure + the dedicated gRPC replication listener. - Review changed configuration keys before restarting a durable node. - Verify that health probes use `/-/liveness` and `/-/readiness`. - Verify that metrics scraping uses `/-/metrics`. @@ -53,10 +53,11 @@ Before upgrading, search for obsolete client and HTTP-management settings. The current product direction is: - gRPC for database client APIs. -- gRPC over the node HTTP(S) endpoint for cluster replication and - follower-to-leader forwarding. +- gRPC over the dedicated replication HTTP(S) endpoint for database replication. +- gRPC over the node HTTP(S) endpoint for follower-to-leader forwarding and + cluster coordination. - HTTP for Admin UI, health, metrics, and infrastructure concerns. -- No legacy EventStore TCP protocol listener or TCP transport configuration. +- No legacy EventStore TCP client protocol listener. - No proprietary plugin configuration. If a setting is no longer documented, remove it rather than carrying it forward @@ -74,9 +75,9 @@ Use [OpenTelemetry integration](diagnostics/integrations.md) for explicit OTLP export and [Metrics](diagnostics/metrics.md) for Prometheus scraping. The Admin UI continues to show active connections after the legacy protocol is -removed. The connection table reports the shared HTTP and gRPC listener, while +removed. The connection table reports the node and replication HTTP/gRPC listeners, while the replication table reports the database replication sessions and their byte -and queue statistics. The shared-endpoint table keeps the live paging and +and queue statistics. The connection table keeps the live paging and per-second traffic view while identifying HTTP, gRPC, TLS, and the observed client. These replace the legacy listener-specific TCP table. diff --git a/proto.lock b/proto.lock index 70d86541cd..e6689b6e5d 100644 --- a/proto.lock +++ b/proto.lock @@ -462,6 +462,11 @@ "id": 21, "name": "es_version", "type": "string" + }, + { + "id": 22, + "name": "replication_end_point", + "type": "EndPoint" } ], "reserved_ids": [ diff --git a/src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs b/src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs new file mode 100644 index 0000000000..1c8c2f7fd3 --- /dev/null +++ b/src/EventStore.ClusterNode/Components/Services/ReplicationEndpointPolicy.cs @@ -0,0 +1,32 @@ +using System; +using System.Net; +using Microsoft.AspNetCore.Http; + +namespace EventStore.ClusterNode.Components.Services; + +public sealed class ReplicationEndpointPolicy(IPEndPoint listenEndPoint) +{ + private const string ReplicationServicePath = "/event_store.replication.Replication"; + + public bool Allows(HttpContext context) + { + var isReplicationConnection = Matches(context.Connection.LocalIpAddress, context.Connection.LocalPort); + var isReplicationRequest = context.Request.Path.StartsWithSegments( + ReplicationServicePath, + StringComparison.Ordinal); + + return isReplicationConnection == isReplicationRequest; + } + + private bool Matches(IPAddress localAddress, int localPort) + { + if (localPort != listenEndPoint.Port) + { + return false; + } + + return listenEndPoint.Address.Equals(IPAddress.Any) || + listenEndPoint.Address.Equals(IPAddress.IPv6Any) || + listenEndPoint.Address.Equals(localAddress); + } +} diff --git a/src/EventStore.ClusterNode/Program.cs b/src/EventStore.ClusterNode/Program.cs index 0759fc776c..6dbe3b2dbb 100644 --- a/src/EventStore.ClusterNode/Program.cs +++ b/src/EventStore.ClusterNode/Program.cs @@ -25,6 +25,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -274,6 +275,8 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig }); var nodeConnectionTracker = new NodeConnectionTracker(); + var replicationEndpointPolicy = new ReplicationEndpointPolicy( + new System.Net.IPEndPoint(options.Interface.ReplicationIp, options.Interface.ReplicationPort)); builder.Services.AddSingleton(nodeConnectionTracker); builder.WebHost.ConfigureKestrel(server => { @@ -285,6 +288,9 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig server.Listen(options.Interface.NodeIp, options.Interface.NodePort, listenOptions => ConfigureHttpOptions(listenOptions, hostedService, nodeConnectionTracker, useHttps: !hostedService.Node.DisableHttps)); + server.Listen(options.Interface.ReplicationIp, options.Interface.ReplicationPort, listenOptions => + ConfigureHttpOptions(listenOptions, hostedService, nodeConnectionTracker, + useHttps: !hostedService.Node.DisableHttps, http2Only: true)); if (hostedService.Node.EnableUnixSocket) { @@ -340,6 +346,16 @@ async Task Run(ClusterVNodeHostedService hostedService, ManualResetEventSlim sig context.Request.Headers.UserAgent.ToString()); return next(context); }); + app.Use(async (context, next) => + { + if (!replicationEndpointPolicy.Allows(context)) + { + context.Response.StatusCode = StatusCodes.Status404NotFound; + return; + } + + await next(context); + }); app.UseMiddleware(); hostedService.Node.Startup.Configure(app); if (oauthEnabled) @@ -395,15 +411,20 @@ private static void ConfigureHttpOptions( ListenOptions listenOptions, ClusterVNodeHostedService hostedService, NodeConnectionTracker connectionTracker, - bool useHttps) + bool useHttps, + bool http2Only = false) { listenOptions.Use(next => context => connectionTracker.Track(context, next, useHttps)); + if (http2Only) + { + listenOptions.Protocols = HttpProtocols.Http2; + } if (useHttps) { listenOptions.UseHttps(CreateServerOptionsSelectionCallback(hostedService), null); } - else + else if (!http2Only) { listenOptions.Use(next => new ClearTextHttpMultiplexingMiddleware(next).OnConnectAsync); diff --git a/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs b/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs index ddab6eaabe..b2bf2b68ea 100644 --- a/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs +++ b/src/EventStore.Core.Tests/Cluster/MemberInfoTests.cs @@ -1,5 +1,6 @@ using System; using System.Net; +using System.Reflection; using EventStore.Core.Data; using NUnit.Framework; @@ -41,4 +42,61 @@ public void member_with_ip_endpoint_should_equal() Assert.True(memberWithDnsEndPoint.Is(ipEndPoint)); Assert.True(memberWithDnsEndPoint.Is(dnsEndPoint)); } + + [Test] + public void internal_gossip_round_trip_preserves_the_grpc_replication_endpoint() + { + var replicationEndPoint = new DnsEndPoint("replication-node", 1112); + var httpEndPoint = new DnsEndPoint("public-node", 2113); + var member = EventStore.Core.Cluster.MemberInfo.Initial( + Guid.NewGuid(), + DateTime.UtcNow, + VNodeState.Unknown, + true, + httpEndPoint, + null, + 0, + 0, + false, + replicationEndPoint: replicationEndPoint); + + var grpc = ToGrpcClusterInfo(new EventStore.Core.Cluster.ClusterInfo(member)); + var roundTrip = FromGrpcClusterInfo(grpc); + + Assert.That(roundTrip.Members, Has.Length.EqualTo(1)); + Assert.That(roundTrip.Members[0].HttpEndPoint, Is.EqualTo(httpEndPoint)); + Assert.That(roundTrip.Members[0].ReplicationEndPoint, Is.EqualTo(replicationEndPoint)); + } + + [Test] + public void member_without_a_replication_endpoint_uses_its_http_endpoint() + { + var httpEndPoint = new DnsEndPoint("mixed-version-node", 2113); + var member = EventStore.Core.Cluster.MemberInfo.Initial( + Guid.NewGuid(), + DateTime.UtcNow, + VNodeState.Unknown, + true, + httpEndPoint, + null, + 0, + 0, + false); + var grpc = ToGrpcClusterInfo(new EventStore.Core.Cluster.ClusterInfo(member)); + grpc.Members[0].ReplicationEndPoint = null; + + var roundTrip = FromGrpcClusterInfo(grpc); + + Assert.That(roundTrip.Members[0].ReplicationEndPoint, Is.EqualTo(httpEndPoint)); + } + + private static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(EventStore.Core.Cluster.ClusterInfo clusterInfo) => + (EventStore.Cluster.ClusterInfo)typeof(EventStore.Core.Cluster.ClusterInfo) + .GetMethod("ToGrpcClusterInfo", BindingFlags.Static | BindingFlags.NonPublic) + .Invoke(null, [clusterInfo]); + + private static EventStore.Core.Cluster.ClusterInfo FromGrpcClusterInfo(EventStore.Cluster.ClusterInfo clusterInfo) => + (EventStore.Core.Cluster.ClusterInfo)typeof(EventStore.Core.Cluster.ClusterInfo) + .GetMethod("FromGrpcClusterInfo", BindingFlags.Static | BindingFlags.NonPublic) + .Invoke(null, [clusterInfo, null]); } diff --git a/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs b/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs index c36b939bca..d5b3f61487 100644 --- a/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs +++ b/src/EventStore.Core.Tests/Helpers/MiniClusterNode.cs @@ -23,6 +23,7 @@ using EventStore.Core.TransactionLog.Chunks; using EventStore.Plugins.Subsystems; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Server.Kestrel.Core; using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; @@ -41,6 +42,7 @@ public class MiniClusterNode private static readonly ILogger Log = Serilog.Log.ForContext>(); public IPEndPoint HttpEndPoint { get; } + public IPEndPoint ReplicationEndPoint { get; } public readonly int DebugIndex; @@ -58,7 +60,7 @@ public class MiniClusterNode public VNodeState NodeState = VNodeState.Unknown; private readonly IHost _host; - public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, + public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, IPEndPoint replicationEndPoint, EndPoint[] gossipSeeds, ISubsystem[] subsystems = null, bool enableTrustedAuth = false, int memTableSize = 1000, bool disableFlushToDisk = false, bool readOnlyReplica = false, int nodePriority = 0, @@ -72,6 +74,7 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, DebugIndex = debugIndex; HttpEndPoint = nodeEndPoint; + ReplicationEndPoint = replicationEndPoint; _dbPath = Path.Combine( pathname, @@ -112,6 +115,8 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, { NodeIp = HttpEndPoint.Address, NodePort = HttpEndPoint.Port, + ReplicationIp = ReplicationEndPoint.Address, + ReplicationPort = ReplicationEndPoint.Port, EnableTrustedAuth = enableTrustedAuth }, Database = new() @@ -196,7 +201,7 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, webHost .UseKestrel(o => { - o.Listen(HttpEndPoint, options => + void ConfigureHttps(ListenOptions options) { options.UseHttps(new HttpsConnectionAdapterOptions { @@ -214,7 +219,10 @@ public MiniClusterNode(string pathname, int debugIndex, IPEndPoint nodeEndPoint, return isValid; } }); - }); + } + + o.Listen(HttpEndPoint, ConfigureHttps); + o.Listen(ReplicationEndPoint, ConfigureHttps); }) .UseStartup(Node.Startup); }) diff --git a/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs b/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs index e4c7e658db..d8e41d20c9 100644 --- a/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs +++ b/src/EventStore.Core.Tests/Integration/Archive/when_archiving_and_restoring_a_cluster.cs @@ -125,6 +125,7 @@ protected override MiniClusterNode CreateNode( PathName, index, endpoints.NodeEndPoint, + endpoints.ReplicationEndPoint, gossipSeeds, readOnlyReplica: index == ArchiverNodeIndex, archiveOptions: _archiveOptions.Enabled ? _archiveOptions : null, diff --git a/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs b/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs index b225f884aa..b88b48a586 100644 --- a/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs +++ b/src/EventStore.Core.Tests/Integration/specification_with_cluster.cs @@ -24,10 +24,12 @@ public abstract class specification_with_cluster : Specif protected class Endpoints { public readonly IPEndPoint NodeEndPoint; + public readonly IPEndPoint ReplicationEndPoint; public IEnumerable Ports() { yield return NodeEndPoint.Port; + yield return ReplicationEndPoint.Port; } private readonly List _sockets; @@ -41,8 +43,12 @@ public Endpoints() var nodeEndpoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); nodeEndpoint.Bind(defaultLoopBack); _sockets.Add(nodeEndpoint); + var replicationEndpoint = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + replicationEndpoint.Bind(defaultLoopBack); + _sockets.Add(replicationEndpoint); NodeEndPoint = CopyEndpoint((IPEndPoint)nodeEndpoint.LocalEndPoint); + ReplicationEndPoint = CopyEndpoint((IPEndPoint)replicationEndpoint.LocalEndPoint); } public void DisposeSockets() @@ -149,7 +155,7 @@ protected virtual void BeforeNodesStart() protected virtual MiniClusterNode CreateNode(int index, Endpoints endpoints, EndPoint[] gossipSeeds, bool wait = true) => new( - PathName, index, endpoints.NodeEndPoint, + PathName, index, endpoints.NodeEndPoint, endpoints.ReplicationEndPoint, subsystems: Array.Empty(), gossipSeeds: gossipSeeds); [TearDown] diff --git a/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs b/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs index e45a056858..b8bfbcedfa 100644 --- a/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs +++ b/src/EventStore.Core.Tests/Regression/GrpcOnlySurfaceParityTests.cs @@ -5,6 +5,7 @@ using System.Threading.Tasks; using EventStore.ClusterNode.Components.Services; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http; using NUnit.Framework; namespace EventStore.Core.Tests.Regression; @@ -23,7 +24,7 @@ public void observability_payload_preserves_replication_visibility() } [Test] - public async Task shared_endpoint_connections_are_visible_only_while_active() + public async Task http_connections_are_visible_only_while_active() { var tracker = new NodeConnectionTracker(); var connection = new DefaultConnectionContext("grpc-connection") @@ -73,6 +74,36 @@ public async Task shared_endpoint_connections_are_visible_only_while_active() Assert.That(tracker.Snapshot(), Is.Empty); } + [TestCase(2113, "/streams", true)] + [TestCase(2113, "/event_store.replication.Replication/Replicate", false)] + [TestCase(1112, "/streams", false)] + [TestCase(1112, "/event_store.replication.Replication/Replicate", true)] + public void replication_listener_isolated_from_the_public_node_listener( + int localPort, + string path, + bool expected) + { + var policy = new ReplicationEndpointPolicy(new IPEndPoint(IPAddress.Loopback, 1112)); + var context = new DefaultHttpContext(); + context.Connection.LocalIpAddress = IPAddress.Loopback; + context.Connection.LocalPort = localPort; + context.Request.Path = path; + + Assert.That(policy.Allows(context), Is.EqualTo(expected)); + } + + [Test] + public void wildcard_replication_binding_matches_the_resolved_local_address() + { + var policy = new ReplicationEndpointPolicy(new IPEndPoint(IPAddress.Any, 1112)); + var context = new DefaultHttpContext(); + context.Connection.LocalIpAddress = IPAddress.Parse("192.0.2.1"); + context.Connection.LocalPort = 1112; + context.Request.Path = "/event_store.replication.Replication/Replicate"; + + Assert.That(policy.Allows(context), Is.True); + } + private sealed class TestDuplexPipe(PipeReader input, PipeWriter output) : IDuplexPipe { public PipeReader Input { get; } = input; diff --git a/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs b/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs index b053aaf076..9faa415dc9 100644 --- a/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs +++ b/src/EventStore.Core.Tests/Services/Replication/ReadOnlyReplica/connecting_to_read_only_replica.cs @@ -28,7 +28,7 @@ protected override MiniClusterNode CreateNode(int index, { var isReadOnly = index == 2; var node = new MiniClusterNode( - PathName, index, endpoints.NodeEndPoint, gossipSeeds, + PathName, index, endpoints.NodeEndPoint, endpoints.ReplicationEndPoint, gossipSeeds, readOnlyReplica: isReadOnly); if (wait && !isReadOnly) { diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs index 19697515ac..91ba8c4a35 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceFactoryTests.cs @@ -37,7 +37,30 @@ public void replication_client_uses_node_certificate_names_and_owns_its_http_cli Assert.That(nodeHttpClientFactory.AdditionalCertificateNames, Is.EqualTo(new[] { "cluster.internal" })); client.Dispose(); - Assert.That(nodeHttpClientFactory.Handler.Disposed, Is.True); + Assert.That(nodeHttpClientFactory.TransportHandler.Disposed, Is.True); + } + + [Test] + public void replication_client_normalizes_legacy_sub_second_heartbeat_values() + { + var nodeHttpClientFactory = new RecordingNodeHttpClientFactory(); + var factory = new ReplicationGrpcClientFactory( + Uri.UriSchemeHttps, + nodeHttpClientFactory, + TimeSpan.FromMilliseconds(700), + TimeSpan.FromMilliseconds(700)); + + using var client = factory.Create(new DnsEndPoint("leader.internal", 1112)); + + Assert.Multiple(() => + { + Assert.That(nodeHttpClientFactory.SocketsHandler.KeepAlivePingDelay, + Is.EqualTo(TimeSpan.FromSeconds(1))); + Assert.That(nodeHttpClientFactory.SocketsHandler.KeepAlivePingTimeout, + Is.EqualTo(TimeSpan.FromSeconds(1))); + Assert.That(nodeHttpClientFactory.SocketsHandler.KeepAlivePingPolicy, + Is.EqualTo(HttpKeepAlivePingPolicy.Always)); + }); } [Test] @@ -99,7 +122,8 @@ private sealed class TrackingReplicationGrpcClientFactory : IReplicationGrpcClie private sealed class RecordingNodeHttpClientFactory : INodeHttpClientFactory { - public RecordingHttpMessageHandler Handler { get; } = new(); + public RecordingHttpMessageHandler TransportHandler { get; } = new(); + public SocketsHttpHandler SocketsHandler { get; } = new(); public string[] AdditionalCertificateNames { get; private set; } public HttpClient CreateHttpClient( @@ -107,7 +131,8 @@ public HttpClient CreateHttpClient( Action configureSocketsHttpHandler = null) { AdditionalCertificateNames = additionalCertificateNames; - return new HttpClient(Handler); + configureSocketsHttpHandler?.Invoke(SocketsHandler); + return new HttpClient(TransportHandler); } } diff --git a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs index 5d85c015b5..dcd662b4f8 100644 --- a/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs +++ b/src/EventStore.Core.Tests/Services/Transport/Grpc/Replication/GrpcReplicaServiceSupervisorTests.cs @@ -22,7 +22,7 @@ public class GrpcReplicaServiceSupervisorTests [TestCase(false)] [TestCase(true)] - public async Task pre_replica_state_starts_and_tracks_a_stream_for_the_advertised_http_endpoints( + public async Task pre_replica_state_starts_and_tracks_a_stream_for_the_advertised_replication_endpoints( bool readOnlyReplica) { var fixture = CreateFixture(); @@ -37,7 +37,7 @@ public async Task pre_replica_state_starts_and_tracks_a_stream_for_the_advertise var request = fixture.Factory.Requests.Single(); Assert.Multiple(() => { - Assert.That(request.Endpoints.LeaderEndPoint, Is.EqualTo(fixture.Leader.HttpEndPoint)); + Assert.That(request.Endpoints.LeaderEndPoint, Is.EqualTo(fixture.Leader.ReplicationEndPoint)); Assert.That(request.Endpoints.AdvertisedReplicaEndPoint, Is.EqualTo(fixture.AdvertisedEndPoint)); Assert.That(request.Service.StartCalls, Is.EqualTo(1)); Assert.That(fixture.TrackedTasks.Single(), Is.SameAs(request.Service.Task)); @@ -410,7 +410,7 @@ private static Fixture CreateFixture( startException, createException, beforeCreateReturns); - var advertisedEndPoint = new DnsEndPoint("replica.internal", 2113); + var advertisedEndPoint = new DnsEndPoint("replica.internal", 1112); var trackedTasks = new List(); var supervisor = new GrpcReplicaServiceSupervisor( publisher, @@ -442,7 +442,8 @@ private static MemberInfo CreateLeader() => MemberInfo.ForVNode( 0, Guid.NewGuid(), 0, - false); + false, + replicationEndPoint: new DnsEndPoint("leader.replication.internal", 1112)); private static SystemMessage.StateChangeMessage CreateReplicaState( VNodeState state, diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs index 97b2903377..179fd7e1d8 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterNodeOptionsTests/when_building/with_default_settings.cs @@ -88,9 +88,12 @@ public void should_create_single_cluster_node() [Test] public void should_have_default_endpoint() { + var replicationEndPoint = new IPEndPoint(IPAddress.Loopback, 1112); var httpEndPoint = new IPEndPoint(IPAddress.Loopback, 2113); + Assert.AreEqual(replicationEndPoint, _node.NodeInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint, _node.NodeInfo.HttpEndPoint); + Assert.AreEqual(replicationEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.HttpEndPoint); } @@ -115,9 +118,12 @@ public void should_create_single_cluster_node() [Test] public void should_have_default_endpoints() { + var replicationEndPoint = new IPEndPoint(IPAddress.Loopback, 1112); var httpEndPoint = new IPEndPoint(IPAddress.Loopback, 2113); + Assert.AreEqual(replicationEndPoint, _node.NodeInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint, _node.NodeInfo.HttpEndPoint); + Assert.AreEqual(replicationEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.ReplicationEndPoint); Assert.AreEqual(httpEndPoint.ToDnsEndPoint(), _node.GossipAdvertiseInfo.HttpEndPoint); } diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs index 3caee89cc6..0278976316 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsValidatorTests.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using EventStore.Common.Exceptions; using EventStore.Core.Authentication; using EventStore.Core.Services; @@ -9,6 +10,43 @@ namespace EventStore.Core.XUnit.Tests.Configuration; // Some other tests are in ClusterNodeOptionsTests/when_building public class ClusterVNodeOptionsValidatorTests { + [Theory] + [InlineData("127.0.0.1", "127.0.0.1")] + [InlineData("0.0.0.0", "127.0.0.1")] + [InlineData("127.0.0.1", "0.0.0.0")] + public void node_and_replication_listeners_cannot_overlap(string nodeIp, string replicationIp) + { + var options = new ClusterVNodeOptions + { + Interface = new() + { + NodeIp = IPAddress.Parse(nodeIp), + NodePort = 2113, + ReplicationIp = IPAddress.Parse(replicationIp), + ReplicationPort = 2113, + } + }; + + Assert.Throws(() => ClusterVNodeOptionsValidator.Validate(options)); + } + + [Fact] + public void node_and_replication_listeners_can_use_the_same_port_on_distinct_addresses() + { + var options = new ClusterVNodeOptions + { + Interface = new() + { + NodeIp = IPAddress.Parse("127.0.0.1"), + NodePort = 2113, + ReplicationIp = IPAddress.Parse("127.0.0.2"), + ReplicationPort = 2113, + } + }; + + ClusterVNodeOptionsValidator.Validate(options); + } + [Theory] [InlineData(false, false, true)] [InlineData(false, true, true)] diff --git a/src/EventStore.Core/Cluster/ClusterInfo.cs b/src/EventStore.Core/Cluster/ClusterInfo.cs index 460c3c0586..a9b9cd9119 100644 --- a/src/EventStore.Core/Cluster/ClusterInfo.cs +++ b/src/EventStore.Core/Cluster/ClusterInfo.cs @@ -68,7 +68,11 @@ internal static ClusterInfo FromGrpcClusterInfo(EventStore.Cluster.ClusterInfo g x.AdvertiseHostToClientAs, (int)x.AdvertiseHttpPortToClientAs, x.LastCommitPosition, x.WriterCheckpoint, x.ChaserCheckpoint, x.EpochPosition, x.EpochNumber, Uuid.FromDto(x.EpochId).ToGuid(), x.NodePriority, - x.IsReadOnlyReplica, x.EsVersion == String.Empty ? null : x.EsVersion + x.IsReadOnlyReplica, x.EsVersion == String.Empty ? null : x.EsVersion, + x.ReplicationEndPoint is null + ? null + : new DnsEndPoint(x.ReplicationEndPoint.Address, (int)x.ReplicationEndPoint.Port) + .WithClusterDns(clusterDns) )).ToArray(); return new ClusterInfo(receivedMembers); } @@ -84,6 +88,9 @@ internal static EventStore.Cluster.ClusterInfo ToGrpcClusterInfo(ClusterInfo clu HttpEndPoint = new EventStore.Cluster.EndPoint( x.HttpEndPoint.GetHost(), (uint)x.HttpEndPoint.GetPort()), + ReplicationEndPoint = new EventStore.Cluster.EndPoint( + x.ReplicationEndPoint.GetHost(), + (uint)x.ReplicationEndPoint.GetPort()), LastCommitPosition = x.LastCommitPosition, WriterCheckpoint = x.WriterCheckpoint, ChaserCheckpoint = x.ChaserCheckpoint, diff --git a/src/EventStore.Core/Cluster/MemberInfo.cs b/src/EventStore.Core/Cluster/MemberInfo.cs index f0a22007f8..4fe81259c2 100644 --- a/src/EventStore.Core/Cluster/MemberInfo.cs +++ b/src/EventStore.Core/Cluster/MemberInfo.cs @@ -14,6 +14,7 @@ public class MemberInfo : IEquatable public readonly VNodeState State; public readonly bool IsAlive; + public readonly EndPoint ReplicationEndPoint; public readonly EndPoint HttpEndPoint; public readonly string AdvertiseHostToClientAs; public readonly int AdvertiseHttpPortToClientAs; @@ -31,11 +32,12 @@ public class MemberInfo : IEquatable public readonly string ESVersion; public static MemberInfo ForManager(Guid instanceId, DateTime timeStamp, bool isAlive, - EndPoint httpEndPoint, string esVersion = VersionInfo.UnknownVersion) + EndPoint httpEndPoint, string esVersion = VersionInfo.UnknownVersion, + EndPoint replicationEndPoint = null) { return new MemberInfo(instanceId, timeStamp, VNodeState.Manager, isAlive, httpEndPoint, null, 0, - -1, -1, -1, -1, -1, Guid.Empty, 0, false, esVersion); + -1, -1, -1, -1, -1, Guid.Empty, 0, false, esVersion, replicationEndPoint); } public static MemberInfo ForVNode(Guid instanceId, @@ -52,7 +54,8 @@ public static MemberInfo ForVNode(Guid instanceId, int epochNumber, Guid epochId, int nodePriority, - bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion) + bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion, + EndPoint replicationEndPoint = null) { if (state == VNodeState.Manager) { @@ -62,7 +65,8 @@ public static MemberInfo ForVNode(Guid instanceId, return new MemberInfo(instanceId, timeStamp, state, isAlive, httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, lastCommitPosition, writerCheckpoint, chaserCheckpoint, - epochPosition, epochNumber, epochId, nodePriority, isReadOnlyReplica, esVersion); + epochPosition, epochNumber, epochId, nodePriority, isReadOnlyReplica, esVersion, + replicationEndPoint); } public static MemberInfo Initial(Guid instanceId, @@ -73,7 +77,8 @@ public static MemberInfo Initial(Guid instanceId, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, int nodePriority, - bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion) + bool isReadOnlyReplica, string esVersion = VersionInfo.UnknownVersion, + EndPoint replicationEndPoint = null) { if (state == VNodeState.Manager) { @@ -82,13 +87,15 @@ public static MemberInfo Initial(Guid instanceId, return new MemberInfo(instanceId, timeStamp, state, isAlive, httpEndPoint, advertiseHostToClientAs, advertiseHttpPortToClientAs, - -1, -1, -1, -1, -1, Guid.Empty, nodePriority, isReadOnlyReplica, esVersion); + -1, -1, -1, -1, -1, Guid.Empty, nodePriority, isReadOnlyReplica, esVersion, + replicationEndPoint); } internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool isAlive, EndPoint httpEndPoint, string advertiseHostToClientAs, int advertiseHttpPortToClientAs, long lastCommitPosition, long writerCheckpoint, long chaserCheckpoint, - long epochPosition, int epochNumber, Guid epochId, int nodePriority, bool isReadOnlyReplica, string esVersion = null) + long epochPosition, int epochNumber, Guid epochId, int nodePriority, bool isReadOnlyReplica, + string esVersion = null, EndPoint replicationEndPoint = null) { Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); @@ -98,6 +105,7 @@ internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool State = state; IsAlive = isAlive; + ReplicationEndPoint = replicationEndPoint ?? httpEndPoint; HttpEndPoint = httpEndPoint; AdvertiseHostToClientAs = advertiseHostToClientAs; AdvertiseHttpPortToClientAs = advertiseHttpPortToClientAs; @@ -118,7 +126,8 @@ internal MemberInfo(Guid instanceId, DateTime timeStamp, VNodeState state, bool public bool Is(EndPoint endPoint) { - return endPoint != null && HttpEndPoint.EndPointEquals(endPoint); + return endPoint != null && + (HttpEndPoint.EndPointEquals(endPoint) || ReplicationEndPoint.EndPointEquals(endPoint)); } public MemberInfo Updated(DateTime utcNow, @@ -144,7 +153,7 @@ public MemberInfo Updated(DateTime utcNow, epoch != null ? epoch.EpochNumber : EpochNumber, epoch != null ? epoch.EpochId : EpochId, nodePriority ?? NodePriority, - IsReadOnlyReplica, esVersion ?? ESVersion); + IsReadOnlyReplica, esVersion ?? ESVersion, ReplicationEndPoint); } public override string ToString() @@ -152,12 +161,12 @@ public override string ToString() if (State == VNodeState.Manager) { return - $"MAN {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, {HttpEndPoint}] | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; + $"MAN {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, {ReplicationEndPoint}, {HttpEndPoint}] | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; } return $"Priority: {NodePriority} VND {InstanceId:B} <{(IsAlive ? "LIVE" : "DEAD")}> [{State}, " + - $"{HttpEndPoint}, (ADVERTISED: HTTP:{AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}), " + + $"Replication:{ReplicationEndPoint}, {HttpEndPoint}, (ADVERTISED: HTTP:{AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}), " + $"Version: {ESVersion}] " + $"{LastCommitPosition}/{WriterCheckpoint}/{ChaserCheckpoint}/E{EpochNumber}@{EpochPosition}:{EpochId:B} | {TimeStamp:yyyy-MM-dd HH:mm:ss.fff}"; } @@ -178,6 +187,7 @@ public bool Equals(MemberInfo other) return other.InstanceId == InstanceId && other.State == State && other.IsAlive == IsAlive + && Equals(other.ReplicationEndPoint, ReplicationEndPoint) && Equals(other.HttpEndPoint, HttpEndPoint) && other.AdvertiseHostToClientAs == AdvertiseHostToClientAs && other.AdvertiseHttpPortToClientAs == AdvertiseHttpPortToClientAs @@ -216,6 +226,7 @@ public override int GetHashCode() int result = InstanceId.GetHashCode(); result = (result * 397) ^ State.GetHashCode(); result = (result * 397) ^ IsAlive.GetHashCode(); + result = (result * 397) ^ ReplicationEndPoint.GetHashCode(); result = (result * 397) ^ HttpEndPoint.GetHashCode(); result = (result * 397) ^ (AdvertiseHostToClientAs != null ? AdvertiseHostToClientAs.GetHashCode() : 0); result = (result * 397) ^ AdvertiseHttpPortToClientAs.GetHashCode(); diff --git a/src/EventStore.Core/ClusterVNode.cs b/src/EventStore.Core/ClusterVNode.cs index 1629df49e1..aa60c45689 100644 --- a/src/EventStore.Core/ClusterVNode.cs +++ b/src/EventStore.Core/ClusterVNode.cs @@ -291,10 +291,14 @@ public ClusterVNode(ClusterVNodeOptions options, OptionsFormatter.LogConfig("Archive", archiveOptions); var httpEndPoint = new IPEndPoint(options.Interface.NodeIp, options.Interface.NodePort); + var replicationEndPoint = new IPEndPoint( + options.Interface.ReplicationIp, + options.Interface.ReplicationPort); Log.Information("Quorum size set to {quorum}.", options.Cluster.QuorumSize); - NodeInfo = new VNodeInfo(instanceId.Value, debugIndex, httpEndPoint, options.Cluster.ReadOnlyReplica); + NodeInfo = new VNodeInfo(instanceId.Value, debugIndex, httpEndPoint, options.Cluster.ReadOnlyReplica, + replicationEndPoint); var metricsConfiguration = MetricsConfiguration.Get(configuration); var trackers = new Trackers(); @@ -876,21 +880,39 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() { var nodeIpAddress = options.Interface.NodeIp; var hostToAdvertise = options.Interface.NodeHostAdvertiseAs ?? nodeIpAddress.ToString(); + var replicationIpAddress = options.Interface.ReplicationIp; + var replicationHostToAdvertise = + options.Interface.ReplicationHostAdvertiseAs ?? replicationIpAddress.ToString(); - if (nodeIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) + if ((nodeIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) || + (replicationIpAddress.Equals(IPAddress.Any) && options.Interface.ReplicationHostAdvertiseAs == null)) { IPAddress nonLoopbackAddress = IPFinder.GetNonLoopbackAddress(); - hostToAdvertise = + var addressToAdvertise = (options.Cluster.ClusterSize > 1 ? nonLoopbackAddress : IPAddress.Loopback).ToString(); + if (nodeIpAddress.Equals(IPAddress.Any) && options.Interface.NodeHostAdvertiseAs == null) + { + hostToAdvertise = addressToAdvertise; + } + + if (replicationIpAddress.Equals(IPAddress.Any) && + options.Interface.ReplicationHostAdvertiseAs == null) + { + replicationHostToAdvertise = addressToAdvertise; + } } var httpEndPoint = new DnsEndPoint(hostToAdvertise, options.Interface.NodePortAdvertiseAs > 0 ? options.Interface.NodePortAdvertiseAs : NodeInfo.HttpEndPoint.GetPort()); + var advertisedReplicationEndPoint = new DnsEndPoint(replicationHostToAdvertise, + options.Interface.ReplicationTcpPortAdvertiseAs > 0 + ? options.Interface.ReplicationTcpPortAdvertiseAs + : NodeInfo.ReplicationEndPoint.GetPort()); return new GossipAdvertiseInfo(httpEndPoint, options.Interface.AdvertiseHostToClientAs, - options.Interface.AdvertiseNodePortToClientAs); + options.Interface.AdvertiseNodePortToClientAs, advertisedReplicationEndPoint); } _httpService = new KestrelHttpService(_mainQueue, NodeInfo.HttpEndPoint); @@ -1385,7 +1407,8 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() GossipAdvertiseInfo.HttpEndPoint, GossipAdvertiseInfo.AdvertiseHostToClientAs, GossipAdvertiseInfo.AdvertiseHttpPortToClientAs, - options.Cluster.NodePriority, options.Cluster.ReadOnlyReplica, VersionInfo.Version); + options.Cluster.NodePriority, options.Cluster.ReadOnlyReplica, VersionInfo.Version, + GossipAdvertiseInfo.ReplicationEndPoint); // ELECTIONS TRACKER _mainBus.Subscribe(trackers.ElectionCounterTracker); @@ -1429,13 +1452,17 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() _grpcReplicaServiceSupervisor = new GrpcReplicaServiceSupervisor( _mainQueue, new GrpcReplicaServiceFactory( - new ReplicationGrpcClientFactory(uriScheme, _nodeHttpClientFactory), + new ReplicationGrpcClientFactory( + uriScheme, + _nodeHttpClientFactory, + TimeSpan.FromMilliseconds(options.Interface.ReplicationHeartbeatInterval), + TimeSpan.FromMilliseconds(options.Interface.ReplicationHeartbeatTimeout)), new ReplicaSubscriptionDataSource(Db, epochManager), NodeInfo.InstanceId, options.Cluster.ReadOnlyReplica ? ReplicaPromotability.NonPromotable : ReplicaPromotability.Promotable), - GossipAdvertiseInfo.HttpEndPoint, + GossipAdvertiseInfo.ReplicationEndPoint, AddTask); _mainBus.Subscribe(_grpcReplicaServiceSupervisor); _mainBus.Subscribe(_grpcReplicaServiceSupervisor); @@ -2164,5 +2191,5 @@ private void ReloadCertificates(ClusterVNodeOptions options) } public override string ToString() => - $"[{NodeInfo.InstanceId:B}, {NodeInfo.HttpEndPoint}]"; + $"[{NodeInfo.InstanceId:B}, {NodeInfo.ReplicationEndPoint}, {NodeInfo.HttpEndPoint}]"; } diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs index 39b8b7a729..65d89fa37e 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs @@ -178,10 +178,10 @@ public record ApplicationOptions [Description("The maximum size of appends, in bytes. May not exceed 16MB.")] public int MaxAppendSize { get; init; } = 1_024 * 1_024; - [Description("Disable authentication, authorization, and TLS on the shared HTTP/gRPC listener.")] + [Description("Disable authentication, authorization, and TLS on the HTTP/gRPC listeners.")] public bool Insecure { get; init; } = false; - [Description("Disable TLS on the shared HTTP/gRPC listener while keeping authentication and authorization enabled.")] + [Description("Disable TLS on the HTTP/gRPC listeners while keeping authentication and authorization enabled.")] public bool DisableTls { get; init; } = false; public bool AuthDisabled() => Insecure; @@ -620,15 +620,24 @@ public record GrpcOptions [Description("Interface Options")] public record InterfaceOptions { + [Description("The IP address used by the gRPC replication listener.")] + public IPAddress ReplicationIp { get; init; } = IPAddress.Loopback; + [Description("The IP Address for the node.")] public IPAddress NodeIp { get; init; } = IPAddress.Loopback; [Description("The Port to run the HTTP server on.")] public int NodePort { get; init; } = 2113; + [Description("The port used by the gRPC replication listener.")] + public int ReplicationPort { get; init; } = 1112; + [Description("Advertise the Node's host name to other nodes and external clients as.")] public string? NodeHostAdvertiseAs { get; init; } = null; + [Description("Advertise the gRPC replication host name to other nodes in the cluster as.")] + public string? ReplicationHostAdvertiseAs { get; init; } = null; + [Description("Advertise Host in Gossip to Client As.")] public string? AdvertiseHostToClientAs { get; init; } = null; @@ -638,6 +647,17 @@ public record InterfaceOptions [Description("Advertise Http Port As.")] public int NodePortAdvertiseAs { get; init; } = 0; + [Description("Advertise the gRPC replication port as.")] + public int ReplicationTcpPortAdvertiseAs { get; init; } = 0; + + [Description("Keepalive ping timeout for gRPC replication connections. Values below 1000 ms use the HTTP/2 minimum of 1000 ms."), + Unit("ms")] + public int ReplicationHeartbeatTimeout { get; init; } = 700; + + [Description("Keepalive ping interval for gRPC replication connections. Values below 1000 ms use the HTTP/2 minimum of 1000 ms."), + Unit("ms")] + public int ReplicationHeartbeatInterval { get; init; } = 700; + [Description("Whether to allow local connections via a UNIX domain socket.")] public bool EnableUnixSocket { get; init; } = false; diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs index b7555bf60c..5665c3bd26 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs @@ -98,6 +98,16 @@ public static ClusterVNodeOptions WithNodeEndpointOn( this ClusterVNodeOptions options, IPEndPoint endPoint) => options with { Interface = options.Interface with { NodeIp = endPoint.Address, NodePort = endPoint.Port } }; + /// + /// Sets the endpoint used by node-to-node gRPC replication. + /// + public static ClusterVNodeOptions WithReplicationEndpointOn( + this ClusterVNodeOptions options, IPEndPoint endPoint) => + options with + { + Interface = options.Interface with { ReplicationIp = endPoint.Address, ReplicationPort = endPoint.Port } + }; + /// /// Sets up the External Host that would be advertised /// @@ -108,6 +118,20 @@ public static ClusterVNodeOptions AdvertiseExternalHostAs(this ClusterVNodeOptions options, EndPoint endPoint) => options with { Interface = options.Interface with { NodeHostAdvertiseAs = endPoint.GetHost(), } }; + /// + /// Sets the endpoint advertised to other nodes for gRPC replication. + /// + public static ClusterVNodeOptions AdvertiseReplicationHostAs( + this ClusterVNodeOptions options, EndPoint endPoint) => + options with + { + Interface = options.Interface with + { + ReplicationHostAdvertiseAs = endPoint.GetHost(), + ReplicationTcpPortAdvertiseAs = endPoint.GetPort() + } + }; + /// /// /// The diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs index cc0ad32d0a..35d4a1312a 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsValidator.cs @@ -27,6 +27,28 @@ public static void Validate(ClusterVNodeOptions options) throw new ArgumentNullException(nameof(options.Interface.NodeIp)); } + if (options.Interface.ReplicationIp == null) + { + throw new ArgumentNullException(nameof(options.Interface.ReplicationIp)); + } + + if (options.Interface.NodePort == options.Interface.ReplicationPort && + EndpointsOverlap(options.Interface.NodeIp, options.Interface.ReplicationIp)) + { + throw new ArgumentException( + $"{nameof(options.Interface.NodePort)} and {nameof(options.Interface.ReplicationPort)} cannot bind the same endpoint."); + } + + if (options.Interface.ReplicationHeartbeatInterval <= 0) + { + throw new ArgumentOutOfRangeException(nameof(options.Interface.ReplicationHeartbeatInterval)); + } + + if (options.Interface.ReplicationHeartbeatTimeout <= 0) + { + throw new ArgumentOutOfRangeException(nameof(options.Interface.ReplicationHeartbeatTimeout)); + } + if (options.Cluster.ClusterSize <= 0) { throw new ArgumentOutOfRangeException(nameof(options.Cluster.ClusterSize), options.Cluster.ClusterSize, @@ -169,4 +191,11 @@ public static bool ValidateForStartup(ClusterVNodeOptions options) return true; } + private static bool EndpointsOverlap(System.Net.IPAddress first, System.Net.IPAddress second) => + first.Equals(second) || + first.Equals(System.Net.IPAddress.Any) || + second.Equals(System.Net.IPAddress.Any) || + first.Equals(System.Net.IPAddress.IPv6Any) || + second.Equals(System.Net.IPAddress.IPv6Any); + } diff --git a/src/EventStore.Core/Data/GossipAdvertiseInfo.cs b/src/EventStore.Core/Data/GossipAdvertiseInfo.cs index d60f980c21..dc8de85350 100644 --- a/src/EventStore.Core/Data/GossipAdvertiseInfo.cs +++ b/src/EventStore.Core/Data/GossipAdvertiseInfo.cs @@ -5,14 +5,17 @@ namespace EventStore.Core.Data { public class GossipAdvertiseInfo { + public DnsEndPoint ReplicationEndPoint { get; } public DnsEndPoint HttpEndPoint { get; } public string AdvertiseHostToClientAs { get; } public int AdvertiseHttpPortToClientAs { get; } public GossipAdvertiseInfo(DnsEndPoint httpEndPoint, - string advertiseHostToClientAs, int advertiseHttpPortToClientAs) + string advertiseHostToClientAs, int advertiseHttpPortToClientAs, + DnsEndPoint replicationEndPoint = null) { Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); + ReplicationEndPoint = replicationEndPoint ?? httpEndPoint; HttpEndPoint = httpEndPoint; AdvertiseHostToClientAs = advertiseHostToClientAs; AdvertiseHttpPortToClientAs = advertiseHttpPortToClientAs; @@ -20,7 +23,7 @@ public GossipAdvertiseInfo(DnsEndPoint httpEndPoint, public override string ToString() { - return $"Http: {HttpEndPoint}, HttpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}"; + return $"Replication: {ReplicationEndPoint}, Http: {HttpEndPoint}, HttpAdvertiseToClientAs: {AdvertiseHostToClientAs}:{AdvertiseHttpPortToClientAs}"; } } } diff --git a/src/EventStore.Core/Data/VNodeInfo.cs b/src/EventStore.Core/Data/VNodeInfo.cs index 6d1407d038..9361344b21 100644 --- a/src/EventStore.Core/Data/VNodeInfo.cs +++ b/src/EventStore.Core/Data/VNodeInfo.cs @@ -8,29 +8,32 @@ public class VNodeInfo { public readonly Guid InstanceId; public readonly int DebugIndex; + public readonly EndPoint ReplicationEndPoint; public readonly EndPoint HttpEndPoint; public readonly bool IsReadOnlyReplica; public VNodeInfo(Guid instanceId, int debugIndex, EndPoint httpEndPoint, - bool isReadOnlyReplica) + bool isReadOnlyReplica, EndPoint replicationEndPoint = null) { Ensure.NotEmptyGuid(instanceId, "instanceId"); Ensure.NotNull(httpEndPoint, nameof(httpEndPoint)); DebugIndex = debugIndex; InstanceId = instanceId; + ReplicationEndPoint = replicationEndPoint ?? httpEndPoint; HttpEndPoint = httpEndPoint; IsReadOnlyReplica = isReadOnlyReplica; } public bool Is(EndPoint endPoint) { - return endPoint != null && HttpEndPoint.Equals(endPoint); + return endPoint != null && + (HttpEndPoint.Equals(endPoint) || ReplicationEndPoint.Equals(endPoint)); } public override string ToString() { - return $"InstanceId: {InstanceId:B}, HttpEndPoint: {HttpEndPoint}, IsReadOnlyReplica: {IsReadOnlyReplica}"; + return $"InstanceId: {InstanceId:B}, ReplicationEndPoint: {ReplicationEndPoint}, HttpEndPoint: {HttpEndPoint}, IsReadOnlyReplica: {IsReadOnlyReplica}"; } } } diff --git a/src/EventStore.Core/Services/ElectionsService.cs b/src/EventStore.Core/Services/ElectionsService.cs index e3f7abe195..a220a232ce 100644 --- a/src/EventStore.Core/Services/ElectionsService.cs +++ b/src/EventStore.Core/Services/ElectionsService.cs @@ -141,7 +141,7 @@ public ElectionsService(IPublisher publisher, memberInfo.AdvertiseHostToClientAs, memberInfo.AdvertiseHttpPortToClientAs, ownInfo.LastCommitPosition, ownInfo.WriterCheckpoint, ownInfo.ChaserCheckpoint, ownInfo.EpochPosition, ownInfo.EpochNumber, ownInfo.EpochId, ownInfo.NodePriority, - memberInfo.IsReadOnlyReplica, VersionInfo.Version) + memberInfo.IsReadOnlyReplica, VersionInfo.Version, memberInfo.ReplicationEndPoint) }; } diff --git a/src/EventStore.Core/Services/Gossip/NodeGossipService.cs b/src/EventStore.Core/Services/Gossip/NodeGossipService.cs index 2007807a7c..cca9ede01e 100644 --- a/src/EventStore.Core/Services/Gossip/NodeGossipService.cs +++ b/src/EventStore.Core/Services/Gossip/NodeGossipService.cs @@ -67,7 +67,8 @@ protected override MemberInfo GetInitialMe() lastEpoch == null ? -1 : lastEpoch.EpochNumber, lastEpoch == null ? Guid.Empty : lastEpoch.EpochId, _nodePriority, - _memberInfo.IsReadOnlyReplica, _memberInfo.ESVersion); + _memberInfo.IsReadOnlyReplica, _memberInfo.ESVersion, + _memberInfo.ReplicationEndPoint); } protected override MemberInfo GetUpdatedMe(MemberInfo me) diff --git a/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs b/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs index 52d2f29e3d..d4b8bf3c17 100644 --- a/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs +++ b/src/EventStore.Core/Services/Replication/GrpcReplicaServiceSupervisor.cs @@ -277,7 +277,7 @@ private async ValueTask ReplaceActiveAsync( { active.Service = _factory.Create( new FencedPublisher(this, active), - new GrpcReplicaConnectionEndpoints(leader.HttpEndPoint, _advertisedReplicaEndPoint)); + new GrpcReplicaConnectionEndpoints(leader.ReplicationEndPoint, _advertisedReplicaEndPoint)); SetActive(active); var task = active.Service.Start(); _trackTask(task); @@ -296,7 +296,8 @@ private async ValueTask ReplaceActiveAsync( { ClearActive(active); await StopAsync(active.Service); - Log.Warning(exception, "Failed to start replication stream to [{leaderEndPoint}].", leader.HttpEndPoint); + Log.Warning(exception, "Failed to start replication stream to [{leaderEndPoint}].", + leader.ReplicationEndPoint); _publisher.Publish(new ReplicationMessage.LeaderConnectionFailed( leaderConnectionCorrelationId, leader)); } diff --git a/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs b/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs index 4c8f0ea9e9..463f63a119 100644 --- a/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs +++ b/src/EventStore.Core/Services/Replication/ReplicationGrpcClient.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Net; +using System.Net.Http; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; @@ -32,24 +33,37 @@ public interface IReplicationGrpcCall : IDisposable public sealed class ReplicationGrpcClientFactory : IReplicationGrpcClientFactory { + private static readonly TimeSpan DefaultKeepAlivePingDelay = TimeSpan.FromMilliseconds(700); + private static readonly TimeSpan DefaultKeepAlivePingTimeout = TimeSpan.FromMilliseconds(700); + private static readonly TimeSpan MinimumKeepAlivePingValue = TimeSpan.FromSeconds(1); private readonly string _uriScheme; private readonly INodeHttpClientFactory _nodeHttpClientFactory; + private readonly TimeSpan _keepAlivePingDelay; + private readonly TimeSpan _keepAlivePingTimeout; public ReplicationGrpcClientFactory( string uriScheme, - INodeHttpClientFactory nodeHttpClientFactory) + INodeHttpClientFactory nodeHttpClientFactory, + TimeSpan? keepAlivePingDelay = null, + TimeSpan? keepAlivePingTimeout = null) { Ensure.NotNullOrEmpty(uriScheme, nameof(uriScheme)); Ensure.NotNull(nodeHttpClientFactory, nameof(nodeHttpClientFactory)); _uriScheme = uriScheme; _nodeHttpClientFactory = nodeHttpClientFactory; + _keepAlivePingDelay = NormalizeKeepAliveValue(keepAlivePingDelay ?? DefaultKeepAlivePingDelay); + _keepAlivePingTimeout = NormalizeKeepAliveValue(keepAlivePingTimeout ?? DefaultKeepAlivePingTimeout); } + private static TimeSpan NormalizeKeepAliveValue(TimeSpan value) => + value < MinimumKeepAlivePingValue ? MinimumKeepAlivePingValue : value; + public IReplicationGrpcClient Create(EndPoint leaderEndPoint) { Ensure.NotNull(leaderEndPoint, nameof(leaderEndPoint)); - return new ReplicationGrpcClient(_uriScheme, leaderEndPoint, _nodeHttpClientFactory); + return new ReplicationGrpcClient(_uriScheme, leaderEndPoint, _nodeHttpClientFactory, + _keepAlivePingDelay, _keepAlivePingTimeout); } } @@ -60,9 +74,18 @@ internal sealed class ReplicationGrpcClient : IReplicationGrpcClient public ReplicationGrpcClient( string uriScheme, EndPoint leaderEndPoint, - INodeHttpClientFactory nodeHttpClientFactory) + INodeHttpClientFactory nodeHttpClientFactory, + TimeSpan keepAlivePingDelay, + TimeSpan keepAlivePingTimeout) { - var httpClient = nodeHttpClientFactory.CreateHttpClient(leaderEndPoint.GetOtherNames()); + var httpClient = nodeHttpClientFactory.CreateHttpClient( + leaderEndPoint.GetOtherNames(), + handler => + { + handler.KeepAlivePingDelay = keepAlivePingDelay; + handler.KeepAlivePingTimeout = keepAlivePingTimeout; + handler.KeepAlivePingPolicy = HttpKeepAlivePingPolicy.Always; + }); httpClient.Timeout = Timeout.InfiniteTimeSpan; httpClient.DefaultRequestVersion = new Version(2, 0); diff --git a/src/Protos/Grpc/cluster.proto b/src/Protos/Grpc/cluster.proto index 93d75be19f..e53b81756b 100644 --- a/src/Protos/Grpc/cluster.proto +++ b/src/Protos/Grpc/cluster.proto @@ -145,4 +145,5 @@ message MemberInfo { reserved 20; reserved "advertise_tcp_port_to_client_as"; string es_version = 21; + EndPoint replication_end_point = 22; } From e32db30cceff90cde437bf8a534e413393b3c38d Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 12 Sep 2026 17:13:41 -0400 Subject: [PATCH 3/4] fix(cluster): preserve isolated replication reachability Signed-off-by: Yordis Prieto --- Dockerfile | 5 +- docker-compose.yml | 3 + docs/networking.md | 20 +++--- docs/upgrade-guide.md | 4 ++ samples/server/docker-compose-cluster.yaml | 7 +- shared.env | 1 + .../Configuration/ClusterVNodeOptionsTests.cs | 69 +++++++++++++++++++ src/EventStore.Core/ClusterVNode.cs | 5 +- .../Configuration/ClusterVNodeOptions.cs | 11 +++ .../ClusterVNodeOptionsExtensions.cs | 2 +- 10 files changed, 111 insertions(+), 16 deletions(-) diff --git a/Dockerfile b/Dockerfile index 2e2d9dd03e..edbfe3228d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -83,11 +83,12 @@ RUN mkdir -p /var/lib/eventstore && \ USER eventstore -RUN printf "NodeIp: 0.0.0.0" >> /etc/eventstore/eventstore.conf +RUN printf "NodeIp: 0.0.0.0\n\ +ReplicationIp: 0.0.0.0" >> /etc/eventstore/eventstore.conf VOLUME /var/lib/eventstore /var/log/eventstore -EXPOSE 2113/tcp +EXPOSE 1112/tcp 2113/tcp HEALTHCHECK --interval=5s --timeout=5s --retries=24 \ CMD curl --fail --insecure https://localhost:2113/-/liveness || curl --fail http://localhost:2113/-/liveness || exit 1 diff --git a/docker-compose.yml b/docker-compose.yml index 4c7b8f72ef..b63bee223e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,6 +18,7 @@ services: environment: - EVENTSTORE_GOSSIP_SEED=172.30.240.12:2113,172.30.240.13:2113 - EVENTSTORE_NODE_IP=172.30.240.11 + - EVENTSTORE_REPLICATION_IP=172.30.240.11 - EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node/node.crt - EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node/node.key - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=127.0.0.1 @@ -45,6 +46,7 @@ services: environment: - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.13:2113 - EVENTSTORE_NODE_IP=172.30.240.12 + - EVENTSTORE_REPLICATION_IP=172.30.240.12 - EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node/node.crt - EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node/node.key - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=127.0.0.1 @@ -72,6 +74,7 @@ services: environment: - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.12:2113 - EVENTSTORE_NODE_IP=172.30.240.13 + - EVENTSTORE_REPLICATION_IP=172.30.240.13 - EVENTSTORE_CERTIFICATE_FILE=/etc/eventstore/certs/node/node.crt - EVENTSTORE_CERTIFICATE_PRIVATE_KEY_FILE=/etc/eventstore/certs/node/node.key - EVENTSTORE_ADVERTISE_HOST_TO_CLIENT_AS=127.0.0.1 diff --git a/docs/networking.md b/docs/networking.md index cff4ea7f02..c5d7a346c0 100644 --- a/docs/networking.md +++ b/docs/networking.md @@ -237,15 +237,17 @@ Please note that the `ExtHostAdvertiseAs` parameter has been deprecated as of ve If the bound replication address or port is not reachable as-is from the other nodes, override the endpoint advertised through internal gossip. -| Format | Host syntax | Port syntax | -|:---------------------|:---------------------------------------------|:-------------------------------------------------| -| Command line | `--replication-host-advertise-as` | `--replication-tcp-port-advertise-as` | -| YAML | `ReplicationHostAdvertiseAs` | `ReplicationTcpPortAdvertiseAs` | -| Environment variable | `EVENTSTORE_REPLICATION_HOST_ADVERTISE_AS` | `EVENTSTORE_REPLICATION_TCP_PORT_ADVERTISE_AS` | - -The port option retains its existing name for configuration compatibility, but -the advertised endpoint now carries gRPC replication rather than the removed -TCP replication protocol. +| Format | Host syntax | Port syntax | +|:---------------------|:---------------------------------------------|:---------------------------------------------| +| Command line | `--replication-host-advertise-as` | `--replication-port-advertise-as` | +| YAML | `ReplicationHostAdvertiseAs` | `ReplicationPortAdvertiseAs` | +| Environment variable | `EVENTSTORE_REPLICATION_HOST_ADVERTISE_AS` | `EVENTSTORE_REPLICATION_PORT_ADVERTISE_AS` | + +::: warning +`ReplicationTcpPortAdvertiseAs` is deprecated. Use +`ReplicationPortAdvertiseAs` for the gRPC replication endpoint. If both are +configured, `ReplicationPortAdvertiseAs` takes precedence. +::: ### Advertise to clients diff --git a/docs/upgrade-guide.md b/docs/upgrade-guide.md index fc1a272634..576b7b4a42 100644 --- a/docs/upgrade-guide.md +++ b/docs/upgrade-guide.md @@ -60,6 +60,10 @@ current product direction is: - No legacy EventStore TCP client protocol listener. - No proprietary plugin configuration. +Use `ReplicationPortAdvertiseAs` for replication endpoint translation. The +previous `ReplicationTcpPortAdvertiseAs` name remains accepted for configuration +compatibility and produces a deprecation warning. + If a setting is no longer documented, remove it rather than carrying it forward silently. diff --git a/samples/server/docker-compose-cluster.yaml b/samples/server/docker-compose-cluster.yaml index 6101d3190f..249a130c52 100644 --- a/samples/server/docker-compose-cluster.yaml +++ b/samples/server/docker-compose-cluster.yaml @@ -1,6 +1,6 @@ # Before starting, provide certs/ca/ca.crt and node.crt/node.key under each node directory. -# Each node certificate must include localhost, its service DNS name, and its configured node IP in its -# subject alternative names. The node IPs are 172.30.240.11, 172.30.240.12, and 172.30.240.13. +# Each node certificate must include localhost, its service DNS name, and its configured node and replication IP in +# its subject alternative names. The node IPs are 172.30.240.11, 172.30.240.12, and 172.30.240.13. # Keep each node private key readable only by the account running the container. services: node1.eventstore: &template @@ -11,6 +11,7 @@ services: - vars.env environment: - EVENTSTORE_NODE_IP=172.30.240.11 + - EVENTSTORE_REPLICATION_IP=172.30.240.11 - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2111 - EVENTSTORE_GOSSIP_SEED=172.30.240.12:2113,172.30.240.13:2113 - EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/certs/ca @@ -44,6 +45,7 @@ services: container_name: node2.eventstore environment: - EVENTSTORE_NODE_IP=172.30.240.12 + - EVENTSTORE_REPLICATION_IP=172.30.240.12 - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2112 - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.13:2113 - EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/certs/ca @@ -76,6 +78,7 @@ services: container_name: node3.eventstore environment: - EVENTSTORE_NODE_IP=172.30.240.13 + - EVENTSTORE_REPLICATION_IP=172.30.240.13 - EVENTSTORE_ADVERTISE_NODE_PORT_TO_CLIENT_AS=2113 - EVENTSTORE_GOSSIP_SEED=172.30.240.11:2113,172.30.240.12:2113 - EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/certs/ca diff --git a/shared.env b/shared.env index 1bce5012ba..52fb417492 100644 --- a/shared.env +++ b/shared.env @@ -1,5 +1,6 @@ EVENTSTORE_CLUSTER_SIZE=3 EVENTSTORE_RUN_PROJECTIONS=All +EVENTSTORE_REPLICATION_PORT=1112 EVENTSTORE_NODE_PORT=2113 EVENTSTORE_TRUSTED_ROOT_CERTIFICATES_PATH=/etc/eventstore/certs/ca EVENTSTORE_DISCOVER_VIA_DNS=false diff --git a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs index 5aa70e05a0..f18a6018cf 100644 --- a/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs +++ b/src/EventStore.Core.XUnit.Tests/Configuration/ClusterVNodeOptionsTests.cs @@ -95,6 +95,75 @@ public void valid_parameters() Assert.Empty(values); } + [Fact] + public void replication_port_advertise_as_is_configurable() + { + var options = GetOptions("--replication-port-advertise-as 2112"); + + options.Interface.ReplicationPortAdvertiseAs.Should().Be(2112); + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + Assert.Empty(options.Unknown.Options); + } + + [Fact] + public void replication_port_advertise_as_is_configurable_from_the_environment() + { + var configuration = new ConfigurationBuilder() + .AddEventStoreDefaultValues() + .AddEventStoreEnvironmentVariables(( + "EVENTSTORE_REPLICATION_PORT_ADVERTISE_AS", + "2112")) + .Build(); + + var options = ClusterVNodeOptions.FromConfiguration(configuration); + + options.Interface.ReplicationPortAdvertiseAs.Should().Be(2112); + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + } + + [Fact] + public void replication_port_advertise_as_is_configurable_from_yaml() + { + var yamlPath = Path.Combine( + Path.GetTempPath(), + $"eventstore-replication-advertise-{Guid.NewGuid():N}.conf"); + + try + { + File.WriteAllText(yamlPath, "ReplicationPortAdvertiseAs: 2112"); + var configuration = EventStoreConfiguration.Build(["--config", yamlPath], new Hashtable()); + + var options = ClusterVNodeOptions.FromConfiguration(configuration); + + options.Interface.ReplicationPortAdvertiseAs.Should().Be(2112); + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + } + finally + { + File.Delete(yamlPath); + } + } + + [Fact] + public void deprecated_replication_tcp_port_advertise_as_remains_compatible() + { + var options = GetOptions("--replication-tcp-port-advertise-as 3112"); + + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(3112); + options.GetDeprecationWarnings().Should().Contain( + "ReplicationTcpPortAdvertiseAs setting has been deprecated"); + Assert.Empty(options.Unknown.Options); + } + + [Fact] + public void replication_port_advertise_as_takes_precedence_over_deprecated_alias() + { + var options = GetOptions( + "--replication-port-advertise-as 2112 --replication-tcp-port-advertise-as 3112"); + + options.Interface.GetReplicationPortAdvertiseAs().Should().Be(2112); + } + [Fact] public void grpc_compression_level_defaults_to_optimal() { diff --git a/src/EventStore.Core/ClusterVNode.cs b/src/EventStore.Core/ClusterVNode.cs index aa60c45689..4ed416f955 100644 --- a/src/EventStore.Core/ClusterVNode.cs +++ b/src/EventStore.Core/ClusterVNode.cs @@ -906,9 +906,10 @@ GossipAdvertiseInfo GetGossipAdvertiseInfo() options.Interface.NodePortAdvertiseAs > 0 ? options.Interface.NodePortAdvertiseAs : NodeInfo.HttpEndPoint.GetPort()); + var replicationPortAdvertiseAs = options.Interface.GetReplicationPortAdvertiseAs(); var advertisedReplicationEndPoint = new DnsEndPoint(replicationHostToAdvertise, - options.Interface.ReplicationTcpPortAdvertiseAs > 0 - ? options.Interface.ReplicationTcpPortAdvertiseAs + replicationPortAdvertiseAs > 0 + ? replicationPortAdvertiseAs : NodeInfo.ReplicationEndPoint.GetPort()); return new GossipAdvertiseInfo(httpEndPoint, options.Interface.AdvertiseHostToClientAs, diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs index 65d89fa37e..e9341c51d3 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptions.cs @@ -648,8 +648,19 @@ public record InterfaceOptions public int NodePortAdvertiseAs { get; init; } = 0; [Description("Advertise the gRPC replication port as.")] + public int ReplicationPortAdvertiseAs { get; init; } = 0; + + [Description("Advertise the gRPC replication port as.")] + [Deprecated( + "The ReplicationTcpPortAdvertiseAs setting has been deprecated because replication uses gRPC. " + + "Use ReplicationPortAdvertiseAs instead.")] public int ReplicationTcpPortAdvertiseAs { get; init; } = 0; + public int GetReplicationPortAdvertiseAs() => + ReplicationPortAdvertiseAs > 0 + ? ReplicationPortAdvertiseAs + : ReplicationTcpPortAdvertiseAs; + [Description("Keepalive ping timeout for gRPC replication connections. Values below 1000 ms use the HTTP/2 minimum of 1000 ms."), Unit("ms")] public int ReplicationHeartbeatTimeout { get; init; } = 700; diff --git a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs index 5665c3bd26..9a4ef773da 100644 --- a/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs +++ b/src/EventStore.Core/Configuration/ClusterVNodeOptionsExtensions.cs @@ -128,7 +128,7 @@ options with Interface = options.Interface with { ReplicationHostAdvertiseAs = endPoint.GetHost(), - ReplicationTcpPortAdvertiseAs = endPoint.GetPort() + ReplicationPortAdvertiseAs = endPoint.GetPort() } }; From 17b74e2dbb38af1d6a876db4065bf3fb0770f091 Mon Sep 17 00:00:00 2001 From: Yordis Prieto Date: Sat, 12 Sep 2026 18:01:00 -0400 Subject: [PATCH 4/4] fix(ci): preserve reliable test verification Signed-off-by: Yordis Prieto --- scripts/test.sh | 2 +- src/Directory.Packages.props | 4 ++-- .../EventStore.TestClient.Tests.csproj | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index a7ed1e5b29..c8cbe1a780 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -219,7 +219,7 @@ project_timeout() { printf '%s\n' "15m" ;; core-services:EventStore.Core.Tests) - printf '%s\n' "15m" + printf '%s\n' "20m" ;; core-cluster-services:EventStore.Core.Tests) printf '%s\n' "25m" diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 252802db38..9eb964ba07 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -10,8 +10,8 @@ - - + + diff --git a/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj b/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj index 51224f527e..2cb72fad1a 100644 --- a/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj +++ b/src/EventStore.TestClient.Tests/EventStore.TestClient.Tests.csproj @@ -3,6 +3,7 @@ true +